使用 VBA 停止 SAP 中的事务

Stop transaction in SAP with VBA

我有一个有效的 VBA 宏,它进入 SAP,启动事务,然后在电子表格中提取数据。

但有时计算运行时间太长,或者我只想停止它进行干预。在左上角的工具栏上有一个功能,用户可以手动“停止交易”。

是否有“停止交易”按钮的任何 SAP 脚本代码,这样我就可以避免手动步骤?

SAP 工具栏:

假定VBA宏在第一个会话中是运行。如果在启动宏之前打开了第二个会话,它可用于关闭第一个会话。

例如:

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"))

是否执行了"rollback",待测试。

此致, 脚本人

我想你最好用这个场景录制一个脚本,然后你可以随时重新使用它。 否则,我现在正在为同样的情况而苦苦挣扎,但是如果 运行 时间太长, 运行 时间计数器部分将离开 tcode。 这也是一个棘手的问题,但是另一个话题。

更新:意识到没有办法记录'Stop Transaction'步,我应用了上面的方法-谢谢脚本人,这不是你第一次拯救这一天。 对于阅读此线程的任何人 - 了解如何从 VBA 脚本 运行time 中拆分 SAP 运行time 可能很有用。 我引入了一个对象,它是 'Execute' 命令本身。这样,SAP 接受命令并开始执行,而宏将跳过,因为它不是实际命令,而只是应用新对象。这个技巧可以帮助用户编写一个时间计数器并在 运行 太长时删除会话。 作为参考,请在此处查看我的代码 - 我引用了我的代码中包含相关方法的部分。

'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.

或者,查看我刚刚编写和测试的代码以使用 Windows API 至 运行 停止事务菜单项。我在 SAP 论坛上提出了一个问题,但同时我自己弄清楚了 (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

TestRunMenuItemByString 函数只能在会话启动后使用,并且只有在实际执行事务时才会起作用。您将需要弄清楚如何引用您的 sap 会话对象 (oSAPSession),以便使用它的句柄值。

声明应该在 VBA 的 32 位和 64 位版本中都有效,并且 LongPtr 已用于句柄 (h) 和指针 (lp) 变量以反映这一点。

这已在 Microsoft Access 中测试过,但我看不出为什么它不能在其他 Office 应用程序的 VBA 中运行。我不能保证它适用于 VBScript。