GetActiveObjects - Get multiple active COM objects

Post your working scripts, libraries and tools
lexikos
Posts: 6445
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

GetActiveObjects - Get multiple active COM objects

22 Feb 2015, 00:36

GetActiveObjects([Prefix:="", CaseSensitive:=false])

Returns an associative array of active COM objects, where each key is the item moniker or suffix of the object. If Prefix is specified, only objects whose item monikers match the given prefix are returned, and the prefix is omitted from the returned keys.

Code: Select all

; GetActiveObjects v1.0 by Lexikos
; http://ahkscript.org/boards/viewtopic.php?f=6&t=6494
GetActiveObjects(Prefix:="", CaseSensitive:=false) {
    objects := {}
    DllCall("ole32\CoGetMalloc", "uint", 1, "ptr*", malloc) ; malloc: IMalloc
    DllCall("ole32\CreateBindCtx", "uint", 0, "ptr*", bindCtx) ; bindCtx: IBindCtx
    DllCall(NumGet(NumGet(bindCtx+0)+8*A_PtrSize), "ptr", bindCtx, "ptr*", rot) ; rot: IRunningObjectTable
    DllCall(NumGet(NumGet(rot+0)+9*A_PtrSize), "ptr", rot, "ptr*", enum) ; enum: IEnumMoniker
    while DllCall(NumGet(NumGet(enum+0)+3*A_PtrSize), "ptr", enum, "uint", 1, "ptr*", mon, "ptr", 0) = 0 ; mon: IMoniker
    {
        DllCall(NumGet(NumGet(mon+0)+20*A_PtrSize), "ptr", mon, "ptr", bindCtx, "ptr", 0, "ptr*", pname) ; GetDisplayName
        name := StrGet(pname, "UTF-16")
        DllCall(NumGet(NumGet(malloc+0)+5*A_PtrSize), "ptr", malloc, "ptr", pname) ; Free
        if InStr(name, Prefix, CaseSensitive) = 1 {
            DllCall(NumGet(NumGet(rot+0)+6*A_PtrSize), "ptr", rot, "ptr", mon, "ptr*", punk) ; GetObject
            ; Wrap the pointer as IDispatch if available, otherwise as IUnknown.
            if (pdsp := ComObjQuery(punk, "{00020400-0000-0000-C000-000000000046}"))
                obj := ComObject(9, pdsp, 1), ObjRelease(punk)
            else
                obj := ComObject(13, punk, 1)
            ; Store it in the return array by suffix.
            objects[SubStr(name, StrLen(Prefix) + 1)] := obj
        }
        ObjRelease(mon)
    }
    ObjRelease(enum)
    ObjRelease(rot)
    ObjRelease(bindCtx)
    ObjRelease(malloc)
    return objects
}
Examples

Show the item monikers and (default) interface names of all active objects:

Code: Select all

for name, obj in GetActiveObjects()
    list .= name " -- " ComObjType(obj, "Name") "`n"
MsgBox %list%
Show the process ID and solution filename of all running instances of Visual Studio Express 2013:

Code: Select all

for pid, dte in GetActiveObjects("!WDExpress.DTE.12.0:")
    list .= pid " -- " dte.Solution.FullName "`n"
MsgBox %list%
Related: ObjRegisterActive
User avatar
TLM
Posts: 1418
Joined: 01 Oct 2013, 07:52
GitHub: TLMcode
Contact:

Re: GetActiveObjects - Get multiple active COM objects

22 Feb 2015, 09:28

Sweet, this will def. come in handy :)
ty
for direct help, tweet at me: https://twitter.com/CW_DTech
IMEime
Posts: 750
Joined: 20 Sep 2014, 06:15

Re: GetActiveObjects - Get multiple active COM objects

23 Jun 2015, 19:41

Woo.
Thanks nice code.
Last edited by IMEime on 29 Jan 2016, 03:24, edited 1 time in total.
autocart
Posts: 149
Joined: 12 May 2014, 07:42

Re: GetActiveObjects - Get multiple active COM objects

14 Sep 2015, 12:20

Hi lexikos or anybody else understanding this,

What am I doing wrong? I have at 2 instances of MS Publisher 2002 running on Win 8.1.
In the first example, which should list all, I get:
GetActiveObjects-message.JPG
GetActiveObjects-message.JPG (18.13 KiB) Viewed 9019 times
I cant get it to work. Any help highly appreciated.

I also tried to use Acc_ObjectFromWindow() (as shown here for Excel: http://www.autohotkey.com/board/topic/7 ... plication/) but could not get it to work either.

My testing code is simple:

Code: Select all

MButton::
$PbApp := ComObjActive("Publisher.Application")
$PbApp.ActiveDocument.ActiveView.Zoom := -2 ; whole page
It works well with the first instance. But I want it to work in both active instances.
(I'd also like it to work on Win7 with Publisher 2007 as well. :-) )
Any help highly appreciated. Please. Thank you.

EDIT: I know that this particular example can be done by a key-combo inside Publisher too. This is only a simple example to see if it works.
kon
Posts: 1756
Joined: 29 Sep 2013, 17:11

Re: GetActiveObjects - Get multiple active COM objects

14 Sep 2015, 13:12

Publisher seems to be different in that it launches a new process (MSPUB.EXE) for each document, but they can all still be enumerated with the documents collection:

Code: Select all

PbApp := ComObjActive("Publisher.Application")
for Doc, in PbApp.Documents
  MsgBox, % Doc.Name
autocart
Posts: 149
Joined: 12 May 2014, 07:42

Re: GetActiveObjects - Get multiple active COM objects

14 Sep 2015, 13:46

Hey, thx.
How do I address the currently active document (the one in the foreground with focus where the user works in)?

EDIT: No, Publisher (2002) does not know "Documents".
kon
Posts: 1756
Joined: 29 Sep 2013, 17:11

Re: GetActiveObjects - Get multiple active COM objects

14 Sep 2015, 17:03

EDIT: No, Publisher (2002) does not know "Documents".
That complicates things. I was not able to find docs for Office XP (2002), although this seems to suggest that 2002 should have an Application.Documents collection - perhaps there is some other internal difference preventing it from being enumerated? Can you access an individual item (not using a for-loop)? ie: MsgBox, % PbApp.Documents(1).Name

The Application.ActiveDocument property seems to be pretty useless, even in recent versions, from an AHK perspective.
Currently I don't see a better solution than the following. This seems to work in the 2010 version, but to get the active window, it needs to be active; it can't just be the topmost publisher window (as with ComObjActive for some other applications), it needs to be the topmost window as reported by Window Spy / AHK because it uses WinExist() to match the window.

I also ran into a weird issue where the window would appear active, but Window Spy and WinExist were reporting blank. See the comments.

Code: Select all

^1::
PbApp := ComObjActive("Publisher.Application")
PbDoc := GetPubDoc(PbApp)
MsgBox, % PbDoc.Name
return

GetPubDoc(PbApp, Title := "A")
{
  ; I was getting some results I had not encountered before: WindowSpy was reporting a blank
  ; window name/class/exe if a Publisher window was newly activated. I needed to click on the 
  ; window a second time (even though it appeared to be active) in order for Window Spy to
  ; recognize it. WinActivate was the solution.
  if (Title = "A" && WinExist(Title) = 0x0)
  {
    WinActivate, ahk_class MSWinPub
    WinWaitActive, ahk_class MSWinPub,, 1
    if (ErrorLevel)
      return
  }
  if (WinHwnd := WinExist(Title))
    for Doc, in PbApp.Documents
      if (Doc.ActiveWindow.Hwnd = WinHwnd)
        return  Doc
}
autocart
Posts: 149
Joined: 12 May 2014, 07:42

Re: GetActiveObjects - Get multiple active COM objects

15 Sep 2015, 00:30

Hey Kon,
I could test it now in Publisher 2007 on Win 7 (64bit) at work.

IT WORKS HERE!!!! THANK YOU SOOOO MUCH, Kon!!

This installment of Pub 2007 is even more important, since it is at work, where I wanted to employ it in the first place, but I had started the thread at home using Pub 2002 for testing. (Maybe it has something to do with Win 8.1 and Pub XP (2002) being already a little older - I also got an error message during install but everything seemed to work so far).
So, everything is super, perfect, and ok now. Thanks again. You are awesome!!

Kind regards,
autocart

P.S.: The object catalog (F2) in the VBA editor (Alt+F11) does list"Documents" in Pub 2007, but in Pub 2002 it does not list it. So I guess it really does not exist in Pub 2002.
wpb
Posts: 54
Joined: 14 Dec 2015, 01:53

Re: GetActiveObjects - Get multiple active COM objects

26 Jan 2016, 07:59

Any idea why this might not return any results when running as Administrator on Win7/8, but does return results when run with normal privileges?
autocart
Posts: 149
Joined: 12 May 2014, 07:42

Re: GetActiveObjects - Get multiple active COM objects

26 Jan 2016, 08:30

What is running as admin?
Whatever it is, could it have to do with network paths?
wpb
Posts: 54
Joined: 14 Dec 2015, 01:53

Re: GetActiveObjects - Get multiple active COM objects

26 Jan 2016, 12:41

Sorry, I mean when running as admin an ahk script containing this function, and the first example code above ("Show the item monikers and (default) interface names of all active objects:"). I don't think network paths come into it in this case, but thanks for the suggestion.
wpb
Posts: 54
Joined: 14 Dec 2015, 01:53

Re: GetActiveObjects - Get multiple active COM objects

26 Jan 2016, 17:06

Actually, it seems this is only happening on Win 8, if that gives a clue. I'm looking at adding some debug output to the function to see where it fails. Lexikos, any pointers on where to start? (It's over my level of understanding atm, but I'll start reading!)
lexikos
Posts: 6445
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: GetActiveObjects - Get multiple active COM objects

26 Jan 2016, 20:12

I do not believe it is specific to Windows 8. Perhaps your non-Windows 8 system has UAC disabled or absent, in which case both programs are probably running as admin.

I think there is a separate "running object table" for each integrity level. Each program can only access active objects of programs at the same integrity level.
wpb
Posts: 54
Joined: 14 Dec 2015, 01:53

Re: GetActiveObjects - Get multiple active COM objects

26 Jan 2016, 23:15

I think you are right - this is to do with UAC. Unfortunately, it's a script that is working fine on my system and on others' but not this particular Win 8 system, but it makes debug harder.

Nevertheless, I would have expected when running this script as admin, it would be able to "see" all running objects, because they will all have an equal or lower integrity level?
lexikos
Posts: 6445
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: GetActiveObjects - Get multiple active COM objects

27 Jan 2016, 00:10

"the same" does not mean "equal or lower".

As far as I know, the limitation applies to all systems with UAC. If a program is running on a different integrity level, you cannot access its active objects. This is particularly problematic with EnableUIAccess:
ComObjActive/ComObjGet may fail. UIA programs are probably only able to access the active objects of other UIA programs, not regular programs. For example, ComObjActive("Word.Application") will fail because Word is not marked for UI access. The reverse is also true: objects registered by UIA scripts can only be accessed by other UIA programs/scripts.
wpb
Posts: 54
Joined: 14 Dec 2015, 01:53

Re: GetActiveObjects - Get multiple active COM objects

27 Jan 2016, 02:41

"the same" does not mean "equal or lower".
No, quite, I totally took your point, it just seems like a strange OS model that processes with higher integrity level can't see those with same or lower. Obviously the opposite makes sense, but this to me seems odd. Seems like there must be some way around it - I'm just not clever enough to know what it is!

I'm pretty sure that it is working on some systems with UAC enabled, though, and the object I'm looking for is an Excel workbook - I'd be surprised if people are running Excel as admin, but I will check. If they're not, it means the admin-level AHK script is successfully seeing the objects of the non-admin Excel process in some cases, but not this particular one.

[OT side note: I also find that not running my script as admin means it can't write (using FileAppend) a file in A_Temp. Is that to be expected? Is there a safe location where you can put a temporary file even when the script is not running as admin on a system with UAC enabled?]
lexikos
Posts: 6445
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: GetActiveObjects - Get multiple active COM objects

27 Jan 2016, 06:20

You can see for yourself whether it applies to your system by running GetActiveObjects example #1. It shows all objects on "the" running object table. Run some program which has an active object, like Word or Visual Studio. If it shows up in the list, run the script or the program as admin and check again.

Another user figured it out by himself: ComObjActive doesn't work with script as Admin

If I google "GetActiveObject administrator", I find several instances of the problem in other languages, with other programs. At least one refers to it as a bug in Windows:
The issue is that when installed (on Vista/7/8) we run with the UIAccess privilage (allowing NVDA to access the UI of apps running as administrator), but there seems to be a bug in Windows (specifically the running object table I think) that does not allow access to lower privilage COM objects from a higher level process.
Source: GetActiveObject fails when running with uiAccess · Issue #2483 · nvaccess/nvda
That project's comHelper.getActiveObject function, referenced on that page, works around the problem by using a separate process to get the object. Objects can be passed from process to process via LresultFromObject/ObjectFromLresult. Those functions are also used with WM_GETOBJECT, which is used internally by AccessibleObjectFromWindow. This function is wrapped by Acc_ObjectFromWindow in Acc.ahk, which can be used with Excel and other Office apps, but not all apps which register active objects. I don't have Office installed to confirm that it works across process boundaries.
I also find that not running my script as admin means it can't write (using FileAppend) a file in A_Temp. Is that to be expected?
No. It works just fine for me. I suppose the Temp folder permissions can be changed, but I wonder how many other programs would fall over if it was not writeable.

Code: Select all

f = %A_Temp%\foo.txt
FileAppend foo, % f
Run % f
Sleep 1000
FileDelete % f
wpb
Posts: 54
Joined: 14 Dec 2015, 01:53

Re: GetActiveObjects - Get multiple active COM objects

27 Jan 2016, 16:32

Thanks, Lexikos, that's very informative.
hobboy
Posts: 41
Joined: 05 Jan 2016, 09:59

Re: GetActiveObjects - Get multiple active COM objects

15 Jun 2016, 00:18

Thanks for the script lexikos, that's very handy.
Unfortunately I can't seem to get this working correctly with the application I'm using, running two at once. I'll Call the application ComObjType "MyAppType"

The first issue I have is with the associative array. For both instances of the application the name is simply "!{my prog CLSID}", so I have to change the function to output a regular array so I can get multiple objects with the same name.

When I have the second application running I get an extra obj to use, however this object still refers to the first application instance.

Some possibly useful of information:

first instance of the program starts: two COM objects created
1) display name = "!{my prog CLSID}", ComObjType= "MyAppType"
2) display name = "c:\program files (x86)\myapp\startscript.blah", ComObjType= "MyAppType" (this is the script which starts the application)

when second instance starts, the same kind of object as above is created, but not the second one:
1) display name = "!{my prog CLSID}", ComObjType= "MyAppType"

The two applications have separate processes "myapp.exe"

Your post seems to suggest the keys from the associative array the function returns should contain the PID, but as mentioned above I only get "!{CLSID}". Do you know how I can get the second application instance? Is this a limitation of your script or could it be worked around?
hobboy
Posts: 41
Joined: 05 Jan 2016, 09:59

Re: GetActiveObjects - Get multiple active COM objects

16 Jun 2016, 04:17

I've had a bit more of a look into this and have tried to understand the GetActiveObjects script.

In the GetObject documentation it states:
Generally, you call the IRunningObjectTable::GetObject method only if you are writing your own moniker class (that is, implementing the IMoniker interface). You typically call this method from your implementation of IMoniker::BindToObject.
I don't know whether this matters but I'm wondering if IMoniker::BindToObject may fix my problem :think:.

Instead of using IRunningObjectTable::GetObject to retrieve the object I was trying to use IMoniker::BindToObject, however I'm not too familiar with this stuff and haven't got it to work

Return to “Scripts and Functions”

Who is online

Users browsing this forum: Dumitas and 57 guests