Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate

Allowing Function Pointer in DllCall


  • Please log in to reply
43 replies to this topic
Sean
  • Guests
  • Last active:
  • Joined: --

It is a known problem, still mysterious for me, but the problem might be in Windows rather than in AHK (just an hypothesis).


Thanks for the explanation. It made me embarrassed at first.

I checked on a couple of WinXP Pro SP2 computers at work and both have it in System32.


It was missing in my system, so I had to extract it from the non-SP XP CD.

majkinetor
  • Moderators
  • 4512 posts
  • Last active: May 20 2019 07:41 AM
  • Joined: 24 May 2006
Hm... this is very intersting topic for me. I didn't know com functions can be called this way....

Need some more information from you:

DllCall("msjava\call", "Uint", DecodeInteger(pv + 4*5), "Uint", ppv, "Uint", &wFile, "Uint", 0) 
DllCall("msjava\call", "Uint", DecodeInteger(pv + 4*3), "Uint", ppv, "Uint", 7) 
DllCall("msjava\call", "Uint", DecodeInteger(pv + 4*2), "Uint", ppv)

I see that first func you call is IActiveDesktop::SetWallpaper. But how did you find 4*5 offset ?
The last one is ApplyChanges i guess... the middle is unknown

I watched definition of interface here but I didn't connected the third and second offset

Anyway, I understand now what you want, at it would be really convinient in this sceneraio. This can really lead to COM solution with AHK if you can explain me how did you find those offsets :D


BTW, don't expect that anyone will read your code if you don't present it the correct way. It happend that I interested much in topic you started so I was willing to investigate. I seriously dobut that anybody can read your topic and understand what you want, or what you use...
Posted Image

Sean
  • Guests
  • Last active:
  • Joined: --

I see that first func you call is IActiveDesktop::SetWallpaper. But how did you find 4*5 offset ?
The last one is ApplyChanges i guess... the middle is unknown

It's in ShlObj.h. You may search there by Active Desktop in this case.
BTW, you seemed to miss that they should be 0-based.
So, the middle one is ApplyChanges and the last one is Release.

here

I know this site. As a matter of fact, my friend googled and found this site after I told him about my idea on ComCall via DllCall. I found it handy, but I believe Platform SDK is the ultimate place to go.

Anyway, I understand now what you want, at it would be really convinient in this sceneraio. This can really lead to COM solution with AHK if you can explain me how did you find those offsets :D

Good news. Just keep in mind that it may require a lot of researches and experiments sometimes, especially regarding the ones intended for VB scripts, they are so opaque and obscure.

BTW, don't expect that anyone will read your code if you don't present it the correct way. It happend that I interested much in topic you started so I was willing to investigate. I seriously dobut that anybody can read your topic and understand what you want, or what you use...

To be honest, I thought just one person would be enough. Now I suppose I'm relieved.

majkinetor
  • Moderators
  • 4512 posts
  • Last active: May 20 2019 07:41 AM
  • Joined: 24 May 2006
I didn't count IUnknown functions for some reason.. :? :roll: :oops:

Ok, I think Chris should take this request into account and as it is very easy to implement to make it high priority. Then we can experiment with COM and create some nice wrappers without msjava.dll or equivalent
Posted Image

Chris
  • Administrators
  • 10727 posts
  • Last active:
  • Joined: 02 Mar 2004
I've applied the suggestion. (Edit: It's now in the current release.)

Here's an example based on yours:
if not hmodule := DllCall("GetModuleHandle", str, "user32")
	MsgBox !hmodule
else
{
	if not CharUpperProc := DllCall("GetProcAddress", uint, hmodule, str, "CharUpperA")
		MsgBox !CharUpperProc
	else
	{
		MyVar = abc
		result := DllCall(CharUpperProc, str, MyVar, str)
		if !(MyVar == "ABC" && result == "ABC")
			MsgBox Result: %result%|%MyVar%`nErr: "%ErrorLevel%"
	}
}

BTW, is this a bug? I have to use VarSetCapacity(sText, 10240, 1)

This is documented in the fine print: When a variable's address (e.g. &MyVar) is passed to a function and that function alters the length of the variable's contents, subsequent uses of the variable may behave incorrectly. To fix this, do one of the following: 1) Pass MyVar as a "str" argument rather than as a uint/address; 2) In v1.0.44.03+, call VarSetCapacity(MyVar, -1) to update the variable's internally-stored length after calling DllCall.

I've changed the documentation to bring more attention to this limitation. By the way, AutoHotkey remembers the string-length of each variable because it improves performance.

Thanks.

SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005
@ majkinetor / PhiLho

Looks very interesting. Can you throw more light on this ? Maybe test and post a couple of examples ?

Please..

:)

Sean
  • Guests
  • Last active:
  • Joined: --

I've applied the suggestion. If you have time, feel free to try this test version:
http://www.autohotke... ... ddress.exe

It's a really fast implementation. Thanks for considering it.

BTW, is this a bug? I have to use VarSetCapacity(sText, 10240, 1)

This is documented in the fine print:

It was my fault. I seemed to miss it, I was quickly reading the "Variables and Expressions" section and DllCall() section before writing the examples.

I'd like to post one more example whose ProgID is InternetExplorer.Application (:it's in ExDisp.h).

I chose this example because in this case the COM Server resides in exe file (:iexplore.exe) and a new string type is unavoidable: BSTRING which is probably unfamiliar to many users. In reality, BSTRING is nothing but a UTF-16 string prepended with 4-byte string length data, but an important property of it is that it's owned by the system so can cross the process boundaries without a problem.

An excellent tool for getting information about COM is MS's oleview.exe which is included in reskit. And tlb from the site mentioned once is also good for this approach.

sUrl := "http://www.autohotkey.com/forum/"

DCOM := False ; here no need for DCOM

VarSetCapacity(wUrl, StrLen(sUrl) * 2 + 2)

; Convert from ANSI to UTF-16
DllCall("MultiByteToWideChar"
	, "Uint", 0
	, "Uint", 0
	, "Uint", &sUrl
	, "int",  -1
	, "Uint", &wUrl
	, "int",  StrLen(sUrl) + 1)

; Allocate BSTRING from UTF-16
pstr := DllCall("oleaut32\SysAllocString", "Uint", &wUrl)

; Initialize COM
DllCall("ole32\CoInitialize", "Uint", 0)

VarSetCapacity(CLSID_InternetExplorer, 16)

; Encode 128bit CLSID_InternetExplorer
EncodeInteger(&CLSID_InternetExplorer    , 0x0002DF01)
EncodeInteger(&CLSID_InternetExplorer + 4, 0)
EncodeInteger(&CLSID_InternetExplorer + 8, 0xC0)
EncodeInteger(&CLSID_InternetExplorer +12, 0x46 << 24)

VarSetCapacity(IID_IWebBrowser2, 16)

; Encode 128bit IID_IWebBrowser2
EncodeInteger(&IID_IWebBrowser2    , 0xD30C1661)
EncodeInteger(&IID_IWebBrowser2 + 4, 0xCDAF | 0x11D0 << 16)
EncodeInteger(&IID_IWebBrowser2 + 8, 0x8A | 0x3E << 8 | 0x00 << 16 | 0xC0 << 24)
EncodeInteger(&IID_IWebBrowser2 +12, 0x4F | 0xC9 << 8 | 0xE2 << 16 | 0x6E << 24)

; CreateObject( "InternetExplorer.Application" )
DllCall("ole32\CoCreateInstance"
	, "Uint", &CLSID_InternetExplorer
	, "Uint", 0
	, "Uint", 1 | 2 | 4 | (DCOM ? 0x10 : 0) ; CLSCTX_ALL
	, "Uint", &IID_IWebBrowser2
	, "UintP", ppwb)

pwb := DecodeInteger(ppwb)

; VARIANT
VarSetCapacity(var, 8 * 2, 0)

DllCall(DecodeInteger(pwb + 4*11) ; Navigate
	, "Uint", ppwb
	, "Uint", pstr
	, "Uint", &var
	, "Uint", &var
	, "Uint", &var
	, "Uint", &var)

DllCall(DecodeInteger(pwb + 4*41) ; (Make) Visible
	, "Uint", ppwb
	, "int",  true)

DllCall(DecodeInteger(pwb + 4* 2) ; Release
	, "Uint", ppwb)

; Uninitialize COM
DllCall("ole32\CoUninitialize")

; Free the allocated BSTRING
DllCall("oleaut32\SysFreeString", "Uint", pstr)


DecodeInteger(ref)
{
    DllCall("ntdll\RtlMoveMemory", "UintP", val, "Uint", ref, "Uint", 4)
    Return val
}

EncodeInteger(ref, val)
{
    DllCall("ntdll\RtlFillMemoryUlong", "Uint", ref, "Uint", 4, "Uint", val)
}



majkinetor
  • Moderators
  • 4512 posts
  • Last active: May 20 2019 07:41 AM
  • Joined: 24 May 2006
It seems that it is time now, Sean, to encapsulate some of the interesting COM objects. I think that WebBrowser2 control should on top of the request. It would be nice to pit things you already done in AHK form.

If you are interesting we can do that on wiki (cuz of colaboration).


Thank you Crhis for fast update. I guess we will have some COM functionality within AHK soon.
Thank you Sean for this COM insight.
Posted Image

Chris
  • Administrators
  • 10727 posts
  • Last active:
  • Joined: 02 Mar 2004
Nice example. Thanks for trying it out so quickly; and thanks again for the idea.

Sean
  • Guests
  • Last active:
  • Joined: --

I think that WebBrowser2 control should on top of the request.

There was a reason I used CLSID_InternetExplorer than CLSID_WebBrowser. This WebBrowser object owns a window, i.e., should be tied to a (Internet Explorer) window to be useful. If I had used merely CLSID_WebBrowser, the object created would have been useless as it's bound to no window at all, contrary to CLSID_InternetExplorer which creates own window.

To obtain the corresponding WebBrowser object of an existing Internet Explorer window is non-trivial, but possible fortunately. Windows provides IShellWindows interface which keeps all running windows of explorer.exe and iexplore.exe and returns the corresponding WebBrowser object on request. But, unfortunately this time, it's provided only when the shell is explorer.exe. If using an alt shell, then can't have it.

BTW, I found out that the script I mentioned is really about embedding a WebBrowser control in GUI of AHK. And, I think what you're really interested in is also this.

If merely embedding a WebPage in GUI is the sole purpose, there is a really simple way. I saw this method in at least two sites. I borrowed the code from them:

DllCall("atl\AtlAxWinInit")
DllCall("CreateWindowEx", 0, "AtlAxWin", "", 0x50000000, ..., GUI's Handle, ...)
; I omitted parameter types and some parameters for simplicity

Then, if explorer.exe is the shell, we can obtain the control object indirectly using IShellWindows.

But, if to completely control the embedded WebBrowser from creation to closing regardless of the environment, like the library cwebpage.dll does, is the purpose, then I can't tell if it's possible atm. Need a reseach.

Sean
  • Guests
  • Last active:
  • Joined: --

Nice example.

Thanks for good words.

Sean
  • Guests
  • Last active:
  • Joined: --
This is an example how to embed and control WebBrowser control.
I suppose now no need for an external library.

Gui, Show, w800 h600 Center, WebControl

hWnd := WinExist( "WebControl" )

CLSID_WebBrowser := "{8856F961-340A-11D0-A96B-00C04FD705A2}"

VarSetCapacity(IID_IWebBrowser2, 16)

;Encode 128bit IID_IWebBrowser2
EncodeInteger(&IID_IWebBrowser2    , 0xD30C1661)
EncodeInteger(&IID_IWebBrowser2 + 4, 0xCDAF | 0x11D0 << 16)
EncodeInteger(&IID_IWebBrowser2 + 8, 0x8A | 0x3E << 8 | 0x00 << 16 | 0xC0 << 24)
EncodeInteger(&IID_IWebBrowser2 +12, 0x4F | 0xC9 << 8 | 0xE2 << 16 | 0x6E << 24)


hModule := DllCall("LoadLibrary", "str", "atl.dll")

DllCall("atl\AtlAxWinInit")

hCtrl := DllCall("CreateWindowEx"
	, "Uint", 0x200				           ; WS_EX_CLIENTEDGE
	, "str",  "AtlAxWin"			          ; ClassName
	, "Uint", &CLSID_WebBrowser		 ; WindowName
	, "Uint", 0x10000000 | 0x40000000       ; WS_VISIBLE | WS_CHILD
	, "int",  0				              ; Left
	, "int",  0				              ; Top
	, "int",  800				             ; Width
	, "int",  600				             ; Height
	, "Uint", hWnd
	, "Uint", 0
	, "Uint", 0
	, "Uint", 0)

; Obtain IUnknown Interface
DllCall("atl\AtlAxGetControl", "Uint", hCtrl, "UintP", ppunk)

; Obtain IWebBrowser2 Interface
DllCall(DecodeInteger(DecodeInteger(ppunk) + 4*0)    ; QueryInterface
	, "Uint", ppunk
	, "Uint", &IID_IWebBrowser2
	, "UintP", ppwb)

DllCall(DecodeInteger(DecodeInteger(ppunk) + 4*2)    ; Release
	, "Uint", ppunk)

pwb := DecodeInteger(ppwb)

sUrl := "http://www.autohotkey.com/"
VarSetCapacity(wUrl, StrLen(sUrl) * 2 + 2)
Unicode(sUrl, wUrl)

sUrl1 := "http://www.autohotkey.com/forum/"
VarSetCapacity(wUrl1, StrLen(sUrl1) * 2 + 2)
Unicode(sUrl1, wUrl1)

; VARIANT
VarSetCapacity(var, 8 * 2, 0)

DllCall(DecodeInteger(pwb + 4*11)    ; Navigate
	, "Uint", ppwb
	, "Uint", &wUrl
	, "Uint", &var
	, "Uint", &var
	, "Uint", &var
	, "Uint", &var)

Sleep, 5000

DllCall(DecodeInteger(pwb + 4*11)    ; Navigate
	, "Uint", ppwb
	, "Uint", &wUrl1
	, "Uint", &var
	, "Uint", &var
	, "Uint", &var
	, "Uint", &var)

Sleep, 5000

DllCall(DecodeInteger(pwb + 4*7)     ; GoBack
	, "Uint", ppwb)

Sleep, 3000

DllCall(DecodeInteger(pwb + 4*8)     ; GoForward
	, "Uint", ppwb)

Sleep, 2000

DllCall(DecodeInteger(pwb + 4*2)     ; Release
	, "Uint", ppwb)

DllCall("FreeLibary", "Uint", hModule)

MsgBox, DONE


Unicode(ByRef sString, ByRef wString)
{
    DllCall("MultiByteToWideChar"
	, "Uint", 0
	, "Uint", 0
	, "Uint", &sString
	, "int",  -1
	, "Uint", &wString
	, "int",  StrLen(sString) + 1)
}

DecodeInteger(ref)
{
    DllCall("ntdll\RtlMoveMemory", "UintP", val, "Uint", ref, "Uint", 4)
    Return val
}

EncodeInteger(ref, val)
{
    DllCall("ntdll\RtlFillMemoryUlong", "Uint", ref, "Uint", 4, "Uint", val)
}



BoBo
  • Guests
  • Last active:
  • Joined: --

sUrl := "http://www.autohotkey.com/"
VarSetCapacity(wUrl, StrLen(sUrl) * 2 + 2)
Unicode(sUrl, wUrl)
sUrl1 := "http://www.autohotkey.com/forum/"
VarSetCapacity(wUrl1, StrLen(sUrl1) * 2 + 2)
Unicode(sUrl1, wUrl1)

Looks promising (TBH, all that looks cryptic to me, so my expectations are 100% 'consumer orientated' :oops:).
I've executed your script/sample and based on the above links, set within the code I've expected to get/see one of those pages loaded within the Gui/Control. But I might be wrong.

Unfortunatly the control is staying empty. Could the following setting cause that issue because it's OS/Browser related:
CLSID_WebBrowser := "{8856F961-340A-11D0-A96B-00C04FD705A2}"
??

Thx for your effort. Much appreciated. :D

majkinetor
  • Moderators
  • 4512 posts
  • Last active: May 20 2019 07:41 AM
  • Joined: 24 May 2006
[deleted]
Posted Image

foom
  • Members
  • 386 posts
  • Last active: Jul 04 2007 04:53 PM
  • Joined: 19 Apr 2006
Sean could you please elaborate where you get the information to build for example the IID_IWebBrowser2 (InterfaceID?)?