3

I have a working VBA macro which enters SAP, starts a transaction and then extracts the data in spreadsheet.

But sometimes the calculation runs too long, or I just would like to stop it to intervene. There is a functionality on the toolbar at the top left corner, where the user can "stop transaction" manually.

Is there any SAP script code for the "stop transaction" button, so I can avoid the manual step?

SAP toolbar:

SAP toolbar
Sandra Rossi
  • 11,934
  • 5
  • 22
  • 48
ViktorovicsL
  • 75
  • 2
  • 8
  • Be aware that VBA and VBScript are 2 totally different languages. Please clarify which one of them you actually mean and adjust your question and tags. – Pᴇʜ Jul 04 '18 at 11:46
  • Good question, but I guess you probably just can't – Storax Jul 04 '18 at 11:50
  • I still hope, that there is a solution for that. I have searched everywhere in google, but no luck. – ViktorovicsL Jul 04 '18 at 14:46

3 Answers3

1

It is assumed that the VBA macro is running in the first session. If a second session is opened before starting the macro, it can be used to close the first session.

for example:

Set SapGuiAuto  = GetObject("SAPGUI")
Set SAPapp = SapGuiAuto.GetScriptingEngine
Set SAPconnection = SAPapp.Children(0)
Set session    = SAPconnection.Children(1)

session.findById("wnd[0]/tbar[0]/okcd").text = "/i1"
session.findById("wnd[0]").sendVKey 0
session.createSession

Application.Wait (Now + TimeValue("0:00:05"))

session.findById("wnd[0]/tbar[0]/okcd").text = "/i3"
session.findById("wnd[0]").sendVKey 0
session.createSession

Application.Wait (Now + TimeValue("0:00:05"))

Whether a "rollback" is carried out or not, would be to test.

Regards, ScriptMan

ScriptMan
  • 1,580
  • 1
  • 9
  • 9
1

I guess you better record a script with this scenario, then you can re-use it any time. Otherwise, I am at the very moment struggling with the same case, but with the run time counter part to leave the tcode if running too long. It is a hart nut to crack too, but a different topic.

Update: realizing that there is no way to get the 'Stop Transaction' step recorded, I applied the above method - thank you Script Man, it was not the first time you saved the day. For anyone reading this thread - may be useful to know how to split the SAP runtime from VBA script runtime. I introduced an object that is the 'Execute' command itself. This way, SAP takes the command and starts execution, while the macro will step over as it is not an actual command but applying a new object only. This trick can help users to write a time counter and drop the session if running too long. For reference, see my code here - I quoted the part of my code that contains the relevant method.

'check whether you already have an extra session open to close the long running session
'open one if needed
On Error Resume Next
Set session1 = Connection.Children(1)
If Err.Number <> 0 Then
   session.CreateSession
    Application.Wait (Now + TimeValue("0:00:05"))
    're-set the sessions, ensuring you use the first session for actual work and keep session1 in background
    Set session = Connection.Children(0)
    Set session1 = Connection.Children(1)
    SesCount = Connection.Sessions.Count()
    Err.Clear
On Error GoTo 0
End If

'get the ID of first session, so you can enter the correct terminating transaction code when needed
sessionID = Mid(session.ID, (InStrRev(session.ID, "[") + 1), 1)
Terminator = "/i" & sessionID + 1
session.FindById("wnd[0]").Maximize

'some code comes here

'here I use an object to apply the execute button - this way parallel with the SAP runtime, the VBA script can proceed.
perec = session.FindById("wnd[0]/tbar[1]/btn[8]").press

'here we set a loop to check whether system is busy over a certain time then we may interrupt:
Do
Application.Wait (Now + TimeValue("0:00:05"))
SecondsElapsed = SecondsElapsed + 5
fityirc = session.Busy()
if fityirc = False then
exit Do
end if
Loop Until SecondsElapsed >= 100


If fityirc = True Then
session1.FindById("wnd[0]/tbar[0]/okcd").Text = Terminator
session1.FindById("wnd[0]").sendVKey 0
End If

'...and so on. This solution is applied in a loop to extract datasets massively without human interaction.
lazov
  • 11
  • 3
0

Or, have a look at code I've just written and tested to use the Windows API to run the Stop Transaction menu item. I raised a question about it on the SAP forum, but figured it out myself in the meantime (SAP Forum)

Private Declare PtrSafe Function FindWindowA Lib "user32.dll" (ByVal lpClassName As String, ByVal lpWindowName As String) As LongPtr
Private Declare PtrSafe Function GetSystemMenu Lib "user32" (ByVal hWnd As LongPtr, ByVal bRevert As Long) As LongPtr
Private Declare PtrSafe Function GetMenuItemCount Lib "user32" (ByVal hMenu As LongPtr) As Long
Private Declare PtrSafe Function GetMenuItemInfoA Lib "user32" (ByVal hMenu As LongPtr, ByVal un As Long, ByVal b As Long, lpMenuItemInfo As MENUITEMINFO) As Long
Private Declare PtrSafe Function SendMessageA Lib "user32" (ByVal hWnd As LongPtr, ByVal wMsg As Long, ByVal wParam As LongPtr, lParam As Any) As LongPtr

Public Const MIIM_STRING As Integer = &H40
Public Const MIIM_ID = &H2
Public Const WM_COMMAND = &H111
Public Const WM_SYSCOMMAND = &H112

Public Type MENUITEMINFO
    cbSize As Long
    fMask As Long
    fType As Long
    fState As Long
    wID As LongPtr
    hSubMenu As Long
    hbmpChecked As Long
    hbmpUnchecked As Long
    dwItemData As Long
    dwTypeData As String
    cch As Long
End Type

Public Function RunMenuItemByString(ByVal sMenuItem As String, _
                                    ByVal sWindowClass As String, _
                                    ByVal sWindowText As String, _
                                    ByVal iCommandType As Integer) As Boolean

    Dim hWnd As LongPtr, hMenu As LongPtr, lpMenuItemID As LongPtr

    Dim lngMenuItemCount As Long, lngMenuItem As Long, lngResultMenuItemInfo As Long

    Dim typMI As MENUITEMINFO

    Dim s As String

    Dim blnRet As Boolean

    hWnd = FindWindowA(sWindowClass, sWindowText)

    hMenu = GetSystemMenu(hWnd, 0&)

    lngMenuItemCount = GetMenuItemCount(hMenu)

    For lngMenuItem = 0 To lngMenuItemCount - 1

        typMI.cbSize = Len(typMI)
        typMI.dwTypeData = String$(255, " ")
        typMI.cch = Len(typMI.dwTypeData)
        typMI.fMask = MIIM_STRING Or MIIM_ID
        lngResultMenuItemInfo = GetMenuItemInfoA(hMenu, lngMenuItem, 1, typMI)
        s = Trim$(typMI.dwTypeData)
        lpMenuItemID = typMI.wID
        If InStr(1, s, sMenuItem, vbTextCompare) > 0 Then
            blnRet = SendMessageA(hWnd, iCommandType, lpMenuItemID, 0&) = 0
            Exit For
        End If

    Next lngMenuItem

    RunMenuItemByString = blnRet

End Function

Public Function TestRunMenuItemByString()

    lpHwndSAPSession = oSAPSession.FindById("wnd[0]").Handle

    sWindowText = GetWindowText(lpHwndSAPSession)

    TestRunMenuItemByString = RunMenuItemByString("Stop Transaction", "SAP_FRONTEND_SESSION", sWindowText, WM_SYSCOMMAND)

End Function

The TestRunMenuItemByString function can be used only after a session is started, and will only work if there is actually a transaction executing. You will need to figure out how to reference your sap session object (oSAPSession) in order to use the Handle value from it.

The declarations should work in both 32 bit and 64 bit versions of VBA and the LongPtr has been used for the handle (h) and pointer (lp) variables to reflect this.

This was tested in Microsoft Access, but I see no reason why it shouldn't work in VBA in other Office applications. I can't vouch for it being adaptable for VBScript.

Ian J.
  • 37
  • 1
  • 10