recreating AHK v2's ComCall function

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

recreating AHK v2's ComCall function

30 Oct 2019, 13:30

ComCall - Syntax & Usage | AutoHotkey v2
https://lexikos.github.io/v2/docs/commands/ComCall.htm

Like so? Any issues/improvements? Cheers.

Code: Select all

ComCall(Index, ComObject, Params*)
{
	local Ptr, Result
	Ptr := IsObject(ComObject) ? ComObject.Ptr : ComObject
	Result := DllCall(NumGet(NumGet(Ptr+0)+Index*A_PtrSize), "Ptr",Ptr, Params*)
	if !(Params.Length() & 1) ;if even number (i.e. no return type)
	&& (Result < 0)
		throw Exception("", -1, Result)
	return Result
}
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: recreating AHK v2's ComCall function

31 Oct 2019, 03:09

This will not work with output variables (there is no really good solution for that). V1 comobjs have no ptr property if I'm not mistaken, I guess you could use comobjvalue instead. I would wrap the call to dllcall in try-catch-throw to catch misc. errors which are otherwise silent in v1, but throws in v2. Minor, comcall will throw if you explicitly specify the return type to hresult and it returns an error code. Also note that cdecl is allowed for comcall.

Cheers.
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: recreating AHK v2's ComCall function

11 Nov 2019, 19:22

This will not work with output variables (there is no really good solution for that).
What are you referring to?
V1 comobjs have no ptr property if I'm not mistaken, I guess you could use comobjvalue instead.
Yes, I meant to relook at this. So ...

Code: Select all

	Ptr := IsObject(ComObject) ? ComObject.Ptr : ComObject ;current
	Ptr := IsObject(ComObject) ? ComObjValue(ComObject) : ComObject ;your proposal?
	Ptr := IsObject(ComObject) ? ComObject(ComObject) : ComObject ;best?
The documentation suggests that ComObject would be better than ComObjValue. Do you agree?

Links:
ComObjValue - Syntax & Usage | AutoHotkey v2
https://lexikos.github.io/v2/docs/commands/ComObjValue.htm
ComObject - Syntax & Usage | AutoHotkey v2
https://lexikos.github.io/v2/docs/commands/ComObject.htm
I would wrap the call to dllcall in try-catch-throw to catch misc. errors which are otherwise silent in v1, but throws in v2.
Do you have any examples of code that would cause such errors.
So I can test it in AHK v2, and copy the error message text.
Minor, comcall will throw if you explicitly specify the return type to hresult and it returns an error code.
I didn't consider that a user would want to specify HRESULT as the return type in AHK v1.
Here's a fix:

Code: Select all

ComCall(Index, ComObject, Params*)
{
	local IsHResult, Ptr, Result
	Ptr := IsObject(ComObject) ? ComObject.Ptr : ComObject

	IsHResult := !(Params.Length() & 1) ;if even number, return type is HRESULT
	if (Params.Length() & 1)
	&& (Params[Params.Length()] = "HRESULT")
		Params.Pop(), IsHResult := 1

	Result := DllCall(NumGet(NumGet(Ptr+0)+Index*A_PtrSize), "Ptr",Ptr, Params*)

	if IsHResult && (Result < 0)
		throw Exception("", -1, Result)
	return Result
}
Does this address the problem you mentioned?
Also note that cdecl is allowed for comcall.
Cdecl would appear in the return type.
The return type is omitted or is the last parameter in Params.
Is there some significance re. Cdecl I'm missing?

Thanks for your comments.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: recreating AHK v2's ComCall function

12 Nov 2019, 02:14

jeeswg wrote:
11 Nov 2019, 19:22
What are you referring to?
Output variables, eg, asterix suffixed and str params.
The documentation suggests that ComObject would be better than ComObjValue. Do you agree?
No.
Do you have any examples of code that would cause such errors.
documentation, :arrow: Dllcall Error Handling
Does this address the problem you mentioned?
At least not if Cdecl is present.

Cheers.
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: recreating AHK v2's ComCall function

12 Nov 2019, 17:31

'OUTPUT' VARIABLES

- Ah yes, in my testing, I hadn't come across the problem of 'output' variables yet. (Do you have another name for this?)
- A workaround is to use 'Ptr' and avoid special types such as 'Int*'/'Str'.
- (Otherwise AHK v1 could be given either: ComCall (officially), or, some form of combined ByRef/variadic parameter handling.)

CDECL

- So are you saying to handle 'Cdecl'? E.g. 'Cdecl HRESULT'?
- The documentation says 'Cdecl HRESULT' is invalid, that you can't have a prefix/suffix with HRESULT:
DllCall - Syntax & Usage | AutoHotkey v2
https://lexikos.github.io/v2/docs/commands/DllCall.htm
A 32-bit integer. This is generally used with COM functions and is valid only as a return type without any prefix or suffix.
- Or did you mean something else re. Cdecl?

POINTERS

- In general, for COM classes that implement IDispatch, you use ComObjCreate to give you a COM object, with an easy-to-use 'obj.property'/'obj.method()' syntax.
- For COM classes that don't implement IDispatch, you use ComObjQuery to give you a pointer, and you use that pointer with DllCall (or the new handier ComCall) to call the methods in a vtable by their addresses.
- This is covered by maul.esel:
[advanced] Using raw COM interfaces in AHK - Tutorials - AutoHotkey Community
https://autohotkey.com/board/topic/81077-advanced-using-raw-com-interfaces-in-ahk/
COM interface tutorial
https://maul-esel.github.io/tutorials/COM-Interfaces.html
- Also:
IDispatch - Wikipedia
https://en.wikipedia.org/wiki/IDispatch
- I haven't ever wanted to get a pointer from an existing COM object, and call some vtable methods.
- Have you ever done this? Do you have any examples? Or do you know of any examples on the forum?
- Once I've established that, I can consider how best to get the pointer from a COM object.

- Cheers.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
JnLlnd
Posts: 490
Joined: 29 Sep 2013, 21:29
Location: Montreal, Quebec, Canada
Contact:

Re: recreating AHK v2's ComCall function

27 Oct 2022, 23:41

Hi @jeeswg

Was there any progress converting ComCall to v1.1? I tried using your 2nd version above but no success. I'm not very familiar with v2 new functions. I need to convert this v2 code from lexikos. It is using the v2 ComCall function.

Here is my best try... Not working, yet.

Code: Select all

#requires AutoHotkey v1.1
#SingleInstance,Force
#NoEnv

return

^1::
    tab := GetActiveExplorerTab()
    MsgBox, % "tab.Document: " . tab.Document ; should be "ShellFolderView"
return

GetActiveExplorerTab(hwnd := "")
{
	if (hwnd = "")
		hwnd := WinExist("A")
    activeTab := 0
	ControlGet, activeTab, Hwnd, , ShellTabWindowClass1, ahk_id %hwnd% ; File Explorer (Windows 11)
	if !StrLen(activeTab)
		ControlGet, activeTab, Hwnd, , TabWindowClass1, ahk_id %hwnd% ; IE
	MsgBox, Active tab: %activeTab%
	For w in ComObjCreate("Shell.Application").Windows {
        if w.hwnd != hwnd
            continue
        if activeTab { ; The window has tabs, so make sure this is the right one.
            static IID_IShellBrowser := "{000214E2-0000-0000-C000-000000000046}"
            shellBrowser := ComObjQuery(w, IID_IShellBrowser, IID_IShellBrowser)
            ComCall(3, shellBrowser, "uint*", &thisTab)
			MsgBox, thisTab: %thisTab% /vs/ activeTab: %activeTab%
            if thisTab != activeTab
                continue
        }
        return w
    }
}

; https://www.autohotkey.com/boards/viewtopic.php?style=17&p=300846#p300846
ComCall(Index, ComObject, Params*)
{
	local IsHResult, Ptr, Result
	Ptr := IsObject(ComObject) ? ComObject.Ptr : ComObject

	IsHResult := !(Params.Length() & 1) ;if even number, return type is HRESULT
	if (Params.Length() & 1)
	&& (Params[Params.Length()] = "HRESULT")
		Params.Pop(), IsHResult := 1

	Result := DllCall(NumGet(NumGet(Ptr+0)+Index*A_PtrSize), "Ptr",Ptr, Params*)

	if IsHResult && (Result < 0)
		throw Exception("", -1, Result)
	return Result
}

:thumbup: Author of freeware Quick Access Popup, the powerful Windows folders, apps and documents launcher!
:P Now working on Quick Clipboard Editor
:ugeek: The Automator's Courses on AutoHotkey
User avatar
JnLlnd
Posts: 490
Joined: 29 Sep 2013, 21:29
Location: Montreal, Quebec, Canada
Contact:

Re: recreating AHK v2's ComCall function

28 Oct 2022, 17:46

> malcev

Oh...

> Compare

I reviewed the differences in DllCall between v1.1 and v2 and I think the code in the previous post is OK regarding the syntax. What I'm not sure (and I'm far from mastering this area of expertise...) is if the various commands included in the ComCall() function converted to v1.1 by jeeswg executes the correct steps to mimic the v2 ComCall...
:thumbup: Author of freeware Quick Access Popup, the powerful Windows folders, apps and documents launcher!
:P Now working on Quick Clipboard Editor
:ugeek: The Automator's Courses on AutoHotkey
lexikos
Posts: 9664
Joined: 30 Sep 2013, 04:07
Contact:

Re: recreating AHK v2's ComCall function

28 Oct 2022, 19:24

Do not attempt to replicate ComCall in v1; just convert the ComCall calls to DllCall. I think that's what malcev was suggesting by pointing out the TaskbarList examples (the DllCall version also exists in the v2 documentation, for comparison).

The differences between ComCall and DllCall in v2 are:
  • Expansion/evaluation of ComCall's first two parameters into a function address and pointer value.
  • The default return type is HRESULT for ComCall and int for DllCall.
Since DllCall has different behaviour in v1 and v2, replicating the exact behaviour of ComCall in v1 would take more work. It isn't possible due to other differences in the language (i.e. you can't pass a variable ByRef variadically in v1). In general, any generalized functions that wrap DllCall are somewhat limited in v1.

if thisTab != activeTab
You'll need parentheses here.
&thisTab
This creates a reference to the variable in v2, but returns a string address in v1. You'll need to remove the &.

Also note that ComObjQuery returns a pointer value, not a ComObj, in v1. The pointer value can be used directly with NumGet and DllCall, but you must ObjRelease it afterward.
User avatar
JnLlnd
Posts: 490
Joined: 29 Sep 2013, 21:29
Location: Montreal, Quebec, Canada
Contact:

Re: recreating AHK v2's ComCall function

28 Oct 2022, 19:33

Thank you lexikos. I'll work on it.
:thumbup: Author of freeware Quick Access Popup, the powerful Windows folders, apps and documents launcher!
:P Now working on Quick Clipboard Editor
:ugeek: The Automator's Courses on AutoHotkey
User avatar
JnLlnd
Posts: 490
Joined: 29 Sep 2013, 21:29
Location: Montreal, Quebec, Canada
Contact:

Re: recreating AHK v2's ComCall function

28 Oct 2022, 20:06

I did not have to work on it. I found in my notifications that @ntepa just did it :-)
viewtopic.php?p=488620#p488620
:thumbup: Author of freeware Quick Access Popup, the powerful Windows folders, apps and documents launcher!
:P Now working on Quick Clipboard Editor
:ugeek: The Automator's Courses on AutoHotkey
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: recreating AHK v2's ComCall function

29 Oct 2022, 03:46

jeeswg wrote: The documentation says 'Cdecl HRESULT' is invalid, that you can't have a prefix/suffix with HRESULT:
Cdecl isn't used as a prefix or suffix. Prefix/suffix refers to the U prefix and p/* suffixes. 'Cdecl HRESULT' is valid.

Cheers.

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: Bing [Bot], Skrell and 169 guests