GetActiveObjects - Get multiple active COM objects

Post your working scripts, libraries and tools for AHK v1.1 and older
lexikos
Posts: 9583
Joined: 30 Sep 2013, 04:07
Contact:

Re: GetActiveObjects - Get multiple active COM objects

16 Jun 2016, 16:33

hobboy wrote:For both instances of the application the name is simply "!{my prog CLSID}", [...]
When I have the second application running I get an extra obj to use, however this object still refers to the first application instance.
Although I don't think there's any requirement for the display name to be unique, it sounds to me like your application is just registering the same object twice. (Or maybe it fails because the name is not unique?) I doubt there's anything you can do, aside from contacting the developer.
hobboy wrote:Your post seems to suggest the keys from the associative array the function returns should contain the PID
Only for Visual Studio.
hobboy
Posts: 41
Joined: 05 Jan 2016, 09:59

Re: GetActiveObjects - Get multiple active COM objects

16 Jun 2016, 18:59

lexikos wrote:Although I don't think there's any requirement for the display name to be unique, it sounds to me like your application is just registering the same object twice. (Or maybe it fails because the name is not unique?) I doubt there's anything you can do, aside from contacting the developer.
edit:reread your comments
Bummer, I might try to get in contact with them. I've tried to get something going with IMoniker::BindToObject but my knowledge of Dll calls is new and limited https://autohotkey.com/boards/viewtopic.php?f=5&t=19065.
hobboy
Posts: 41
Joined: 05 Jan 2016, 09:59

Re: GetActiveObjects - Get multiple active COM objects

19 Jun 2016, 03:25

Ok... so I discovered how to get multiple instances of my program detected. For some reason I need to open both instances of the program basically at the same time, ie open the 2nd one while the 1st was loading. lexikos' script works great :superhappy:

Just in case anyone ever runs into the same issue...
sancarn
Posts: 224
Joined: 01 Mar 2016, 14:52

Re: GetActiveObjects - Get multiple active COM objects

24 Mar 2017, 15:39

Lexicos,

This is amazing. I wish I knew about this sooner... For the past 10 months I've been using ComObjActive(), which is great of course, but you have so much more control over the COM objects you get with this... Thanks!!

And thanks @kon for leading me here!

This should be pinned somewhere... (Or perhaps even implemented...)

------------------------------------------------------------------

Edit: For those interested I found some implementations in VBA:

VBA
VBA Using OLELib.TLB

It's amazing how when you know what to look for, you find that everyone has done exactly what you want before... You just don't know the correct terminology!
User avatar
Tigerlily
Posts: 377
Joined: 04 Oct 2018, 22:31

Re: GetActiveObjects - Get multiple active COM objects

20 Aug 2020, 00:41

Someone shared this on the AHK Discord server and thought I'd update this thread with a working v2 [a122] example and script of OP. The person who shared it said it worked as is, however I had to throw in the try-catch in the middle for it not to throw an error for me. Not sure how to debug this, but nonetheless it works. Maybe lexikos will chime in.

Error I got before inserting the try-catch block:

Error: (0x80004002) No such interface supported Line# 033
--> if (pdsp := ComObjQuery(punk, "{00020400-0000-0000-C000-000000000046}"))


Code:

Code: Select all

; Example:

list := ""
for name, obj in GetActiveObjects()
    list .= name " -- " ComObjType(obj, "Name") "`n"    
MsgBox list

; Output looks like this (my system):

; !{00024500-0000-0000-C000-000000000046} -- _Application
; !{00024505-0016-0000-C000-000000000046} -- _Application
; C:\Users\Tigerlily\Documents\setSysColors enums.xlsm -- _Workbook
; clsid:FFDE5359-5502-4F1A-8395-EFCAEEE02D3D: -- 


; v2 compatible script (v2 a122)
GetActiveObjects(Prefix:="", CaseSensitive:=false) {
    objects := Map()
    ,DllCall("ole32\CoGetMalloc", "uint", 1, "ptr*", malloc:=0) ; malloc: IMalloc
    ,DllCall("ole32\CreateBindCtx", "uint", 0, "ptr*", bindCtx:=0) ; bindCtx: IBindCtx
    ,ComCall(8, bindCtx, "ptr*", rot:=0) ; rot: IRunningObjectTable
    ,ComCall(9, rot, "ptr*", enum:=0) ; enum: IEnumMoniker
    while ComCall(3, enum, "uint", 1, "ptr*", mon:=0, "ptr", 0) = 0 ; mon: IMoniker
    {
        ComCall(20, mon, "ptr", bindCtx, "ptr", 0, "ptr*", pname:=0) ; GetDisplayName
        , name := StrGet(pname, "UTF-16")
        , ComCall(5, malloc, "ptr", pname) ; Free
        if (Prefix == "" || InStr(name, Prefix, CaseSensitive) = 1) {
            ComCall(6, rot, "ptr", mon, "ptr*", punk:=0) ; GetObject
            ; Wrap the pointer as IDispatch if available, otherwise as IUnknown.
            try
            {
                if (pdsp := ComObjQuery(punk, "{00020400-0000-0000-C000-000000000046}"))
                    ObjAddRef(pdsp.ptr),obj := ComObject(9, pdsp.ptr, 1),ObjRelease(punk)
                else
                    obj := ComObject(13, punk, 1)
            }
            catch
                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
}
Enjoy <3
-TL
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: GetActiveObjects - Get multiple active COM objects

28 Aug 2021, 06:19

Code: Select all

#Requires AutoHotkey v2.0-beta.1

GetActiveObjects(Prefix := '', CaseSensitive := false) {
	IDispatch(ptr := 0) => ComValue(9, ptr)
	IUnknown(ptr := 0) => ComValue(13, ptr)
	
	DllCall('ole32\CoGetMalloc', 
		'UInt', 1, ; dwMemContext - "This parameter must be 1."
		'Ptr*', Malloc := IUnknown(), ; IMalloc
		'HRESULT')

	DllCall('ole32\CreateBindCtx', 
		'UInt', 0, ; reserved - "This parameter is reserved and must be 0."
		'Ptr*', BindCtx := IUnknown(), ; IBindCtx
		'HRESULT')

	; IBindCtx::GetRunningObjectTable(IRunningObjectTable **pprot)
	ComCall(8, BindCtx, 'Ptr*', ROT := IUnknown())

	; IRunningObjectTable::EnumRunning(IEnumMoniker **ppenumMoniker)
	ComCall(9, ROT, 'Ptr*', EnumMoniker := IUnknown())

	RunningObjects := Map()

	; IEnumMoniker::Next(ULONG celt, IMoniker **rgelt, ULONG *pceltFetched)
	while !ComCall(3, EnumMoniker, 'UInt', 1, 'Ptr*', Moniker := IUnknown(), 'UInt*', 0)
	{
		; IMoniker::GetDisplayName(IBindCtx *pbc, IMoniker *pmkToLeft, LPOLESTR *ppszDisplayName)
		ComCall(20, Moniker, 'Ptr', BindCtx, 'Ptr', 0, 'Ptr*', &pDisplayName := 0)

		displayName := StrGet(pDisplayName)

		; IMalloc::Free(void *pv)
		ComCall(5, Malloc, 'Ptr', pDisplayName)

		; If no prefix or starts with prefix...
		if (Prefix = '' || (InStr(displayName, Prefix, CaseSensitive) = 1))
		{
			; IRunningObjectTable::GetObject(IMoniker *pmkObjectName, IUnknown **ppunkObject)
			ComCall(6, ROT, 'Ptr', Moniker, 'Ptr*', Obj := IUnknown()) ; Wrap as IUnknown; ...

			; ...rewrap as IDispatch if available.
			try
			{
				; [v2.0-beta.1] If the interface is supported, returns 
				; the interface pointer wrapped in a ComValue as IUnknown.
				Obj := ComObjQuery(Obj, '{00020400-0000-0000-C000-000000000046}') ; IID_IDispatch
				
				; We have an IDispatch pointer, but it's wrapped as an IUnknown.
				; Increment the RefCount once to keep it alive during reassignment, 
				; otherwise the pointer will have been automatically released.
				ObjAddRef(Obj.Ptr)
				Obj := IDispatch(Obj)
			}

			; Store it in the return array by suffix.
			RunningObjects[SubStr(displayName, StrLen(Prefix) + 1)] := Obj
		}
	}

	return RunningObjects
}
lexikos
Posts: 9583
Joined: 30 Sep 2013, 04:07
Contact:

Re: GetActiveObjects - Get multiple active COM objects

28 Aug 2021, 18:51

; Increment the RefCount once to keep it alive during reassignment,
; otherwise the pointer will have been automatically released.
This is a bit misleading. You need to increment the reference count to "create a new reference" that you can give to the new wrapper object, because the old wrapper object owns the existing reference. It is not just to keep it alive during reassignment, but beyond the assignment, and to avoid creating a dangling pointer due to incorrect reference counting.
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: GetActiveObjects - Get multiple active COM objects

28 Aug 2021, 19:42

thats certainly a nicer way to put it :P
kauan014
Posts: 55
Joined: 18 Feb 2021, 20:03

Re: GetActiveObjects - Get multiple active COM objects

17 Oct 2021, 14:21

How to "define" the active object name?
When i create one with: viewtopic.php?t=6148
And search using GetActiveObject, it does not have a "name".
Spitzi
Posts: 313
Joined: 24 Feb 2022, 03:45

Re: GetActiveObjects - Get multiple active COM objects

17 Sep 2022, 14:02

Hi.

This is certainly out of my league, but I thought I'd ask anyway ;)

I tried GetActiveObjects to extract the COMobj of an embedded Word instance with this:

Code: Select all

for name, obj in GetActiveObjects() {
    list .= name " -- " ComObjType(obj, "Name") "`n`n`n"
	if InStr(name,"FINDING") {
		oWord := obj
		MsgBox, %name% hier %obj%
	}
}
MsgBox %list%
docsCount := oWord.Documents.Count
MsgBox, docsCount:%docsCount%
I get an error on the line "docsCount := oWord.Documents.Count" and I don't know why. Thanks.
Spitzi
Posts: 313
Joined: 24 Feb 2022, 03:45

Re: GetActiveObjects - Get multiple active COM objects

19 Sep 2022, 06:39

Ok, stupid me.

instead of

Code: Select all

oWord := obj
I should have used

Code: Select all

oWord := obj.Application
I am still struggling, though: I have a program that embeds Word and the user might open other word documents standalone in word. Using GetActiveObjects, I see that there are sometimes two or three word instances open, with a varying number of open documents.

How do I make sure that I get the right object, i.e. the one the user is editing at the moment, or (if another program is active), the document that was last edited/is in front of the other word windows ?

Thanks

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: No registered users and 57 guests