cancel
Showing results for 
Search instead for 
Did you mean: 

Activating the SAP window

Former Member
0 Kudos

Hi All,

I have a script which I run from Excel and it works fine... it simply opens a work order, the only problem is that the SAP window is not activated?

The line Session.findByID("wnd[0]").maximize  doesn't display the SAP window?

I can Alt Tab to the window and it showing what I want but how do I do this as part of the macro.

What am I missing here?

Regards

Steve Bayliss

Accepted Solutions (1)

Accepted Solutions (1)

holger_khn
Contributor
0 Kudos

Hello.

Setup two functions. ActivateWindow and DeActivateWindow:


Public Declare Function FindWindow Lib "user32" Alias _
    "FindWindowA" (ByVal lpClassName As String, _
                   ByVal lpWindowName As String) As Long
                  
Public Declare Function GetWindowPlacement Lib "user32" _
    (ByVal hwnd As Long, lpwndpl As WINDOWPLACEMENT) As Long
   
Public Declare Function SetWindowPlacement Lib "user32" _
    (ByVal hwnd As Long, lpwndpl As WINDOWPLACEMENT) As Long
   
Public Declare Function SetForegroundWindow Lib "user32" _
    (ByVal hwnd As Long) As Long
   
Public Declare Function GetForegroundWindow Lib "user32" () As Long

Public Declare Function BringWindowToTop Lib "user32" _
    (ByVal hwnd As Long) As Long   

Const SW_SHOWNORMAL = 1
Const SW_SHOWMINIMIZED = 2

Public Type POINTAPI
    X As Long
    Y As Long
End Type

Public Type RECT
    Left As Long
    Top As Long
    Right As Long
    Bottom As Long
End Type

Public Type WINDOWPLACEMENT
    Length As Long
    flags As Long
    showCmd As Long
    ptMinPosition As POINTAPI
    ptMaxPosition As POINTAPI
    rcNormalPosition As RECT
End Type

Public Function ActivateWindow(xhWnd&) As Boolean

    Dim Result&, WndPlcmt As WINDOWPLACEMENT
 
    With WndPlcmt
   
        .Length = Len(WndPlcmt)
        Result = GetWindowPlacement(xhWnd, WndPlcmt)
       
        If Result Then
       
            If .showCmd = SW_SHOWMINIMIZED Then
                .flags = 0
                .showCmd = SW_SHOWNORMAL
                Result = SetWindowPlacement(xhWnd, WndPlcmt)
              Else
                Call SetForegroundWindow(xhWnd)
                Result = BringWindowToTop(xhWnd)
            End If
           
            If Result Then ActivateWindow = True
           
        End If
       
    End With
   
  End Function

Public Function DeActivateWindow(xhWnd&) As Boolean

    Dim Result&, WndPlcmt As WINDOWPLACEMENT
 
    With WndPlcmt
   
        .Length = Len(WndPlcmt)
        Result = GetWindowPlacement(xhWnd, WndPlcmt)
       
        If Result Then
                .flags = 0
                .showCmd = SW_SHOWMINIMIZED
                Result = SetWindowPlacement(xhWnd, WndPlcmt)
                If Result Then DeActivateWindow = True
        End If
       
    End With
   
End Function

Then you can set Windows in fore- and/or background.


SessionHWND = Session.FindById("wnd[0]").Handle
ActivateWindow (SessionHWND)

'Start of your code
      'Your code
'End of your code

DeActivateWindow (SessionHWND)
ActivateWindow (Application.hwnd) 'ExcelWBInFront

Best regards

Holger

Former Member
0 Kudos

Holger,

Thank you so much for your response, very much appreciated.

I will give that a go as soon as I can.

Regards

Steve.

stefan_schnell
Active Contributor
0 Kudos

Hello Holger,

cool code, thanks for that.

Cheers

Stefan

Answers (1)

Answers (1)

0 Kudos

I wrote a little bit of code to pull some material master text data from SAP that I could not retrieve in any other way but via a specific transaction.


Dim session

Function NOTES()

Dim DataObj As MSForms.DataObject

Set DataObj = New MSForms.DataObject

Dim Application

Dim material, plant As String

Dim xMATERIAL As String

Dim sbarmessage As String

If Not IsObject(Application) Then

   Set SapGuiAuto = GetObject("SAPGUI")

   Set Application = SapGuiAuto.GetScriptingEngine

End If

If Not IsObject(Connection) Then

   Set Connection = Application.Children(0)

End If

If Not IsObject(session) Then

   Set session = Connection.Children(0)

End If

If IsObject(WScript) Then

   WScript.ConnectObject session, "on"

   WScript.ConnectObject Application, "on"

End If

    InputRow = InputBox("Enter the Starting Row", "Starting Row", 3)

    Row = CInt(InputRow)

xMATERIAL = Worksheets("NOTES").Cells(Row, 1)

While xMATERIAL <> ""

    xMATERIAL = Worksheets("NOTES").Cells(Row, 1)

    xNOTES = Worksheets("NOTES").Cells(Row, 2)

            Call ClearClipboard

        session.findById("wnd[0]/tbar[0]/okcd").Text = "/nmd04"

        session.findById("wnd[0]").sendVKey 0

        session.findById("wnd[0]/usr/tabsTAB300/tabpF01/ssubINCLUDE300:SAPMM61R:0301/ctxtRM61R-MATNR").Text = xMATERIAL

        session.findById("wnd[0]/usr/tabsTAB300/tabpF01/ssubINCLUDE300:SAPMM61R:0301/ctxtRM61R-WERKS").Text = "VQ02"

            AppActivate "Stock/Requirements List: Initial Screen"

        session.findById("wnd[0]").sendVKey 0

        session.findById("wnd[0]/usr/subINCLUDE8XX:SAPMM61R:0800/btnRM61R-MNTXT").press

        SendKeys ("^(a)")

            Call Wait

        session.findById("wnd[0]/tbar[1]/btn[38]").press

        DataObj.GetFromClipboard

        On Error Resume Next

        myString = DataObj.GetText(1)

Whoa:

        If Err <> 0 Then

            Err.Clear

            GoTo ADVANCE

        End If

        Worksheets("NOTES").Cells(Row, 2).Select

        Worksheets("NOTES").Cells(Row, 2) = myString

ADVANCE:

        X = X + 1

        Row = Row + 1

        xMATERIAL = Worksheets("NOTES").Cells(Row, 1)

        Worksheets("NOTES").Cells(Row, 1).Select

Wend

End Function

Sub Wait()

    l = 1

    While l < 10000

        l = l + 1

        Wend

End Sub

I had a second module stating the following:


Private Declare Function OpenClipboard Lib "User32.dll" _

(ByVal hWndNewOwner As Long) As Long

Private Declare Function EmptyClipboard Lib "User32.dll" () As Long

Private Declare Function CloseClipboard Lib "User32.dll" () As Long

    Public Function ClearClipboard()

        Dim Ret

        Ret = OpenClipboard(0&)

        If Ret <> 0 Then Ret = EmptyClipboard

        CloseClipboard

    End Function

Anyway... after putting all of that down... What i want to say is that you can call the specific window you're looking for by the specific name.  In my script, i was running transaction MD04 to access material plant data.  That transaction labels as "Stock/Requirements List: Initial Screen".  It can be called via:

AppActivate "Stock/Requirements List: Initial Screen"


Likewise, if i had wanted to open a work order, I could have called via:


AppActivate "Change Order: Initial Screen"


So long as you had navigated to that transaction in SAP prior to enabling it, you would have had an active IW32 screen for order maintenance.

The rest of my code was for manipulation of the clipboard data and clearing in order to enter multiple lines or paragraphs of information into a single Excel cell.  Excel tends to spread data over multiple cells.  My script modifies the clipboard data to bypass that.

Anyway... that's how i called MD04 directly via AppActivate.

Former Member
0 Kudos

Thanks Ehren, very useful also... 

Love the label 'Whoa'  hehe

Cheers Steve

script_man
Active Contributor
0 Kudos

Hi Steve and the other,

although a solution already exists, I would like to contribute a small example of me.

for example:

'vbs version:

myString = "SAP Easy Access"

if Not IsObject(application) Then

   Set SapGuiAuto  = GetObject("SAPGUI")

   Set application = SapGuiAuto.GetScriptingEngine

End If

If Not IsObject(connection) Then

   Set connection = application.Children(0)

End If

If Not IsObject(session) Then

   Set session    = connection.Children(0)

End If

for mySession = 0 to connection.children.count - 1

Set session    = connection.Children(int(mySession))

Position = instr(1,session.findById("wnd[0]").text,myString)

if Position <> 0  then exit for

next

if Position <> 0 then

   session.findById("wnd[0]").iconify

  session.findById("wnd[0]").maximize

end if

'excel version:


myString = "SAP Easy Access"

Set SapGuiAuto = GetObject("SAPGUI")

Set SapApplication = SapGuiAuto.GetScriptingEngine

Set Connection = SapApplication.Children(0)

Set session = Connection.Children(0)

For mySession = 0 To Connection.Children.Count - 1

Set session = Connection.Children(Int(mySession))

Position = InStr(1, session.findById("wnd[0]").Text, myString)

If Position <> 0 Then Exit For

Next

If Position <> 0 Then

   session.findById("wnd[0]").iconify

   session.findById("wnd[0]").maximize

End If

The variable myString does not contain the complete name of the SAP window. It reaches only a small part of this. The uppercase and lowercase letters are considered.

In other words:

If after the command ...maximize the SAP window in the foreground is to come, must first be generated the command ... iconify.

Regards,

ScriptMan

0 Kudos

Script Man... what is the ".iconify" suffix you're using associated to the primary SAP session?  I've not used that bit before.

script_man
Active Contributor
0 Kudos

Ehren, Iconify is a method to collapse the main window of a sap session to the minimized state.

0 Kudos

Ok... i thought so.  So, minimizing and then maximizing is making the window active?

script_man
Active Contributor
0 Kudos

Anyway, it works for me as always. But I know of no definition that describes it this way.

0 Kudos

No prob.  I'm just always looking for a different way to do things.

Thanks, bud.

holger_khn
Contributor
0 Kudos

Yes, that is how it works.

There is only a small risk if you have an Excel file or SAP session with same/similar Name. Then it could be that you will not activate the Scripting-active session.

This issue was root cause why I switch using User32 API functions and take window handle. This defintely will maximize and activate window in scope.

script_man
Active Contributor
0 Kudos

If the script starts from Excel the object session knows, which sessionnumber it is. And this session has to be once again minimized and then maximized. Here is no need to search for the window name.