How to use an enum as an output variable for DllCall

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
User avatar
Kenneth Evans
Posts: 4
Joined: 01 Mar 2018, 19:42

How to use an enum as an output variable for DllCall

01 Mar 2018, 20:02

I would like to implement:

Code: Select all

HRESULT WINAPI GetProcessDpiAwareness(
  _In_  HANDLE                hprocess,
  _Out_ PROCESS_DPI_AWARENESS *value
);
where:

Code: Select all

typedef enum _PROCESS_DPI_AWARENESS { 
  PROCESS_DPI_UNAWARE            = 0,
  PROCESS_SYSTEM_DPI_AWARE       = 1,
  PROCESS_PER_MONITOR_DPI_AWARE  = 2
} PROCESS_DPI_AWARENESS;
I am using something like:

Code: Select all

    	res := DllCall("SHcore\GetProcessDpiAwareness", "Ptr", windowHandle, "Int", dpiAwareness)
or:

Code: Select all

    	VarSetCapacity(dpiAwareness, 1)  ; Value is an enum
    	res := DllCall("SHcore\GetProcessDpiAwareness", "Ptr", windowHandle, "Ptr", &dpiAwareness)
In either case A_LastError is 87 (The parameter is incorrect). I use the variable windowHandle successfully in other DllCall's so it is not likely the problem.

So how do I implement this correctly?

Thanks in advance.
Guest

Re: How to use an enum as an output variable for DllCall

02 Mar 2018, 09:38

Code: Select all

WinGet, PID, PID, ahk_id %windowHandle%
processHandle := DllCall("OpenProcess", "UInt", 0x0400, "Int", FALSE, "UInt", PID, "Ptr")
if (processHandle) {
    res := DllCall("SHcore\GetProcessDpiAwareness", "Ptr", processHandle, "UIntP", dpiAwareness)
    if (res == 0)
    	MsgBox %dpiAwareness% ; compare the numeric value displayed to a symbolic constant from the PROCESS_DPI_AWARENESS enum in your head
    DllCall("kernel32\CloseHandle", "Ptr", processHandle)
}
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: How to use an enum as an output variable for DllCall

02 Mar 2018, 09:45

Code: Select all

DllCall("shcore.dll\GetProcessDpiAwareness", "ptr", hProcess, "int*", PROCESS_DPI_AWARENESS)
MsgBox % "DPI awareness: " PROCESS_DPI_AWARENESS

edit: too late ^^
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
User avatar
Kenneth Evans
Posts: 4
Joined: 01 Mar 2018, 19:42

Re: How to use an enum as an output variable for DllCall

03 Mar 2018, 00:24

Neither of these worked for me. The MsgBox shows a value, but it is the same for all windows, and is not correct. In addition, LastError is 87.

This is the full script I am using for testing (if you would like to try it or see if I have an error). I know hh (HotKey Help) is PROCESS_SYSTEM_DPI_AWARE and Chrome is PROCESS_PER_MONITOR_DPI_AWARE. (You can get this from SysInternals Process Explorer, if you enable that column.)

I have been using AutoHotkey less than a week, so it wouldn't be surprising if I have something wrong.

Code: Select all

#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.
; #Warn  ; Enable warnings to assist with detecting common errors.
SendMode Input  ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir%  ; Ensures a consistent starting directory.

GetInfo("hh.exe")
GetInfo("Chrome.exe")
return

GetInfo(name){
	WinGet, hProcess, ID, ahk_exe %name%
	WinGet, processName, ProcessName, ahk_exe %name%
	WinGetTitle, title, ahk_exe %name%

	DllCall("SetLastError", "UInt", 0)
	dpiAwarenessErr0 := A_LastError 
	;DllCall("shcore.dll\GetProcessDpiAwareness", "ptr", hProcess, "UIntP", PROCESS_DPI_AWARENESS)
	DllCall("shcore.dll\GetProcessDpiAwareness", "ptr", hProcess, "int*", PROCESS_DPI_AWARENESS)
	dpiAwarenessErr := A_LastError 
	
	MsgBox % name . "`n"
	. "processName: " . processName . "`n"
	. "hProcess: " . hProcess . "`n"
	. "title: " . title . "`n"
	. "DPI awareness: " PROCESS_DPI_AWARENESS . "`n"
	. "LastError: " . A_LastError . "`n"
	return
}
Thanks for the help.
User avatar
Flipeador
Posts: 1204
Joined: 15 Nov 2014, 21:31
Location: Argentina
Contact:

Re: How to use an enum as an output variable for DllCall

03 Mar 2018, 00:39

Code: Select all

MsgBox % GetProcessDpiAwareness("Chrome.exe")

GetProcessDpiAwareness(Process)
{
    Static PROCESS_DPI_AWARENESS := {0: "PROCESS_DPI_UNAWARE", 1: "PROCESS_SYSTEM_DPI_AWARE", 2: "PROCESS_PER_MONITOR_DPI_AWARE"}

    Process, Exist, % Process
    If (!(ProcessId := ErrorLevel))
        Return "The process does not exist"

    If (!(hProcess := DllCall("Kernel32.dll\OpenProcess", "UInt", 0x0400, "Int", FALSE, "UInt", ProcessId, "Ptr")))
        Return "OpenProcess ERROR"

    If (DllCall("Shcore.dll\GetProcessDpiAwareness", "Ptr", hProcess, "IntP", DPI_AWARENESS))
    {
        DllCall("Kernel32.dll\CloseHandle", "Ptr", hProcess)
        Return "GetProcessDpiAwareness ERROR"
    }

    DllCall("Kernel32.dll\CloseHandle", "Ptr", hProcess)
    Return PROCESS_DPI_AWARENESS[DPI_AWARENESS]
}
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: How to use an enum as an output variable for DllCall

03 Mar 2018, 06:07

Hello.
To note, GetProcessDpiAwareness() requires Windows 8.1 or newer.
Also, regarding your second try in the first post, that is,

Code: Select all

VarSetCapacity(dpiAwareness, 1)  ; Value is an enum
res := DllCall("SHcore\GetProcessDpiAwareness", "Ptr", windowHandle, "Ptr", &dpiAwareness)
That is ok, but note that you would need to use numget(&dpiAwarness,"char") to get the value. Your main issue seems to be that you are passing a window handle (HWND),

Code: Select all

WinGet, hProcess, ID, ahk_exe %name% ; This gets the HWND of the window
and not the process handle (HANDLE hprocess).

Finally, varsetcapacity allocates a minimum of 4 bytes on ANSI builds and 8 bytes on unicode, this doesn't really matter here, but could be useful for future reference.

Cheers.
User avatar
Kenneth Evans
Posts: 4
Joined: 01 Mar 2018, 19:42

Re: How to use an enum as an output variable for DllCall

03 Mar 2018, 14:16

Ok, the problem is that I was using a window handle rather than a process handle. Somehow I didn't get that into my head until Helgef said it explicitly.

The function supplied by Flipeador is cool. I am now using something like that. The current version of WinDef.h defines DPI_AWARENESS as:

Code: Select all

typedef enum DPI_AWARENESS {
    DPI_AWARENESS_INVALID           = -1,
    DPI_AWARENESS_UNAWARE           = 0,
    DPI_AWARENESS_SYSTEM_AWARE      = 1,
    DPI_AWARENESS_PER_MONITOR_AWARE = 2
} DPI_AWARENESS;
Thus IntP is the correct type, and I added the DPI_AWARENESS_INVALID value to his array.

Many thanks to all who responded. I learned some things about AutoHotkey in addition to resolving this issue.
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: How to use an enum as an output variable for DllCall

03 Mar 2018, 15:38

Thus IntP is the correct type
Are you certain? As far as I know an enum doesn't guarantee a type, at least not in c, but I'm not certain. All values of the enum fits in a byte, so if the function writes -1 to a byte, you will get 255 back from int* :thumbdown:. In addition if you happen to have assigned a value to the variable you are passing, i.e, x in dllcall(..."int*", x), say, x:=0x100, then the enum values 1-3 will comeback as 0x101-0x103 if the function only writes to a single byte. So you could test, but I wouldn't rely on the result. However, if you use char*, you will get the correct result regardless of wether the function writes -1-3, to one, four or eight bytes. But passing a char* isn't good if it writes more than a byte, so, varsetcapacity(x,8) + dllcall(..."ptr", &x) + numget(x,"char") should be safe :thumbup:.

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

Re: How to use an enum as an output variable for DllCall

03 Mar 2018, 16:15

When I tried to read up on an enum, the suggestion was that the value would be an Int or UInt. Assuming it is an Int or UInt, if there are any negative values then it's an Int, and if it has 0x80000000 as a possible value then it's a UInt. Perhaps there are exceptions, but if I see enum I guess Int if no other information is given.
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: How to use an enum as an output variable for DllCall

03 Mar 2018, 16:55

@jeeswg, you can assign an enum value to any integer type, i.e., I doubt a (c) compiler would complain about

Code: Select all

enum a {b=-1};    //Edit: =-1
int x = b;
char y = b;
if I see enum I guess Int
You can guess whatever you like, but in this case, interpreting the value as char will work for all of the possible enum values :thumbup:.

Cheers.
User avatar
Kenneth Evans
Posts: 4
Joined: 01 Mar 2018, 19:42

Re: How to use an enum as an output variable for DllCall

04 Mar 2018, 09:53

Am I certain? I am reasonably certain IntP works for my case, though I've never got a -1. In general, no.

The following article says an enum can be any integral type. That may include long long or unsigned long long.

https://docs.microsoft.com/en-us/cpp/cp ... ations-cpp

This article says: Values of unscoped enumeration type are implicitly-convertible to integral types. If the underlying type is not fixed, the value is convertible to the first type from the following list able to hold their entire value range: int, unsigned int, long, unsigned long, long long, or unsigned long long. If the underlying type is fixed, the values can be converted to their promoted underlying type.

http://en.cppreference.com/w/cpp/language/enum

In C++ I would write:

DPI_AWARENESS dpiAwareness = GetProcessDpiAwareness(processID);

and it would all be taken care of. As to what AutoHotkey is doing, I do not know. I think this should be in the FAQ, which I assume are blessed by someone who does know.
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: How to use an enum as an output variable for DllCall

08 Mar 2018, 14:22

Hello Kenneth Evans, thank you for the links. My comments,

From the second link,
Unscoped enumeration
enum name { enumerator = constexpr , enumerator = constexpr , ... } (1)
[...]
1) Declares an unscoped enumeration type whose underlying type is not fixed (in this case, the underlying type is an implementation-defined integral type that can represent all enumerator values; this type is not larger than int unless the value of an enumerator cannot fit in an int or unsigned int.
This is the case for enum DPI_AWARENESS, hence, the underlying type can be anything, but not larger than int, since -1-3 all fits within an int.
In C++ I would write: [...] and it would all be taken care of. As to what AutoHotkey is doing, I do not know.
Indeed, we do not worry because the compiler will manage it for us, but, AHK knows nothing about what the unknown compiler did with the function you are calling, it will pass what you tell it, and interpret the result as you tell it. So in your case, int* is fine for the range 1-3 (whenever you pass a pointer to a zero). If you are interested in the -1 value, you need to also check for 0xff and 0xffff (short). Or simply let -1 be an else-case.

Cheers.
lexikos
Posts: 9583
Joined: 30 Sep 2013, 04:07
Contact:

Re: How to use an enum as an output variable for DllCall

11 Mar 2018, 02:01

That is a good point, but I think it's fairly academic.

Consider what happens if mstype (the type used by the compiler of the Win32 libraries) is different to yourtype (the type used by your C++ compiler):
  1. If mstype is smaller than yourtype, you may get the wrong value, since the upper bytes may be left uninitialized or contain the wrong sign (0x00 for positive vs 0xFF for negative).
  2. If mstype is larger than yourtype, the API might cause an access violation, or overwrite any variable adjacent to the one whose address you passed.
  3. If mstype is the same as yourtype, it's all good.
Maybe I'm missing something, but it seems to me that all of the compilers used to build Win32 applications must use the same underlying type, or they just wouldn't work.

I can't find DPI_AWARENESS in my copy of the Windows SDK, but if I copy the definition into a C++ project (AutoHotkey) and refer to std::underlying_type<DPI_AWARENESS>::type, IntelliSense shows it as int. That matches what I've observed with sizeof() for other enums. If we're targeting the Win32 API rather than Standard C++, I think it's safe to assume that enums use int as the underlying type (when it fits all possible values).

Of course, that's not a reason to not use "char*".

It may be interesting to note that when you pass an integer parameter with the "*" or "P" suffix, DllCall passes the address of a 64-bit variable containing the full 64-bit input value regardless of which base type you used. It only truncates the output value, not the input value.
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: How to use an enum as an output variable for DllCall

12 Mar 2018, 06:13

lexikos wrote:It may be interesting to note
Very much, thank you.

With that in mind, I will conclude, at least for my own future reference,

How to use an enum as an output variable for DllCall?
  • Use the smallest type* which fits all enumerators of the enum.
How to use an enum as an input variable for DllCall?
  • Use int* when the callee wants a value.
How to use an enum as an input/output variable for DllCall?
  • When the callee wants a value, and might modify it, I recommend varsetcapacity(inout, 4), numput(in, inout, "int"), dllcall(..., "ptr", &inout, ...), out := numget(inout, smallestType). (In v2, we can skip varsetcapacity, and do "ptr", &(inout:=in), but we need to do numget in general.)
The observant reader noted that I used bold above, ... an enum ..., and indeed, I have a reason, because there is another case which is less trivial than the above ones. Consider the case when you want to call a function declared as, eg,void f(DPI_AWARENESS values[4]) :lol:.

For an output array, we can handle it without knowing the underlying type beforehand. However, for an input array, we are more or less toast :( .
it seems to me that all of the compilers used to build Win32 applications must use the same underlying type, or they just wouldn't work.
Especially with the array cases in mind, I agree, or it would be pretty bad practice to ask for enum* as in/out :think:. Yet, I've seen no convincing evidence suggesting a standard type, although int seems like the most likely candidate :problem:.

Note, most of this discussion is pointless if there are no negative enumerators :lol:.

Cheers.
Regarding the output array case, I considered making it a puzzle but it didn't really turn out suitable for that thread. I leave it here if someone wants to ponder it.
Bonus puzzle

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: Anput, Nerafius and 116 guests