How does "DllCall" work? Topic is solved

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
User avatar
Brain-Shock Omega
Posts: 11
Joined: 13 Mar 2019, 03:41

How does "DllCall" work?

Post by Brain-Shock Omega » 25 Jun 2019, 17:54

I've been reading through the code of this "Vista Audio Control Library" https://github.com/Masonjar13/AHK-Library/blob/master/Required-Libraries/VA.ahk

After finding what functions I need for my application, i'm not really understanding how the functions work as many of them are simply returns from a DllCall command, such as this one specifically:

Code: Select all

VA_IAudioMeterInformation_GetPeakValue(this, ByRef Peak) {                              
    return DllCall(NumGet(NumGet(this+0)+3*A_PtrSize), "ptr", this, "float*", Peak)
When I looked up the documentation for DllCall here: https://www.autohotkey.com/docs/commands/DllCall.htm
as well as NumGet: https://www.autohotkey.com/docs/commands/NumGet.htm

I became somewhat confused on how exactly this expression after DllCall is telling the PC where to go for whatever address it is looking for. To me it seems like the address being called is a location inside the System32 folder of the PC, I guess. Then with that address some file or function is being ran that is giving the value that this function requires and gets the PeakValue of the audio device that was obtained previously for another "GetDevice" function.

After reading how NumGet is supposed to work though, i'm not understanding how it is being used as a parameter to go to this specific address and how it is even working to get said number.

Basically i'm saying I don't really understand how DllCall works and the documentation is not really giving me an answer that I can understand with my little experience communicating directly with files from the PCs OS files. Also I don't get how NumGet is being used in the expression and if this is something that is used often when making functions and libraries such as this. Any additional info would be appreciated, even if it's just a source that I can read about how this works in a more layman's terms and if I might be having to use DllCalls and such once I get deeper into programming and scripting beyond a beginner level, thanks.
User avatar
Brain-Shock Omega
Posts: 11
Joined: 13 Mar 2019, 03:41

Re: How does "DllCall" work?

Post by Brain-Shock Omega » 26 Jun 2019, 00:46

So I got an answer on the AHK Discord channel. Apparently the example I gave was one of the more complex examples I could have been trying to start out with. If anyone would like to know the details about it, I would be happy to post the conversation, but the user did not want me to use their name, so i'll transcribe the conversation if anyone asks me to.

Overall, it seems like I was sort of close to understanding what was going on in this function. Primarily the "NumGet" is being used in an expression because the address of the info that is being retrieved, or the function that is being called from the .dll file at said address is changing constantly (this can be seen if you print the output of the "GetDevice" function over a few compile cycles). So a static address can't be given in this case since the location is changing.
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: How does "DllCall" work?

Post by jeeswg » 26 Jun 2019, 03:01

The ITaskbarList example, at the bottom of this page, should make things clearer:
DllCall() - Syntax & Usage | AutoHotkey
https://www.autohotkey.com/docs/commands/DllCall.htm

Plus this note re. ComObjCreate:
ComObjCreate() - Syntax & Usage | AutoHotkey
https://www.autohotkey.com/docs/commands/ComObjCreate.htm
If an IID is specified, an interface pointer is returned. The script must typically call ObjRelease when it is finished with the pointer.

Otherwise, a wrapper object usable by script is returned. See object syntax.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: How does "DllCall" work?  Topic is solved

Post by swagfag » 26 Jun 2019, 11:20

first u ComObjCreate(read: get a pointer to) the IMMDeviceEnumerator interface.
through a long and complicated chain of operations that im not gonna get into(u can look inside VA_GetDevice()), u get back the pointer to the device u specified or the default one.
next, u ask it for the pointer to the IAudioMeterInformation interface.
once u have that, u need to call the IAudioMeterInformation object's GetPeakValue() method.
DllCall can call a function from a pointer(u can give it an address, eg DllCall(830592365)).
an object's methods are a set of function pointers stored in a thing called vtable.
ComObjects most of the time have their vtables all up in front, as the very first member(but this isnt a requirement, so if ure working with wonky ComObjects or multiple inheritance, the vtable might be elsewhere; in the middle, at the end, whatever)
so the pointer to the ComObject will usually point to the start of the vtable and in turn the first method in the vtable.
all ComObjects inherit from IUnknown which contains the methods QueryInterface(), AddRef(), Release(), so whatever actual ComObject u end up retrieving, its methods start at the offset 3
if this is the pointer to IAudioMeterInformation, vtable := NumGet(this+0) gets u the pointer to the vtable(and u have to do +0 on v1 or else ahk will do funny stuff)
if vtable is the pointer to IAudioMeterInformation's vtable, GetPeakValue := NumGet(vtable+0, 3 * A_PtrSize) gets u the pointer to the method IAudioMeterInformation::GetPeakValue(). to find out which offset u have to use, look inside endpointvolume.h - its the first topmost method, so we go 0,1,2(recall IUnknown's methods),3 and thats the offset.
if GetPeakValue is the pointer to the method IAudioMeterInformation::GetPeakValue() and this is the pointer to IAudioMeterInformation, DllCall(GetPeakValue, "ptr", this, "float*", Peak) calls the function. ull notice GetPeakValue(float* fPeak) only mentions 1 parameter, so why does the DllCall pass in 2? its because GetPeakValue is a method, not a function. methods are called on instances, eg myAudioMeter.GetPeakValue(fpeak). this is functionally equivalent to GetPeakValue(myAudioMeter, fpeak) - the object being acted upon is actually the function's first argument
User avatar
Brain-Shock Omega
Posts: 11
Joined: 13 Mar 2019, 03:41

Re: How does "DllCall" work?

Post by Brain-Shock Omega » 26 Jun 2019, 15:41

Thanks @jeeswg for pointing out those examples.

Also thanks @swagfag for the detailed breakdown of what is going on. I have to ask though from some other info I heard from my previous Discord conversation. You mentioned a "vtable", so I assume this is the part of a .dll file that is currently only readable as machine code, but the vtable is the "Index" of which functions can be requested from that machine code with specific addresses being a binary number, and that's why GetNum is being used.

Hopefully I am somewhat close with what I am saying about vtables, first time I have heard of them in my learning.

Also, I guess it is normal for the addresses of these vtables to be changes constantly? Since if you use the "GetDevice" function, the output address that it gives changes every-time that function runs, so I assume this happens with most objects in the computer's memory. Is there cases where a .dll or vtable has a static address, or does it always change?
uepnol
Posts: 6
Joined: 02 Dec 2019, 01:36

Re: How does "DllCall" work?

Post by uepnol » 12 Jan 2022, 20:04

swagfag wrote:
26 Jun 2019, 11:20
first u ComObjCreate(read: get a pointer to) the IMMDeviceEnumerator interface.
through a long and complicated chain of operations that im not gonna get into(u can look inside VA_GetDevice()), u get back the pointer to the device u specified or the default one.
next, u ask it for the pointer to the IAudioMeterInformation interface.
once u have that, u need to call the IAudioMeterInformation object's GetPeakValue() method.
DllCall can call a function from a pointer(u can give it an address, eg DllCall(830592365)).
an object's methods are a set of function pointers stored in a thing called vtable.
ComObjects most of the time have their vtables all up in front, as the very first member(but this isnt a requirement, so if ure working with wonky ComObjects or multiple inheritance, the vtable might be elsewhere; in the middle, at the end, whatever)
so the pointer to the ComObject will usually point to the start of the vtable and in turn the first method in the vtable.
all ComObjects inherit from IUnknown which contains the methods QueryInterface(), AddRef(), Release(), so whatever actual ComObject u end up retrieving, its methods start at the offset 3
if this is the pointer to IAudioMeterInformation, vtable := NumGet(this+0) gets u the pointer to the vtable(and u have to do +0 on v1 or else ahk will do funny stuff)
if vtable is the pointer to IAudioMeterInformation's vtable, GetPeakValue := NumGet(vtable+0, 3 * A_PtrSize) gets u the pointer to the method IAudioMeterInformation::GetPeakValue(). to find out which offset u have to use, look inside endpointvolume.h - its the first topmost method, so we go 0,1,2(recall IUnknown's methods),3 and thats the offset.
if GetPeakValue is the pointer to the method IAudioMeterInformation::GetPeakValue() and this is the pointer to IAudioMeterInformation, DllCall(GetPeakValue, "ptr", this, "float*", Peak) calls the function. ull notice GetPeakValue(float* fPeak) only mentions 1 parameter, so why does the DllCall pass in 2? its because GetPeakValue is a method, not a function. methods are called on instances, eg myAudioMeter.GetPeakValue(fpeak). this is functionally equivalent to GetPeakValue(myAudioMeter, fpeak) - the object being acted upon is actually the function's first argument
I'm self taught could you show me a working script of this because that way i could reverse engineer it to understand it
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: How does "DllCall" work?

Post by swagfag » 13 Jan 2022, 19:51

https://github.com/Masonjar13/AHK-Library/blob/master/Required-Libraries/VA.ahk#L306-L368
the vtable is the "Index"
a vtable can be anything ud like it to be. u can write ur own programming language that would emit asm such that ur v"table" would be splayed out across all available memory with every successive method pointer being followed by another pointer, pointing to the next method and so on. of course, having to traverse a possibly veery long chain of pointers in what-is-certainly-going-to-be noncontiguous memory would be a very inefficient(performance-wise) and moronic implementation, but u could do if u wanted to. idk, maybe u want to obfuscate something, for instance

but in the case of these CoreAudioAPI COM objects specifically, its pretty safe to assume the DLL that implements them is one compiled with the MSVC c++ compiler, so a vtable would be nothing more than a static contiguous array of function pointers to the objects' methods.
I guess it is normal for the addresses of these vtables to be changes constantly?
no, that would be quite ABnormal actually(not that it cant be done. it can, but then ud have to have a separate mechanism for keeping track of which already-instantiated objects need their pointers readjusted, in addition to actually readjusting them whenever the vtables relocate elsewhere... and who wants to waste memory and CPU cycles doing all that)
Since if you use the "GetDevice" function, the output address that it gives changes every-time that function runs, so I assume this happens with most objects in the computer's memory.
::GetDevice() gives u a pointer to an instantiated object. it (more likely than not) changes every time, because that's where the memory allocator has decided it would be nice to place it in memory. u havent retrieved the address of the vtable. if u had fetched it from 2 different object instances of the same class, ud have seen that it points to the same location in memory.
uepnol
Posts: 6
Joined: 02 Dec 2019, 01:36

Re: How does "DllCall" work?

Post by uepnol » 13 Jan 2022, 21:17

i really have no clue want i'm doing with dllcalls and referencing core audio api. ive gone down many rabbit holes trying different code to no avail to get GetPeakValue() of process id. could you give a working example i could figure out how to use. ps if your wondering what i want it for it is to make a hotkey that checks GetPeakValue() of process id of two programs, on hotkey press if processid is playing it pauses, if hotkey quickly pressed again play other process id
Post Reply

Return to “Ask for Help (v1)”