Although the use of the UnsignedLong (ULong) data type in external function declarations for Windows handles in WinAPI functions may work in many cases, I think it is a terrible idea for several reasons.
1. It is not how the WinAPI functions are documented, so for all intent and purposes, it's incorrect.
The only reason it works at all is because of the interprocess communcation between 32-bit and 64-bit applications that the Windows O/S facilitates (refer to the link that Arnd posted in a comment elsewhere in this thread [Thank you for posting that link, Arnd!] ). From my perspective, using ULong for a Windows handle is well, sloppy programming.
2. In only works for function argument parameters and not in structures.
Several API functions require the use of structures, and a structure member that holds a Windows handle must be a 64-bit integer in a 64-bit process/application, or else the API function will not be able to extract all of the structure member values correctly. Using the wrong data type not only affects the relative offset of any following structure members from the beginning of the structure, it can and often does change the alignment of structure members and the alignment of the structure itself in memory... as well as the overall length of the structure.
Some WinAPI structures contain a member that specifies the structure length, so specifying the correct structure length can be critical to ensure it works correctly.
Here's a simple example. The FlashWindowEx API function gets passed the address of a FLASHWINFO structure. That structure is defined as follows in C/C++:
typedef struct {
UINT cbSize;
HWND hwnd;
DWORD dwFlags;
UINT uCount;
DWORD dwTimeout;
} FLASHWINFO, *PFLASHWINFO;
Here's the PB external function declaration:
FUNCTION Boolean FlashWindowEx ( REF s_flashwinfo pfwi ) LIBRARY "user32.dll"
Note: The structure argument is passed by reference because the API function requires that the memory address of the structure be passed as the argument value.
In 32-bit, each of the three typedef'd data types in the structure declaration (UINT, HWND and DWORD) are equivalent to ULongs in PB, so the members are laid out in memory as five consecutive four-byte unsigned integers. Since the largest size of any structure member is four bytes, the structure gets aligned on a four-type boundary and its length must be a multiple of four. Its length is therefore 20 bytes, so the caller must set the value of the first member (the size of the structure, in memory, in bytes) to 20.
The structure becomes dramatically different in 64-bit. The first member is still a four-byte integer, but the type definition HWND used by second argument (the handle to the window that is to be "flashed") becomes an eight-byte integer in 64-bit. Because it is now an eight-byte integer, the second structure member must now be aligned on a memory address that is evenly divisible by eight instead of four... so four bytes of "padding" are needed in memory between the first and second structure members. This brings the structure length at this point to 16 bytes. The remaining three members are still four-byte integers as they were in 32-bit, so they can immediately follow the second member in memory. The structure length is 16 + 12 = 28 bytes, right?
Unfortunately, no.
You see, the length and starting memory address of a structure must be evenly divisible by the size of the largest data element, which was four in 32-bit but is now eight in 64-bit because the second structure member (the handle) is now an eight-byte integer instead of four. Therefore, another four bytes of padding are required after the last structure member. The correct structure length is 32 bytes and this is value that is needed in the first structure member in order for the API function to be called successfully in 64-bit.
This reinforces my next reason for not using ULong for Windows handles:
3. Consistency. If you always use the proper data type, you won't have to try and remember these arcane rules and recognize when you should use an eight-byte integer instead of a four-byte integer. Other developers looking at your code won't be confused (or at least maybe they will not be AS confused ).
4. The Longptr data type is the proper and perfect data type for this use, because the PB compiler changes the length allocated for this data type, based on the target bitness.
5. The PowerScript Handle() function was recently changed to now return a Longptr data type, so Appeon Engineering recognizes that Longptr is the correct and proper data type to be used with Windows handles.
My final reason to use Longptr for Windows handles is the simplest and I think the best reason:
6. It provides a good example for other developers to follow.
So, Chris, a lot of PB developers look to the STD framework that you've developed for guidance on the proper way to do things in PB, as well they should... you've done a terrific job in designing it, providing the framework to all, and in keeping it updated. For this reason, I think it would be best for everyone if you made the change to use Longptr everywhere a Windows handle is being utilized.
John
P.S.: For all, here's a link to Microsoft documentation that I use frequently to help me translate WinAPI typedef'd data types to PB data types:
https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types
Some type definitions are nested, so in those cases you have to work your way through multiple layers. For example HWND becomes HANDLE, which becomes PVOID, which becomes a pointer (memory address) to any data type (or you can also think of it as a pointer to anything). A memory address in 32-bit is 32 bits (four bytes) long, and in 64-bit is 64-bits (eight bytes) long. This is why/how the length of a Windows handle is dependent on the app's bitness.
It is possible to call a function with int or long that has a longptr as function argument.
Even more if a function returns longptr there is no warning that you have a type clash when you assign it to a long or ulong.
@Chris, I am not sure if you should also better use longptr for HWND (your screenshot seems to use ulong for HWND).
Yes, you certainly can. I still have many external calls that pass a pointer as uLong and did not need to change this to LongPtr and they still work in the 32 & 64 bit world. So it seems to me that the requirement for using LongPtr is based on how the external method has been written. HTH
Regards ... Chris
https://learn.microsoft.com/en-us/windows/win32/winprog64/interprocess-communication
Declaring GetFocus() and GetForegroundWindow() returning "different" types (ulong vs. LongPtr) is doable. But I don't like it:-(
Regards .. Arnd