【问题标题】:Determine when a shelled process ends in VBA在 VBA 中确定外壳进程何时结束
【发布时间】:2021-11-13 04:56:00
【问题描述】:

我需要使用 Web 浏览器,登录网站,然后返回 VBA 代码并继续处理。我已经尝试过https://docs.microsoft.com/en-us/office/vba/access/concepts/windows-api/determine-when-a-shelled-process-ends 的代码,但这不适用于 MS Edge(或 Opera、Chrome 或 Firefox)。我尝试将可执行文件直接称为“C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe”,但这仍然不起作用。 我不得不将功能更改为 PtrSafe。

代码运行正常,但是当它到达行时

' Wait for the shelled application to finish: 
Do 
   ReturnValue = WaitForSingleObject(proc.hProcess, 0) 
   DoEvents 
Loop Until ReturnValue <> 258 

即使 Edge 仍处于打开状态,返回值为 0,因此它不会等待。

我在 Window 10 64 位上运行 MS-Access 2016

代码:

Private Type STARTUPINFO
    cb As Long
    lpReserved As String
    lpDesktop As String
    lpTitle As String
    dwX As Long
    dwY As Long
    dwXSize As Long
    dwYSize As Long
    dwXCountChars As Long
    dwYCountChars As Long
    dwFillAttribute As Long
    dwFlags As Long
    wShowWindow As Integer
    cbReserved2 As Integer
    lpReserved2 As Long
    hStdInput As Long
    hStdOutput As Long
    hStdError As Long
End Type

Private Type PROCESS_INFORMATION
    hProcess As Long
    hThread As Long
    dwProcessID As Long
    dwThreadID As Long
End Type

Private Declare PtrSafe Function WaitForSingleObject Lib "kernel32" (ByVal _
    hHandle As LongPtr, ByVal dwMilliseconds As Long, ByVal bAlertable As Long) As Long

Private Declare PtrSafe Function CreateProcessA Lib "kernel32" (ByVal _
    lpApplicationName As LongPtr, ByVal lpCommandLine As String, ByVal _
    lpProcessAttributes As LongPtr, ByVal lpThreadAttributes As LongPtr, _
    ByVal bInheritHandles As LongPtr, ByVal dwCreationFlags As LongPtr, _
    ByVal lpEnvironment As LongPtr, ByVal lpCurrentDirectory As LongPtr, _
    lpStartupInfo As STARTUPINFO, lpProcessInformation As _
    PROCESS_INFORMATION) As Long

Private Declare PtrSafe Function CloseHandle Lib "kernel32" (ByVal _
    hObject As LongPtr) As Long

Private Const NORMAL_PRIORITY_CLASS = &H20&
Private Const INFINITE = -1&


Public Sub ExecCmd(cmdline As String)
Dim proc As PROCESS_INFORMATION
Dim start As STARTUPINFO
Dim ReturnValue As Integer


' Initialize the STARTUPINFO structure:
start.cb = Len(start)

' Start the shelled application:
ReturnValue = CreateProcessA(0&, cmdline$, 0&, 0&, 1&, NORMAL_PRIORITY_CLASS, 0&, 0&, start, proc)

' Wait for the shelled application to finish:
Do
ReturnValue = WaitForSingleObject(proc.hProcess, 1, 0)
DoEvents
Loop Until ReturnValue <> 258

ReturnValue = CloseHandle(proc.hProcess)
End Sub

【问题讨论】:

  • 为什么不使用 Selenium 并在 VBA 代码中登录而不是脱壳? stackoverflow.com/a/57224810/12502175
  • 您是否尝试过记事本来验证您的代码是否有效,并且您没有因错误添加PtrSafe 而破坏内容(您需要将代码转换为实际使用正确大小的指针)。
  • 我用记事本试过了,没问题
  • 感谢 peterb,如果我使用 selenium,我仍然会遇到 Web 登录必须在其余代码继续之前完成的问题,这就是我在炮轰或有办法实现的原因这与硒。
  • 您是如何将 API 函数适配为 64 位的?只在Declare 和函数之间插入PtrSafe?您是否还改编了“STARTUPINFO”和PROCESS_INFORMATION 类型?您可以编辑您的问题并分享您使用的代码吗?

标签: vba ms-access-2016


【解决方案1】:

请更改两个使用的Type 声明如下,CreateProcess 应该返回一个LongPtr

     Private Type STARTUPINFO       
        cb              As Long    
        padding1        As Long
        lpReserved      As String  
        lpDesktop       As String  
        lpTitle         As String  
        dwX             As Long    
        dwY             As Long    
        dwXSize         As Long    
        dwYSize         As Long    
        dwXCountChars   As Long    
        dwYCountChars   As Long    
        dwFillAttribute As Long    
        dwFlags         As Long    
        wShowWindow     As Integer
        cbReserved2     As Integer
        padding2        As Long
        lpReserved2     As LongPtr '!!!
        hStdInput       As LongPtr '!!!
        hStdOutput      As LongPtr '!!!
        hStdError       As LongPtr '!!!
    End Type                  

    Private Type PROCESS_INFORMATION
        hProcess    As LongPtr '!!!
        hThread     As LongPtr '!!!
        dwProcessID As Long
        dwThreadID  As Long
    End Type

    Private Declare PtrSafe Function CreateProcessA Lib "kernel32" (ByVal _
             lpApplicationName As LongPtr, ByVal lpCommandLine As String, ByVal _
             lpProcessAttributes As LongPtr, ByVal lpThreadAttributes As LongPtr, _
             ByVal bInheritHandles As LongPtr, ByVal dwCreationFlags As LongPtr, _
             ByVal lpEnvironment As LongPtr, ByVal lpCurrentDirectory As LongPtr, _
             lpStartupInfo As STARTUPINFO, lpProcessInformation As _
                                            PROCESS_INFORMATION) As LongPtr '!!!

那么,(在使用的Sub中)Dim ReturnValue As Integer应该在Dim ReturnValue As LongPtr中更改。

请进行测试并发送一些反馈。

我测试了 Edge 应用程序,它正在等待进程终止。

【讨论】:

  • 我已经进行了更改,但它没有停留在“等待外壳应用程序完成”循环中 CreateProcessA 调用的 ReturnValue 为 1^,但 WaitForSingleObject 调用的返回值为 0^所以执行退出循环 ' 启动带壳的应用程序: ReturnValue = CreateProcessA(0&, cmdline$, 0&, 0&, 1&, NORMAL_PRIORITY_CLASS, 0&, 0&, start, proc) ' 等待带壳的应用程序完成:Do ReturnValue = WaitForSingleObject(proc.hProcess, 1, 0) DoEvents 循环直到 ReturnValue 258
  • @user63103 您的意思是在关闭 Edge 进程之前显示“进程已完成”消息?如果是,在我的情况下不会发生这种情况。该消息仅在我关闭进程/Edge 窗口后出现。如果没有,你想完成什么?现在,请忘记变量返回的内容,并专注于回答我的问题。我问他们是为了检查我的代码有什么不同,以防我的第一个问题的答案是肯定的......
  • 是的,我在按钮上有此代码 Private Sub Command13_Click() ExecCmd "MicrosoftEdge www.google.co.uk" MsgBox "Process Finished" End Sub 并且在 Edge 仍处于打开状态时出现消息,但隐藏在边缘后面。我想要实现的是在边缘打开时等待的代码,只有在边缘关闭时才继续。
猜你喜欢
  • 2017-02-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-25
  • 2013-03-29
  • 2017-12-14
相关资源
最近更新 更多