Page 3 of 3

Re: ObjRegisterActive

Posted: 27 Oct 2017, 20:22
by lexikos
Your object would need to implement the IConnectionPointContainer and IConnectionPoint interfaces. In order to do that, you would need to construct the object yourself, including an IUnknown interface implementation which can be queried for those interfaces. You could not simply pass an AutoHotkey object to the client.

Re: ObjRegisterActive

Posted: 27 Jan 2018, 18:06
by nnnik
It seems I never said anything.
I'm very greatful to you for this function. It has been used in several projects of mine and really makes coding more powerful.

Re: ObjRegisterActive

Posted: 27 Mar 2018, 14:33
by A_User
(Sorry for the previous post which I deleted as it had no useful information)

It seems the code below causes memory leaks. I'm guessing ObjRegisterActive() or ComObjActive() is something to do with it.

Main.ahk

Code: Select all

#SingleInstance, Force

loop {
    testMemoryLeak()
}
Return

~Esc::ExitApp

testMemoryLeak() {
    
    _sGUID         := CreateGUID()
    _o := new TestObjRegisterActive
    ObjRegisterActive( _o, _sGUID )

    _sPath         := A_ScriptDir "\Remote.ahk"
    Run, "%A_AhkPath%" "%_sPath%" %_sGUID%,, UseErrorLevel, _iPID    
    
    ; Wait for the remote script to be activated
    while ! _o.oRemote {
        sleep 10
    }
    
    ; ( doing something in an actual script )
    
    ; Done with the remote script
    _o.oRemote.exit()
    
    ObjRegisterActive( _o, "" )
    return _iPID
    
}

class TestObjRegisterActive {
    
    oRemote := ""
    
    __New() {
        this.TickCount := A_TickCount
    }
    
    activate( sRemoteGUID ) {    
        this.oRemote := ComObjActive( sRemoteGUID )
        tooltip % "(From Main) Actiavted: " sRemoteGUID
    }
        
}
Remote.ahk

Code: Select all

#Persistent 
#SingleInstance, Off

_sMainGUID       := %0% ; script argument

_oMain           := ComObjActive( _sMainGUID )

_oRemote         := new Remote
_sRemoteGUID     := CreateGUID()
ObjRegisterActive( _oRemote, _sRemoteGUID )

_oMain.activate( _sRemoteGUID )

ObjRegisterActive( _oRemote, "" )

tooltip % "From Remote: " _oMain.TickCount

Return
~Esc::ExitApp

class Remote {
    
    exit() {
        _func := ObjBindMethod( this, "_exitApp" )
        SetTimer, % _func, -1
    }
        _exitApp() {
            ExitApp
        }
    
}
Tested with: AutoHotkey v1.1.28.00, Windows 7 64bit

Re: ObjRegisterActive

Posted: 28 Mar 2018, 03:52
by sancarn
lexicos wrote:
min wrote:Does using ObjRegisterActive mean that now we can write AHK apps which can be controlled by COM by other programs written in other languages?
Yes. It's easy to do from VBScript. However, it seems GetObject() requires a ProgID rather than a CLSID, so you need to register it in the registry.

Code: Select all

' First set HKCR\testy\CLSID (default value) to the GUID
set x = GetObject(,"testy")
x.Message("Hello, world!")

Apparrently according to this site:

http://web.archive.org/web/200210011332 ... /index.asp

You can also use:

Code: Select all

Set o = GetObject(, "clsid:5c172d3c-c8bf-47b0-80a4-a455420a6911")
A full example can be found here

Code: Select all

Sub CreateGorillaAndEatBanana()
  Dim gc as IApeClass
  Dim ape as IApe
  Dim sz as String
  sz = "clsid:571F1680-CC83-11d0-8C48-0080C73925BA:"
' get the class object for gorillas
  Set gc = GetObject(sz)
' ask gorilla class object to create a new gorilla
  Set ape = gc.CreateApe()
' ask gorilla to eat a banana
  ape.EatBanana
End Sub
I've had no luck with either using CLSID or PROGID sadly (in vba), I just receive "Runtime error 429 - ActiveX component can't create object", which seems a bit odd, but perhaps it's due to IT policy or something **shrug**

Re: ObjRegisterActive

Posted: 11 Nov 2020, 13:14
by hasantr
If the object does not respond, the crash screen of windows appears. But instead of that screen, can I request the called script file to close automatically? I don't want to see the windows crash message because with my main script I can see that the object is still not healthy and restart it.

I'm talking about this kind of crash.
Image

My goal is to create an isolated process against crashes. Even if there is a crash, the user can continue the process from where it left off without realizing it. The main script will create the object again.

I managed to solve it in a different way.

Re: ObjRegisterActive

Posted: 29 Nov 2020, 19:22
by hasantr
I wonder if anyone knows how to use it with Python? Or is it already impossible?

Re: ObjRegisterActive

Posted: 14 Jan 2021, 08:18
by KiddoV
Very nice function, but it doesn't work with Class_SQlite object and Neutron object with NeutronObj.wnd.function() even though, they are all ahk object. Any idea why?

Re: ObjRegisterActive

Posted: 18 Oct 2021, 11:26
by 20170201225639
lexikos wrote:
29 Jan 2015, 03:22
limitations:
...
[*]Some invocations are ambiguous; for example, foo.bar triggers foo.__Call and then foo.__Get.[/list][/list]


I've used this to share an array from script A with script B. Some of the items of the array are function references. I seem unable to check if IsObject(Array[index]) without thereby *triggering* the function references (to hilarious effect in my case, because each of the functions launches an application, so I ended up having to log off my laptop). I suppose this is the ambiguity talked about here? If so, any way to overcome it?

Re: ObjRegisterActive

Posted: 13 Jul 2022, 10:34
by crocodile
This COM object does not seem to be suitable for multithreading.
I created three child processes which operate on the same object at the same time and end up with the wrong result.

Code: Select all

test := { x: 0 }

cb := buffer(16, 0)
CLSID := "{6B39CAA1-A320-4CB0-8DB4-352AA81E460E}"
DllCall("ole32\CLSIDFromString", "wstr", CLSID, "ptr", cb.ptr, "int")
DllCall("oleaut32\RegisterActiveObject", "ptr", ObjPtr(test), "ptr", cb.ptr, "uint", 0, "uintP", &cookie := 0, "uint")

MsgBox "wait"
MsgBox test.x ; 12342
Three sub-threads

Code: Select all

test := ComObjActive("{6B39CAA1-A320-4CB0-8DB4-352AA81E460E}")
sleep 500
loop 10000
	test.x:=test.x+1

Re: ObjRegisterActive

Posted: 13 Jul 2022, 21:00
by lexikos
What are you expecting?
  • They aren't "sub-threads". They are processes.
  • The object you registered (and all of its methods and properties) only exists in one place: the original process that created it.
  • All calls to the object through COM are marshaled to the original process, which is the only one that can actually execute them.
If you have three programmers trying to write code using one keyboard, how well will it work?

Re: ObjRegisterActive

Posted: 13 Jul 2022, 21:24
by crocodile
Thank you. I think I understand how it works. I created a mutex lock to protect it.

Code: Select all

mutex:= DllCall("CreateMutexW", "Ptr", 0, "int", 0, "WStr", t, "int")
DllCall("WaitForSingleObject", "int", mutex, "UInt", 0xFFFFFFFF)
loop 10000
	test.x:=test.x+1
DllCall("ReleaseMutex", "int", mutex), DllCall('CloseHandle', 'int', mutex)

Re: ObjRegisterActive

Posted: 27 Sep 2022, 22:56
by tester
While I'm experimenting with this function, I have encountered an error.
Error: 0x80004002 - No such interface supported

Line#
003: SetWorkingDir,%A_ScriptDir%
006: sGUID := CreateGUID()
007: oSample := new Sample()
008: oBoundSample := ObjBindMethod( oSample, "test" )
009: ObjRegisterActive( oBoundSample, sGUID )
010: oCOMBoundSample := ComObjActive( sGUID )
011: oSink := new Sink()
---> 012: ComObjConnect( oCOMBoundSample, oSink )
It is somehow possible to connect to COM-nized AutoHotkey objects?

Code: Select all

sGUID           := CreateGUID()
oSample         := new Sample()
oBoundSample    := ObjBindMethod( oSample, "nonexistent" )
ObjRegisterActive( oBoundSample, sGUID )
oCOMBoundSample := ComObjActive( sGUID )
oSink           := new Sink()
ComObjConnect( oCOMBoundSample, oSink )
Try {
    oCOMBoundSample.Call()
} Catch _e {
    msgbox % "Error: " _e.Message
}

class Sink {
    Call( aParams* ) {
        msgbox % "A method is called: " aParams.1
    }
}

class Sample {
    test() {
        msgbox % "this is a test!"
    }
}

Re: ObjRegisterActive

Posted: 27 Sep 2022, 23:03
by lexikos
No. AutoHotkey objects don't have events, let alone COM event interfaces. What's your purpose?

Re: ObjRegisterActive

Posted: 27 Sep 2022, 23:11
by tester
I'm looking for a way to catch a case that a bound func object doesn't have an actual callable method. So I just tried to see what happens if I COM-nize the bound func object and call it, hoping that it throws an exception saying that a function does not exist.

Re: ObjRegisterActive

Posted: 28 Sep 2022, 02:30
by lexikos
The bound function is just an object and a method name. To know whether the method "nonexistent" is valid, you would need to query the original object. You should do this before you bind the method, if you do it at all. It may or may not be possible in AutoHotkey v1, depending on the object. In AutoHotkey v2 you could use HasMethod().

The only alternative is to catch the exception that is thrown when you try to call the bound function. It is not a good option.

If you have not done it already, I would suggest starting a new topic in Ask for Help with the details of your actual goal and any attempts at a solution.

Re: ObjRegisterActive

Posted: 10 Mar 2023, 11:31
by wpb
Is there an AHK v2 version of ObjRegisterActive()?

Re: ObjRegisterActive

Posted: 24 Nov 2023, 13:33
by Descolada
Converted to AHK v2:

Code: Select all

/*
    ObjRegisterActive(Object, CLSID, Flags:=0)
    
        Registers an object as the active object for a given class ID.
        Requires AutoHotkey v1.1.17+; may crash earlier versions.
    
    Object:
            Any AutoHotkey object.
    CLSID:
            A GUID or ProgID of your own making.
            Pass an empty string to revoke (unregister) the object.
    Flags:
            One of the following values:
              0 (ACTIVEOBJECT_STRONG)
              1 (ACTIVEOBJECT_WEAK)
            Defaults to 0.
    
    Related:
        http://goo.gl/KJS4Dp - RegisterActiveObject
        http://goo.gl/no6XAS - ProgID
        http://goo.gl/obfmDc - CreateGUID()
*/
ObjRegisterActive(obj, CLSID, Flags:=0) {
    static cookieJar := Map()
    if (!CLSID) {
        if (cookie := cookieJar.Remove(obj)) != ""
            DllCall("oleaut32\RevokeActiveObject", "uint", cookie, "ptr", 0)
        return
    }
    if cookieJar.Has(obj)
        throw Error("Object is already registered", -1)
    _clsid := Buffer(16, 0)
    if (hr := DllCall("ole32\CLSIDFromString", "wstr", CLSID, "ptr", _clsid)) < 0
        throw Error("Invalid CLSID", -1, CLSID)
    hr := DllCall("oleaut32\RegisterActiveObject", "ptr", ObjPtr(obj), "ptr", _clsid, "uint", Flags, "uint*", &cookie:=0, "uint")
    if hr < 0
        throw Error(format("Error 0x{:x}", hr), -1)
    cookieJar[obj] := cookie
}

Re: ObjRegisterActive

Posted: 04 Dec 2023, 16:53
by wpb
@Descolada, many thanks for this. Seems to be working fine.