如何遍历多个Word实例(使用AccessibleObjectFromWindow) [英] How to iterate over multiple Word instances (with AccessibleObjectFromWindow)

查看:236
本文介绍了如何遍历多个Word实例(使用AccessibleObjectFromWindow)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要遍历所有Word实例,无论是由用户,通过自​​动化,Zumbis等打开的.

I need to iterate over all Word instances, no matter if opened by users, by automation, zumbis, etc.

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

I will describe all the steps until now: I saw and implemented the solutions that I got here;

       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

它可以工作,但是:

  1. 要遍历所有单词实例,我们必须先获取GetObject并将其关闭,循环直到不再有打开的实例为止,然后重新打开所有我关心的

  1. for iterate over all word instances we have to GetObject and close it, looping until no more opened instances are, and after, reopen all that I care

  • 这需要很多时间&读/写周期磁盘访问

  • this take a lot of time & R/W cycles & Disk Access

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

and of course has to be accomplished outside Word, because it may close the code running instance first, or in the middle of the loop...

因此,经过一番谷歌搜索后,我看到了一些直接访问该流程的示例,在此

So, after some googling, I saw some examples of access the process direct, here and here for VB.

我设法获取了所有Winword.exe实例的PID,主要是对

I managed to get the PID for all Winword.exe instances, mainly adapting a little the code at 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)

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

For the above code run, we need the PROCESSENTRY32 structure that include both process name (szExeFile) and Process Id fields (th32ProcessID); this code is @ VBnet/Randy Birch.

所以,现在我有instance instance PIDs这个词;接下来是什么?

So, now I have the word instances PIDs; what next?

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

After doing that, I tried to see how could I pass these PID instances to the GetObject function.

这一次,我碰到了这个Python 线程,打开 AccessibleObjectFromWindow 从Windows句柄创建对象.

At this time I bumped into this Python thread, that opened my eyes to the AccessibleObjectFromWindow that creates an object from a windows handle.

我在很多地方挖过,最有用的是这些此处并可以得到这段代码:

I dug in a lot of places, the most useful being these here, here and here and could get this piece of code:

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

上面的代码中将错误注释为(#########);但继续,我确定了所有实例,但无法检索该对象. 对于Excel,这些行:

The errors are commented (########) above into the code; but resuming, I identify all instances, but cannot retrieve the object. For Excel, the lines:

hWinDesk = FindWindowEx(hWinXL, 0&, "XLDESK", vbNullString)
hWin7 = FindWindowEx(hWinDesk, 0&, "EXCEL7", vbNullString)

有效,因为我得到的不是hWinDesk = 1511272和332558,而是得到Excel对象之后的数字.

works, because instead of zero I got hWinDesk = 1511272 and 332558, and after I got the Excel object.

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

The EXCEL7 corresponding Word Windows class is _WwG (but it gives 0 above), the XLMAIN corresponding Word class name is OpusApp. What is the XLDESK corresponding for Word?

因此,我需要帮助才能发现它;还是知道PID是在VBA中捕获COM对象的方法? MS本身建议我调查

So, I need help to discover it; or do you know how to capture the COM object in VBA knowing it's PID? MS itself suggested that I look into the Office 200 docs; I'll do that, but if someone has did this before...

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

In fact now I'm interested in both approaches, but of course this last one is 99% implemented, so, my preferred.

TIA

P.S.当然,实施后,所有对象都将关闭/关闭,进行错误处理等.

P.S. Of course, when implemented, all objects will be closed/nothing, error-handling, etc...

这是Spy ++的输出,按照@Comintern的建议:

EDIT 1: Here is Spy++ output, as per @Comintern advise:

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

Interesting is that I can locate in Excel output only two of the strings: XLMAIN and XLDESK, but cannot find at all the EXCEL7, and Excel object is successfully captured. For Word, I tested all the strings (_WwC,_WwO,), but only

?FindWindowEx(hWinWord, 0&, "_WwF", vbNullString)
 1185896 
?FindWindowEx(hWinDesk, 0&, "_WwB", vbNullString)
 5707422 

按顺序获得一个句柄;但无济于事,因为

got a handle, in that order; but to no avail, because

 ?AccessibleObjectFromWindow(hWin7, OBJID_NATIVEOM, iid, obj)
-2147467259 

有什么想法吗?方向?

推荐答案

按照@Comintern的建议与Spy ++更加亲密之后,我对此进行了追踪:

After getting more intimate with Spy++ as @Comintern suggested, I traced this:

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

This is the actual Window order; all windows below the OpusApp are its children

但是要了解为什么它现在可以运行,我们必须右键单击下面的每个_Ww [A_Z]:

But to understand why it is functioning now, we have to right click every _Ww[A_Z] below:

对于_WwF:

对于其子_WwB:

终于到了目标!!!! _WwG:

And finally to the goal!!!!! _WwG:

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

With this approach, it is obvious that we must add another layer to the code:

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

        Call IIDFromString(StrPtr(IID_IDispatch), iid)
        hWinDesk = FindWindowEx(hWinWord, 0&, "_WwF", vbNullString)
        hWin7 = FindWindowEx(hWinDesk, 0&, "_WwB", vbNullString)
        hFinalWindow = FindWindowEx(hWin7, 0&, "_WwG", vbNullString)
        If AccessibleObjectFromWindow(hFinalWindow, OBJID_NATIVEOM, iid, obj) = S_OK Then
            Set wordApp = obj.Application
            GetWordapp = True
        End If
    End Function

我不了解,但现在不介意的是,为什么在2个不同的实例上重复结果: Debug.print结果:

What I don't understand, but don't mind now, is why duplicate results for 2 different instances: Debug.print results:

   Instance_1 1972934 
                  x - fatores reumaticos.docx
                  FormGerenciadorCentralPacientes.docm
    Instance_2 11010524 
                  x - fatores reumaticos.docx
                  FormGerenciadorCentralPacientes.docm
    Instance_3 4857668 

但是要解决这个问题,我将改写

But to solve that, I'll adapt the marvel solution by @PGS62; resuming:

Private Function GetWordInstances() As Collection
    Dim AlreadyThere As Boolean
    Dim wd As Application
    Set GetWordInstances = New Collection
    ...code...
    For Each wd In GetWordInstances 
                If wd Is WordApp.Application Then
                    AlreadyThere = True
                    Exit For
                End If
            Next
            If Not AlreadyThere Then
                GetWordInstances.Add WordApp.Application
            End If
      ...code...
End Function

而且,为大众提供所有Word实例的迭代,而不必关闭并重新打开!!!

And, voilá, iteration for all Word instances for the masses without have to close and reopen!!!

感谢社区,感谢其他主题中的所有想法,感谢@Comintern提供重要建议.

Thanks, community, for all ideas in other threads, and @Comintern for the crucial advise.

这篇关于如何遍历多个Word实例(使用AccessibleObjectFromWindow)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆