ObjRegisterActive

Post your working scripts, libraries and tools
min

Re: ObjRegisterActive

02 Feb 2015, 08:16

If use is allowed unconditionally, does that mean there are no terms of use?
Hehe, the logic says yes, but when you don't specify terms of use, than default terms of use apply which are set by your state, which often set maximum protection by default (nobody is allowed to use your code in any way).
That's why it's necessary to specify "terms of use", even when you allow others to use it "unconditionally" - WTFPL or Public Domain ...

It's like some sort of default value in the function or class; if you don't specify it, that doesn't mean there's no value - the default value set by your country applies. ;)
WTFPL or equivalent
Thank you sir. :clap:
min

Re: ObjRegisterActive

02 Feb 2015, 09:08

It works like this :)

Code: Select all

NewAHKScript("ObjRegisterActive", "SomeCode", "Lexikos")				; no license/terms of use --> "Maximum protection (set by the state)" applies
NewAHKScript("ObjRegisterActive", "SomeCode", "Lexikos", "WTFPL")		; WTFPL  --> "WTFPL" applies
return

NewAHKScript(Name, Code, Author, License="Maximum protection (set by the state)") {
	; ...
}
User avatar
Relayer
Posts: 118
Joined: 30 Sep 2013, 13:09
Location: Delaware, USA

Re: ObjRegisterActive

02 Feb 2015, 10:22

Relayer wrote:
The first Msgbox works but the for loop complains it cannot find a member.

Lexikos wrote:
There are actually two problems:

"Member not found" comes from requesting the member DISPID_NEWENUM instead of attempting to resolve the name "_NewEnum". This works well with COM objects (since some don't actually resolve the name "_NewEnum"), but AutoHotkey objects don't respond to that ID.

Even if you call _NewEnum() directly, you can't use an AutoHotkey enumerator object remotely because it expects variable references to put the items into. The for-loop passes a normal variable reference, not Variant reference (a ComObject with VT_BYREF|VT_VARIANT). You can pass a Variant reference, but the AutoHotkey object at the other end will just dereference it and pass a useless value to the enumerator.
Given the above, I'm finding it inconvenient to use this for communication between scripts. It seems as if I need to know the names of the object's keys in advance since I cannot use _NewEnum() or a for loop. The use of a hidden GUI to pass a variable value via text still look attractive. Perhaps I'm missing something.

Relayer
GeekDude
Posts: 844
Joined: 02 Oct 2013, 22:13

Re: ObjRegisterActive

02 Feb 2015, 10:28

Well, you can pass strings, numbers, objects, and functions still, as well as create APIs for your script to be manipulated by outside programs. While you wouldn't be able to use _NewEnum(), you could always implement your own loop iteration system that would work over COM (requiring a small amount of boilerplate). However, it would be nice if AHK's IDispatch interface worked with for loops out of the box.
lexikos
Posts: 6201
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: ObjRegisterActive

02 Feb 2015, 13:58

It seems as if I need to know the names of the object's keys in advance since I cannot use _NewEnum() or a for loop. The use of a hidden GUI to pass a variable value via text still look attractive.
You can't use _NewEnum() or a for loop with your hidden GUI method or any of the other methods either, so I'm really not seeing your point.
Hehe, the logic says yes, but when you don't specify terms of use,
My rhetorical question was not about what happens when I don't specify a license, but about the semantics of asking for "terms of use". You asked for terms of use, but too bad, you can't have any.
User avatar
Relayer
Posts: 118
Joined: 30 Sep 2013, 13:09
Location: Delaware, USA

Re: ObjRegisterActive

02 Feb 2015, 18:13

Lexikos,

I specified that it would just be text passed across the hidden Gui. I never expected to be able to send objects that way.
The use of a hidden GUI to pass a variable value via text still look attractive.
Relayer
lexikos
Posts: 6201
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: ObjRegisterActive

02 Feb 2015, 20:58

You can do that with ObjRegisterActive, but also have other capabilities that you could immediately utilize if you find a need. I don't see what's attractive about the hidden GUI method.
tmplinshi
Posts: 1278
Joined: 01 Oct 2013, 14:57

Re: ObjRegisterActive

04 Feb 2015, 09:51

Very useful, thanks!
Coco
Posts: 771
Joined: 29 Sep 2013, 20:37
GitHub: cocobelgica

Re: ObjRegisterActive

21 Feb 2015, 22:42

If the script quits while running a method called by a remote script, the remote script will receive an error.
What's the most efficient way to check from the host script if there are instances of the active object(to prevent premature quitting)? Edit, just saw this comment:
A proper COM server might implement the IExternalConnection interface to detect when all external connections have been released, then exit.
Any tips on how to implement this for a particular object? Given the following draft below, how would I use it for a particular object(active):

Code: Select all

class IExternalConnection
{
	__New(self)
	{
		if !ptr := IExternalConnection.GetAddress("vtable")
		{
			IExternalConnection.SetCapacity("vtable", 5 * A_PtrSize)
			ptr := IExternalConnection.GetAddress("vtable")

			count := [3, 1, 1, 3, 4]
			for i, name in ["QueryInterface", "AddRef", "Release", "AddConnection", "ReleaseConnection"]
			{
				cb := RegisterCallback(IExternalConnection[name], "F", count[i], i-1)
				NumPut(cb, ptr + (i-1) * A_PtrSize)
			}
		}

		ObjSetCapacity(this, "_connect", 2 * A_PtrSize)
		NumPut(&self, NumPut(ptr, this.__Ptr := ObjGetAddress(this, "_connect")))
	}

	QueryInterface(iid, ppvObject)
	{

	}

	AddRef()
	{

	}

	Release()
	{

	}

	AddConnection(exconn, dwreserved)
	{

	}

	ReleaseConnection(extconn, dwreserved, fLastReleaseCloses)
	{

	}
}
Client-side, can I use try ActiveObject.Method() to suppress the above error? If an instance of the active object exists in the remote script and the host script quits, will __Delete be called?
When a client retrieves an instance of an active object and that object has an AddRef method, does it get called?(edit: realized that AddRef is a member of IUnknown)
lexikos
Posts: 6201
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: ObjRegisterActive

22 Feb 2015, 00:26

Some options:
  • Implement your own IDispatch interface like ComDispatch.ahk, so you control what QueryInterface does.
  • Implement an IDispatch wrapper which just calls into the AutoHotkey implementation, but override QueryInterface.
  • Hook into the AutoHotkey implementation (for all Objects script-wide) by using NumGet to retrieve the QueryInterface implementation and NumPut to replace it.
  • Wait for me to implement a QueryInterface meta-method or some other mechanism to implement additional interfaces. (You might be waiting forever.)
  • Grab the C++ source code and implement it yourself.
Client-side, can I use try ActiveObject.Method() to suppress the above error?
It's a normal COM error, which can be suppressed by ComObjError(0) or Try.
If an instance of the active object exists in the remote script and the host script quits, will __Delete be called?
What's __Delete, and how would it be called? The remote script just has a COM object which implements IDispatch. When the remote script no longer needs its reference to the object, it just calls IUnknown::Release(). If the server is no longer running, the real object no longer exists and so neither does the __Delete method.
When a client retrieves an instance of an active object and that object has an AddRef method, does it get called?
The remote script gets a pointer to a proxy object. On each client, the proxy object has its own reference count. Server side, the COM libraries have at least one reference to the object, in the running object table. I do not think AddRef is called when a client connects.
lexikos
Posts: 6201
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: ObjRegisterActive

22 Feb 2015, 00:46

Btw, I think IExternalConnection is typically only used by out-of-process COM servers, where the process is started indirectly via a COM library in order to create objects. If you're providing an interface to automate an application, and the user exits the application, I think it's more appropriate to just notify clients that the application is exiting, then exit. IExternalConnection doesn't provide a solution there: you still need to design your API in a way that clients can be notified when the application is exiting.

So the other alternative I didn't mention is to forget about detecting how many clients are connected, and just design your API so that either the client can notify the server that it should stick around, or the server can notify the client that it is exiting (and the client should disconnect).


I've posted a function which might be useful in connection with ObjRegisterActive: GetActiveObjects().
Coco
Posts: 771
Joined: 29 Sep 2013, 20:37
GitHub: cocobelgica

Re: ObjRegisterActive

22 Feb 2015, 03:23

Thanks for the explanation lexikos.
I think it's more appropriate to just notify clients that the application is exiting
I decided to choose this route. However, I wanted to trigger this via __Delete and I have a question regarding reference count w/ regards to registered object(s). Below is a demo code, when calling ObjRegisterActive the reference count is incremented by 2, so from 1 it becomes 3. Then after launching the client script, when a reference is retrieved(via ComObjActive), 3 becomes 6. Why is this??:

Code: Select all

#Persistent

DllCall("AllocConsole")
conout := FileOpen("CONOUT$", 1|4)

GUID := "{9359E8E4-3E3C-427F-81D8-5E1314B39E73}"
obj := {Test:Func("Test"), Status:1}

; REFCOUNT == 1
ObjAddRef(&obj)
conout.WriteLine("RefCount: " . ObjRelease(&obj)), conout.Read(0)

ObjRegisterActive(obj, GUID) ; register the object

; REFCOUNT == 3 ??
ObjAddRef(&obj)
conout.WriteLine("RefCount: " . ObjRelease(&obj)), conout.Read(0)

code := Format("
(
obj := ComObjActive({1}{2}{1})
obj.Status := 0
obj.Test()
return
)", Chr(34), GUID)

cmd := Format("{1}{2}{1} *", Chr(34), A_AhkPath)
exec := ComObjCreate("WScript.Shell").Exec(cmd)
exec.StdIn.Write(code), exec.StdIn.Close()
while obj.Status && (exec.Status == 0)
	Sleep 10

; REFCOUNT == 6 ??
ObjAddRef(&obj)
conout.WriteLine("RefCount: " . ObjRelease(&obj)), conout.Read(0)

ObjRegisterActive(obj, "") ; revoke

conout.Write("`nPress 'Enter' to quit.`n>>> "), conout.Read(0)
KeyWait Enter, D
DllCall("FreeConsole")
ExitApp

Test(this)
{
	ObjAddRef(&this)
	FileOpen("CONOUT$", 1|4).WriteLine(A_ThisFunc . "->RefCount: " . ObjRelease(&this))
}
I've posted a function which might be useful in connection with ObjRegisterActive: GetActiveObjects().
Thanks, very useful :)
lexikos
Posts: 6201
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: ObjRegisterActive

22 Feb 2015, 04:30

ObjRegisterActive stores a reference to the object as a key in its cookieJar. RegisterActiveObject calls AddRef, since we used the ACTIVEOBJECT_STRONG flag (0). Hence the reference count increasing by 2.

When a remote script retrieves a reference to the object (via proxy), it's all handled by the (OS-provided) COM libraries. If the reference count increases by 3, obviously AddRef was called 3 times, probably because the COM libraries store 3 additional references. I don't think that would be documented anywhere, so basically, don't rely on the reference count.

Also,
This value should be used only for debugging purposes.
Coco
Posts: 771
Joined: 29 Sep 2013, 20:37
GitHub: cocobelgica

Re: ObjRegisterActive

22 Feb 2015, 04:48

When a remote script retrieves a reference to the object (via proxy), it's all handled by the (OS-provided) COM libraries. If the reference count increases by 3, obviously AddRef was called 3 times, probably because the COM libraries store 3 additional references. I don't think that would be documented anywhere, so basically, don't rely on the reference count.
I see, so this is the unpredictable part. I guess I'd have to bind the RevokeActiveObject call to another object(one which actually gets __Deleted on exit). Thanks for the clarification.
tmplinshi
Posts: 1278
Joined: 01 Oct 2013, 14:57

Re: ObjRegisterActive

08 Apr 2015, 08:59

Is there a way to use "human-readable Prog ID" instead of CLSID? For example x := ComObjActive("ahk.CLSID")
User avatar
maestrith
Posts: 735
Joined: 16 Oct 2013, 13:52

Re: ObjRegisterActive

08 Apr 2015, 09:02

Code: Select all

RegisterIDs(CLSID,APPID){
	RegWrite,REG_SZ,HKCU,Software\Classes\%APPID%,,%APPID%
	RegWrite,REG_SZ,HKCU,Software\Classes\%APPID%\CLSID,,%CLSID%
	RegWrite,REG_SZ,HKCU,Software\Classes\CLSID\%CLSID%,,%APPID%
}
clsid:="Your_Class_Name"
appid:={AppIdString}
AHK Studio OSD GUI Creator
Donations
Discord
All code is done on a Windows 10 PC Running x64 and AutoHotkey x32
tmplinshi
Posts: 1278
Joined: 01 Oct 2013, 14:57

Re: ObjRegisterActive

08 Apr 2015, 09:10

Thank you very much, maestrith. Works great!
User avatar
maestrith
Posts: 735
Joined: 16 Oct 2013, 13:52

Re: ObjRegisterActive

08 Apr 2015, 09:10

Thank you very much, maestrith. Works great!
np :)
AHK Studio OSD GUI Creator
Donations
Discord
All code is done on a Windows 10 PC Running x64 and AutoHotkey x32
sancarn
Posts: 212
Joined: 01 Mar 2016, 14:52

Re: ObjRegisterActive

19 Oct 2017, 04:12

Can you register events in your class such that clients can connect, for instance with ComObjConnect(MyObject, "something_")? If so how would you go about implementing them?

Edit: I didn't realise how easy it was to pass a callback function directly to ahk (even from other languages). The question is still relevant though, even if there are other far easier ways to accomplish the same thing, so I will leave it :)

Return to “Scripts and Functions”

Who is online

Users browsing this forum: kris_kaminski, swagfag, TAC109 and 57 guests