Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

COM Standard Library


  • Please log in to reply
669 replies to this topic
Sean
  • Members
  • 2462 posts
  • Last active: Feb 07 2012 04:00 AM
  • Joined: 12 Feb 2007

- These examples make reference to CoHelper.ahk. But you said (I don't remember where) that CoHelper and COM.ahk are not fully compatibles. So what is incompatible ? Are the examples in ADO COM (for example) can be "prefixed" with "#Include COM.ahk" rather than "#Include CoHelper.ahk"with no more modification than to prefix each COM function with "COM_" ?

Slightly incompatible means mostly compatible. COM.ahk is a bit more flexible than CoHelper.ahk. E.g., can't use CreateObject("Excel.Application") with CoHelper.ahk, but can use with COM.ahk. So, COM_ActiveXObject() becomes redundant, but I leave it as a simplified version of COM_CreateObject() and for the compatibility with CoHelper.ahk. Anyway, the difference is negligible, and yes I think you can use the above scripts just prefixing COM_.

For the documentation, I'd like to know, if it is usefull, the difference between COM_Invoke() and COM_Invoke_(), that I seen in the COM source, if the second one can be called directly and is usefull to use ...
For COM_CreateObject and COM_GetObject, I know what they do. They must work (almost) in the same way as the VBS/VBA equivalent. But I don't know the usage/utility of COM_ActiveXObject. Idem for COM_ConnectObject.

For these read the posts here:
<!-- m -->http://www.autohotke...topic20979.html<!-- m -->

Washboard
  • Guests
  • Last active:
  • Joined: --
Hi Sean,

Thanks for your answers. The things are clearer for me, now (Differences between CoHelper.ahk and COM.ahk, between Invoke() and Invoke_(), ...
I'll read again the posts to understand well how Invoke works and I'll try to use COM.ahk.
Thanks again for your work ... and your patience...

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
There's an error in COM_CreateIDispatch() that prevents COM_ConnectObject() from working.
NumPut(RegisterCallback("[color=red]DispInterface[/color]","",A_LoopField,A_Index-1),IDispatch,4*(A_Index-1))
should be
NumPut(RegisterCallback("[color=green]COM_[/color]DispInterface","",A_LoopField,A_Index-1),IDispatch,4*(A_Index-1))
:)

Sean
  • Members
  • 2462 posts
  • Last active: Feb 07 2012 04:00 AM
  • Joined: 12 Feb 2007

There's an error in COM_CreateIDispatch() that prevents COM_ConnectObject() from working.

It's fixed now. Thanks.

Sean
  • Members
  • 2462 posts
  • Last active: Feb 07 2012 04:00 AM
  • Joined: 12 Feb 2007
I added a new function
COM_Enumerate(penum, ByRef Result)    ; [color=red]0 returned on success![/color]
Each enumerated result will be assigned to the ByRef parameter Result.

And, introduced a Global variable _hResult_ which will store the hResult of the Invoke.

Example:
Namespace := "root\cimv2"
Class     := "Win32_Process"
Query     := "SELECT * FROM " . Class

COM_CoInitialize()
psvc := COM_GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\" . Namespace)
pset := COM_Invoke(psvc, "ExecQuery", Query)
penm := COM_Invoke(pset, "_NewEnum")
Loop
	If	[color=red]COM_Enumerate[/color](penm, pobj) = [color=red]0[/color]
		MsgBox, % COM_Invoke(pobj, "GetObjectText_") . SubStr(COM_Release(pobj),1,0)
	Else	Break
COM_Release(penm)
COM_Release(pset)
COM_Release(psvc)
COM_CoUninitialize()


Joy2DWorld
  • Members
  • 562 posts
  • Last active: Jun 30 2014 07:48 PM
  • Joined: 04 Dec 2006

introduced a Global variable _hResult_ which will store the hResult of the Invoke.


while am not a big fan of the "LIBRARY"_function for truly fundamental and basic functions such as the "Invoke()", "CoInitialize()", etc.,

if we're using that structure, probably a good idea, (if this tiny suggestion is helpful) to prefix all of your global Vars with the library prefix, eg:

COM_hResult



(can see this for example in the dde wrapper which needs the globals as basis of function's structural theory)

you can even include a ghost function "Com_hResult()" which returns that global var, so those who might not understand/expect a global var, can chug smoothly along.. ALSO that way the 'item' shows up as a function in your library, so those looking to understand what they can do/get with it, can find it that way...etc.

anyhow, just a suggestion.
Joyce Jamce

Sean
  • Members
  • 2462 posts
  • Last active: Feb 07 2012 04:00 AM
  • Joined: 12 Feb 2007

if we're using that structure, probably a good idea, (if this tiny suggestion is helpful) to prefix all of your global Vars with the library prefix, eg:
COM_hResult

That's a good idea. I'll do it, possibly silently, if there is no objection to it or other suggestion of the names like simply COM_Result for some time.
BTW, probably I'll never use it in the scripts explicitly, it's mainly for debugging purpose.

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
I have a few suggestions regarding COM_ConnectObject (well, the Invoke implementation of COM_DispInterface):
[*:2fjvvxms]Since no parameter count is specified in RegisterCallback, it could potentially create a callback which accepts the wrong number of parameters. This probably doesn't matter, since AutoHotkey automatically restores the stack, but I think it would be prudent to use CDecl (which is caller cleanup vs. callee cleanup.)
[*:2fjvvxms]Since DHTML event handlers do not receive parameters directly, we need a pointer to the DHTML object to retrieve event info. It would generally be useful to know which object raised the event, so I suggest it be added as a parameter (which would be optional.)
[*:2fjvvxms]It should be safe to use the Fast mode of RegisterCallback, since the COM_DispInterface callback would have already started a new thread.Proposed Invoke implementation:
DllCall(NumGet(NumGet(NumGet(this+8))+28),"Uint",NumGet(this+8),"Uint",prm1,"UintP",pname,"Uint",1,"UintP",0), VarSetCapacity(sfn,63), DllCall("wsprintfA","str",sfn,"str","%s%S","Uint",this+40,"Uint",pname,"Cdecl"), COM_SysFreeString(pname), (pfn:=RegisterCallback(sfn[color=olive],"F C"[/color])) ? (hResult:=DllCall(pfn,"Uint",prm5[color=green],"Uint",NumGet(this+12)[/color][color=olive],"CDecl"[/color])) . DllCall("GlobalFree", "Uint", pfn) : (hResult:=0x80020003)
(That's actually one line, as in the current implementation.)

Sean
  • Members
  • 2462 posts
  • Last active: Feb 07 2012 04:00 AM
  • Joined: 12 Feb 2007

Since no parameter count is specified in RegisterCallback, it could potentially create a callback which accepts the wrong number of parameters. This probably doesn't matter, since AutoHotkey automatically restores the stack, but I think it would be prudent to use CDecl (which is caller cleanup vs. callee cleanup.)

Although Cdecl is more flexible in this regard, nothing comes in free. Cdecl needs an overhead for this. So, I urge to always match the number of parameters. As a matter of fact, this was the reason why I ditched all other parameters leaving only the parameter of pDispParams.

Since DHTML event handlers do not receive parameters directly, we need a pointer to the DHTML object to retrieve event info. It would generally be useful to know which object raised the event, so I suggest it be added as a parameter (which would be optional.)

Really? Which event? I'm a bit surprised to hear this, especially regarding MS's own event interface. Usually/naturally (MS's) interfaces have been found well-designed as far as I'm concerned, I mean if the corresponding interface pointer to the event is needed, it's usually specified as the (first) parameter (in DispParams) of the fired event. If not the case, I'll definitely add/restore it.

It should be safe to use the Fast mode of RegisterCallback, since the COM_DispInterface callback would have already started a new thread.

I don't think that can be true if I interpreted correctly your term of new thread. It would violate the Threading Model rule of COM.
Anyway, you have to understand this: I have to play in the safest way with the COM library which is not used by myself only.
If Fast mode really gains substantial performance over Slow mode, then it should be re-considered however.

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006

So, I urge to always match the number of parameters.

My point is that COM.ahk does not enforce this.

Really? Which event?

As I said, DHTML events. ALL of them. DispParams contains zero params. "Although event handlers in the DHTML Object Model do not receive parameters directly, a handler can query an event object for data." (The 'event' property of the window object, which is always accessible to JavaScript...)

it's usually specified as the (first) parameter (in DispParams) of the fired event.

I noticed this is true for the WebBrowser control, but it is not for DHTML. NumGet( pDispParams+8 ) retrieves the argument count, right? (This returns zero.)

I don't think that can be true if I interpreted correctly your term of new thread.

A "thread" in AutoHotkey is basically a snapshot of global script properties; anything that the manual says each thread retains its own settings for. AutoHotkey threads have nothing to do with Windows' concept of threads or the Threading Model of COM.

Cdecl needs an overhead for this.

I don't really know much about this, but exactly how is there overhead? With (and without :?) it, DllCall does stack cleanup. Without it, the callback stub does cleanup. What's the difference?

Actually, I'm not sure that CDecl has any real effect. It seems AutoHotkey restores the stack the same way regardless:
// Even if an exception occurred (perhaps due to the callee having been passed a bad pointer),
	// attempt to restore the stack to prevent making things even worse.
	_asm
	{
		mov esp_end, esp        // See below.
		mov esp, esp_start      //
		// For DC_CALL_STD functions (since they pop their own arguments off the stack):
		// Since the stack grows downward in memory, if the value of esp after the call is less than
		// that before the call's args were pushed onto the stack, there are still items left over on
		// the stack, meaning that too many args (or an arg too large) were passed to the callee.
		// Conversely, if esp is now greater that it should be, too many args were popped off the
		// stack by the callee, meaning that too few args were provided to it.  In either case,
		// and even for CDECL, the following line restores esp to what it was before we pushed the
		// function's args onto the stack, which in the case of DC_CALL_STD helps prevent crashes
		// due too too many or to few args having been passed.
		mov dwEAX, eax          // Save eax/edx registers
		mov dwEDX, edx
	}

	// Possibly adjust stack and read return values.
	// The following is commented out because the stack (esp) is restored above, for both CDECL and STD.
	//if (aFlags & DC_CALL_CDECL)
	//	_asm add esp, our_stack_size    // CDECL requires us to restore the stack after the call.
The difference is that without CDecl, the callback stub also does stack correction, and DllCall treats different stack sizes as an error. (It sets ErrorLevel.)

Sean
  • Members
  • 2462 posts
  • Last active: Feb 07 2012 04:00 AM
  • Joined: 12 Feb 2007
OK, I think I should've mentioned it from the start. The development of COM(.ahk) in public by myself has been finished. There will be only bug fixing, but no more enhancement, possibly except for COM Error Handling if I ever enter into it and it turns out to be suitable to be used inside from a mere script. However, I don't think it will happen near soon as there are too many interesting subjects around. I think my interest is entering into the rather sensitive area, so probably even non-COM works will be kept in private mostly, I suppose. Anyway, my point here is that I'd like to avoid being constantly detracted by the finished work, especially by a hard-to-track error report unless the user provides sufficient infos for it.

So, I urge to always match the number of parameters.

My point is that COM.ahk does not enforce this.

If I enforce it, i.e. specifying the number of parameters, it would really stop working without any explicit error message, when using mismatched number of parameters with the target AHK's functions. Without it, the functions may be still working even if the (intended) number of parameters is different. The only problem would be the stack mis-adjustment, but it would be corrected automatically thanks to the extra work of AHK/Chris.

As I said, DHTML events. ALL of them. DispParams contains zero params.

OK, you meant the individual events in DHTML, not the event interface. No problem with adding it (probably I'll add psink/this, not NumGet(this+12)). However, I'd like to ask first if it's not feasible using Global variables with these, as if I add it I have to correct all of the event-related examples posted so far accordingly.

A "thread" in AutoHotkey is basically a snapshot of global script properties; anything that the manual says each thread retains its own settings for.

I see, you meant AHK's threads. I would say then there would be nothing to prevent from using Fast, at least apparently. I even added Critical in COM_DispInterface() for this. But, this perfectly seeming condition was my concern. If an error ever occurs around this Fast/Slow mode in this near-ideal condition, it will be really hard to track down, especially when it's occurred in other user's machine. In fact, there is no 100% guarantee that it'll never happen, especially with COM. We don't really know what COM manager is doing behind the scene. So, unless I'm convinced that Fast mode improves the performance significantly over Slow mode, probably I may not add it. But, this doesn't prevent you from using it on your own, of course.

DllCall does stack cleanup.

This is what I meant by overhead: the caller should keep an eye on how many bytes are pushed on the stack, then clean them after the call is returned with Cdecl. This after job isn't necessary with Stdcall, AFAIK.

Actually, I'm not sure that CDecl has any real effect. It seems AutoHotkey restores the stack the same way regardless:

I already knew it. That wasn't the point. I talked about things in principle. If taking into the extra safe-guard of AHK, this discussion wasn't necessary from the beginning. Cdecl/Stdcall won't matter with AHK's DllCall(), at least most times if not all.

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006

The only problem would be the stack mis-adjustment, but it would be corrected automatically thanks to the extra work of AHK/Chris.

What if the callback pulls too much (too many parameters) off the stack? I suppose maybe it would just get garbage data, and no real damage would be done.

However, I'd like to ask first if it's not feasible using Global variables with these,

No. I originally thought to add the parameter when I was trying to connect multiple objects of the same type (window objects). I could store the pointers in a list, but I'd have no way to determine which one raised the event.

as if I add it I have to correct all of the event-related examples posted so far accordingly.

You could easily make the parameter optional. No updating would be necessary.

I see, you meant AHK's threads.

Of course. That is what the callback Fast mode relates to.

In fact, there is no 100% guarantee that it'll never happen, especially with COM.

DispInterface does not have Fast mode, therefore it will always create a new ahk thread (i.e. back up all global settings, and restore them when DispInterface returns.)

It seems AutoHotkey restores the stack the same way regardless:

I already knew it. That wasn't the point.

If you already knew it, you should've alread known that Cdecl does not add overhead. In fact, Cdecl for RegisterCallback means less work.

Cdecl needs an overhead for this.


But, this doesn't prevent you from using it on your own, of course.

The entire reason I'm posting is that COM is a standard library scripts, so there'd be little point in me using a modified version in my script, and I'd like to avoid duplicating any COM_* functions...


TBH, I don't mind if you don't make the changes immediately, or ever. I'll probably use my own (singular) event handling functions, anyway.

Sean
  • Members
  • 2462 posts
  • Last active: Feb 07 2012 04:00 AM
  • Joined: 12 Feb 2007

What if the callback pulls too much (too many parameters) off the stack?

Pop-up doesn't necessarily mean (real) data clean-up/altering, usually no altering, unless done intentionally, as that would reduce the speed.
BTW, it may not work if an user ever used the garbage parameters inside the function, which I thought was rather unlikely. My point was not that it's 100% working, but that it's not 100% non-working.
Hmm... possibly 100% non-working may be the better option contrary to my original thought.

No. I originally thought to add the parameter when I was trying to connect multiple objects of the same type (window objects). I could store the pointers in a list, but I'd have no way to determine which one raised the event.

OK, I'll restore this parameter, which used to be present anyway.

DispInterface does not have Fast mode, therefore it will always create a new ahk thread (i.e. back up all global settings, and restore them when DispInterface returns.)

You seem to miss my point. Of course if all calls are made through DispInterface() there will be no problem. However, COM also uses Windows Messages internally. Although I think they are PostMessages, does this fact guarantee there will be no interuption at all?
Anyway, as I didn't test Fast mode myself that much, I need substantial/convincing evidences from other users to change it.

If you already knew it, you should've alread known that Cdecl does not add overhead. In fact, Cdecl for RegisterCallback means less work.

Yes, I realized that yesterday: Pop-up vs. (mere) Retrieve.
This extra work of the safeguard made the thing appear differently.
The question is: should I exploit this extra safe-guard? Is the safe-guard an official feature or not?
And remember my official position is still "always use the correct number of parameters".

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006

DispInterface does not have Fast mode, therefore it will always create a new ahk thread (i.e. back up all global settings, and restore them when DispInterface returns.)

You seem to miss my point. Of course if all calls are made through DispInterface() there will be no problem.

Where else could calls come from?

The default/slow mode causes the function to start off fresh with the default values for settings such as SendMode and DetectHiddenWindows. These defaults can be changed in the auto-execute section.

By contrast, the fast mode inherits global settings from whichever thread happens to be running at the time the function is called. Furthermore, any changes the function makes to global settings (including ErrorLevel and the last-found window) will go into effect for the current thread. Consequently, the fast mode should be used only when it is known exactly which thread(s) the function will be called from.

We know that the callback registered by DispInterface will only ever be called by the DispInterface thread. Since DispInterface cannot continue until the event handler returns, it is conceptually the same thread; much like calling a function normally.

Although I think they are PostMessages, does this fact guarantee there will be no interuption at all?

Whether or not the event handler uses Fast mode has no impact on whether it may be interrupted, or whether whatever interrupts it starts a new thread.


Non-fast mode is essentially this:
[*:h8320mbd]Make copies of ErrorLevel, A_WorkingDir and global_struct g (which is 200 bytes, and holds settings like key delay and Last Found Window.)
[*:h8320mbd]Copy the default global settings to g (again, 200 bytes.)
[*:h8320mbd]Call the script function.
[*:h8320mbd]Restore ErrorLevel, A_WorkingDir and g (another 200 bytes...)

The question is: should I exploit this extra safe-guard? Is the safe-guard an official feature or not?

That sounds like an argument for Cdecl, not against. I think the stack correction in stdcall mode is not documented, whereas the behaviour of Cdecl is. I would say "Without Cdecl, it is a safe-guard. With Cdecl, it is normal behaviour."

Sean
  • Members
  • 2462 posts
  • Last active: Feb 07 2012 04:00 AM
  • Joined: 12 Feb 2007

Where else could calls come from?

What I'm concerned is the interruption of the thread, most likely through (Send)Messages.
I'd like to emphasize again:
The real question is not about why should not use the fast mode, but about why should use the fast mode in spite of its less-safe nature.

And, if there is an error report ever, who will have the responsibility of analyzing it? What if it's really caused by this Fast/Slow mode? Are you gonna take it? The real problem is we can't know a priori this fact until we succeed to pin down the cause. Naturally I'll take the safest bet as the one who is likely to have to take the responsibility.

The solution is simple: just show me the evidence/benefit of the fast mode. Only arguing around the theory usually lead to nowhere. Looks like you have the scroll-bar case in mind. If so, does the fast mode solve the issue while the slow mode does not?

I would say "Without Cdecl, it is a safe-guard. With Cdecl, it is normal behaviour."

Yes, and it need not (and should not IMO if performance is the only concern) be done with Stdcall. And, I never relied on it/stack-correction officially. So, I've been always trying to go as if there is no safe-guard at all, but you always mixed it into the discussion.