如何迭代多个 Word 实例(使用 AccessibleObjectFromWindow)

Mar*_*niz 5 iteration vba pid ms-word instance

我需要遍历所有 Word 实例,无论是由用户、自动化、zumbis 等打开的。

我将描述到目前为止的所有步骤:我看到并实施了我在这里得到的解决方案;

       Do
            For Each objWordDocument In objWordApplication.Documents
               OpenDocs(iContadorDocs - 1) = objWordDocument.Name
               OpenDocs(iContadorDocs) = objWordDocument.path
               iContadorDocs = iContadorDocs + 2
               ReDim Preserve OpenDocs(iContadorDocs)
            Next objWordDocument
            iWordInstances = iWordInstances + 1
            objWordApplication.Quit False
            Set objWordApplication = Nothing
            Set objWordApplication = GetObject(, "Word.Application")
       Loop While Not objWordApplication Is Nothing
Run Code Online (Sandbox Code Playgroud)

它有效,但是:

  1. 为了迭代所有单词实例,我们必须 GetObject 并关闭它,循环直到没有更多打开的实例,然后重新打开我关心的所有内容

    • 这需要很多时间和 R/W 周期和磁盘访问

    • 并且当然必须在 Word 之外完成,因为它可能会先关闭运行实例的代码,或者在循环中间...

所以,经过一些谷歌搜索,我看到了一些直接访问流程的例子,这里这里是 VB。

我设法获得了所有 Winword.exe 实例的 PID,主要是对VBForums的代码进行了一些修改

仅显示修改后的代码段:

   Do
        If LCase(VBA.Left$(uProcess.szExeFile, InStr(1, uProcess.szExeFile, Chr(0)) - 1)) = LCase(ProcessName) Then
            ProcessId = uProcess.th32ProcessID
            Debug.Print "Process name: " & ProcessName & "; Process ID: " & ProcessId
        End If
   Loop While ProcessNext(hSnapShot, uProcess)
Run Code Online (Sandbox Code Playgroud)

对于上面的代码运行,我们需要包含进程名称 (szExeFile) 和进程 ID 字段 (th32ProcessID) 的 PROCESSENTRY32 结构;这段代码是@ VBnet/Randy Birch

所以,现在我有了“实例 PID”这个词;接下来是什么?

这样做之后,我尝试查看如何将这些 PID 实例传递给 GetObject 函数。

这时我遇到了这个 Python线程,它让我看到了AccessibleObjectFromWindow,它从 Windows 句柄创建了一个对象。

我挖了很多地方,最有用的是这些hereherehere,可以得到这段代码:

Private Declare Function FindWindowEx Lib "User32" Alias "FindWindowExA" _
        (ByVal hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As String, _
         ByVal lpsz2 As String) As Long
Private Declare Function IIDFromString Lib "ole32" _
        (ByVal lpsz As Long, ByRef lpiid As GUID) As Long
Private Declare Function AccessibleObjectFromWindow Lib "oleacc" _
        (ByVal hWnd As Long, ByVal dwId As Long, ByRef riid As GUID, _
         ByRef ppvObject As Object) As Long

Private Type GUID
    Data1 As Long
    Data2 As Integer
    Data3 As Integer
    Data4(7) As Byte
End Type
Private Const S_OK As Long = &H0
Private Const IID_IDispatch As String = "{00020400-0000-0000-C000-000000000046}"
Private Const OBJID_NATIVEOM As Long = &HFFFFFFF0

Sub testWord()
Dim i As Long
Dim hWinWord As Long
Dim wordApp As Object
Dim doc As Object
    'Below line is finding all my Word instances
    hWinWord = FindWindowEx(0&, 0&, "OpusApp", vbNullString)
    While hWinWord > 0
        i = i + 1
        '########Successful output
        Debug.Print "Instance_" & i; hWinWord
        '########Instance_1 2034768 
        '########Instance_2 3086118 
        '########Instance_3 595594 
        '########Instance_4 465560 
        '########Below is the problem
        If GetWordapp(hWinWord, wordApp) Then
            For Each doc In wordApp.documents
                Debug.Print , doc.Name
            Next
        End If
        hWinWord = FindWindowEx(0, hWinWord, "OpusApp", vbNullString)
    Wend
End Sub

Function GetWordapp(hWinWord As Long, wordApp As Object) As Boolean
Dim hWinDesk As Long, hWin7 As Long
Dim obj As Object
Dim iid As GUID

    Call IIDFromString(StrPtr(IID_IDispatch), iid)
    hWinDesk = FindWindowEx(hWinWord, 0&, "_WwF", vbNullString)
   '########Return 0 for majority of classes; only for _WwF it returns other than 0
    hWin7 = FindWindowEx(hWinDesk, 0&, "_WwB", vbNullString)
   '########Return 0 for majority of classes; only for _WwB it returns other than 0
    If AccessibleObjectFromWindow(hWin7, OBJID_NATIVEOM, iid, obj) = S_OK Then
   '########Return -2147467259 and does not get object...
        Set wordApp = obj.Application
        GetWordapp = True
    End If
End Function
Run Code Online (Sandbox Code Playgroud)

错误在上面的代码中被注释(########);但恢复后,我识别了所有实例,但无法检索该对象。对于 Excel,这些行:

hWinDesk = FindWindowEx(hWinXL, 0&, "XLDESK", vbNullString)
hWin7 = FindWindowEx(hWinDesk, 0&, "EXCEL7", vbNullString)
Run Code Online (Sandbox Code Playgroud)

有效,因为我得到了 hWinDesk = 1511272 和 332558 而不是零,并且在我得到 Excel 对象之后。

EXCEL7对应的Word Windows类是_WwG(但上面给出了0),XLMAIN对应的Word类名是OpusApp。Word对应的XLDESK是什么?

所以,我需要帮助来发现它;或者你知道如何在知道它是PID的VBA中捕获COM对象吗?MS 本身建议我查看Office 200 文档;我会这样做,但如果有人以前这样做过......

事实上,现在我对这两种方法都感兴趣,但当然最后一种已实现 99%,所以,我更喜欢。

TIA

PS当然,当实现时,所有对象都将关闭/无,错误处理等......

编辑 1: 这是 Spy++ 输出,根据@Comintern 的建议: 间谍++输出

有趣的是,我在Excel输出中只能定位到两个字符串:XLMAIN和XLDESK,但是完全找不到EXCEL7,并且成功捕获了Excel对象。对于 Word,我测试了所有字符串 (_WwC,_WwO,),但仅

?FindWindowEx(hWinWord, 0&, "_WwF", vbNullString)
 1185896 
?FindWindowEx(hWinDesk, 0&, "_WwB", vbNullString)
 5707422 
Run Code Online (Sandbox Code Playgroud)

有一个把手,按这个顺序;但无济于事,因为

 ?AccessibleObjectFromWindow(hWin7, OBJID_NATIVEOM, iid, obj)
-2147467259 
Run Code Online (Sandbox Code Playgroud)

有任何想法吗?方向?

Mar*_*niz 2

按照 @Comintern 的建议,与 Spy++ 更加亲密后,我追踪到了这一点:

\n

在此输入图像描述

\n

这是实际的窗口顺序;OpusApp 下面的所有窗口都是它的子窗口

\n

但要了解它现在起作用的原因,我们必须右键单击下面的每个 _Ww[A_Z]:

\n
Key to translate below images:\nPr\xc3\xb3xima Janela => Next Window\nJanela Anterior => Previous Window\nJanela Pai => Parent Window\nPrimeira Janela Filha => First Child Window\nJanela Propriet\xc3\xa1ria => Previous Window\n
Run Code Online (Sandbox Code Playgroud)\n

对于_WwF:

\n

在此输入图像描述

\n

对于它的孩子_WwB:

\n

在此输入图像描述

\n

终于到达目标了!!!!_WWG:

\n

在此输入图像描述

\n

使用这种方法,显然我们必须在代码中添加另一层:

\n
  Function GetWordapp(hWinWord As Long, wordApp As Object) As Boolean\n        Dim hWinDesk As Long, hWin7 As Long, hFinalWindow As Long\n        Dim obj As Object\n        Dim iid As GUID\n        \n        Call IIDFromString(StrPtr(IID_IDispatch), iid)\n        hWinDesk = FindWindowEx(hWinWord, 0&, "_WwF", vbNullString)\n        hWin7 = FindWindowEx(hWinDesk, 0&, "_WwB", vbNullString)\n        hFinalWindow = FindWindowEx(hWin7, 0&, "_WwG", vbNullString)\n        If AccessibleObjectFromWindow(hFinalWindow, OBJID_NATIVEOM, iid, obj) = S_OK Then\n            Set wordApp = obj.Application\n            GetWordapp = True\n        End If\n    End Function\n
Run Code Online (Sandbox Code Playgroud)\n

我不明白,但现在不介意,为什么两个不同的实例会重复结果:\nDebug.print 结果:

\n
   Instance_1 1972934 \n                  x - fatores reumaticos.docx\n                  FormGerenciadorCentralPacientes.docm\n    Instance_2 11010524 \n                  x - fatores reumaticos.docx\n                  FormGerenciadorCentralPacientes.docm\n    Instance_3 4857668 \n
Run Code Online (Sandbox Code Playgroud)\n

但为了解决这个问题,我将采用@PGS62 的Marvel 解决方案;恢复:

\n
Private Function GetWordInstances() As Collection\n    Dim AlreadyThere As Boolean\n    Dim wd As Application\n    Set GetWordInstances = New Collection\n    ...code...\n    For Each wd In GetWordInstances \n                If wd Is WordApp.Application Then\n                    AlreadyThere = True\n                    Exit For\n                End If\n            Next\n            If Not AlreadyThere Then\n                GetWordInstances.Add WordApp.Application\n            End If\n      ...code...\nEnd Function\n
Run Code Online (Sandbox Code Playgroud)\n

而且,voil\xc3\xa1,大众所有 Word 实例的迭代,无需关闭并重新打开!

\n

感谢社区,感谢其他线程中的所有想法,感谢@Comintern 的重要建议。

\n