This Microsoft documentation would lead you to think it's this:
Declare PtrSafe Function MonitorFromPoint Lib "user32.dll" ( _
POINT As POINTAPI, _
ByVal dwFlags As LongPtr) As Long
Along with a UDT:
Type POINTAPI
X As Long
Y As Long
End Type
But calling this function always returns 0 on 64-bit Office and error 49 "Bad DLL calling convention" on 32-bit Office. FWIW, I left out the conditional compiler VBA7 directive for simplicity.
This appears to work on both 32 and 64 bit Office:
Declare PtrSafe Function MonitorFromPoint Lib "user32.dll" ( _
ByVal X As Long, _
ByVal Y As Long, _
ByVal dwFlags As LongPtr) As Long
But given the numerous references online (and the Microsoft documentation) using POINTAPI, I don't understand why. Even ChatGPT prefers X & Y but who cares right? ;-)
I also don't understand why the declaration doesn't appear in this Microsoft documentation "Win32API_PtrSafe.TXT"
This Microsoft documentation would lead you to think
Not really, it wouldn't.
DWORD
is always Long
, HMONITOR
is always LongPtr
, and the POINT
structure is accepted by value (POINT pt
) rather than by reference (POINT* pt
\ LPPOINT pt
). VBA's POINT As POINTAPI
is ByRef POINT As POINTAPI
.
The correct declaration, however, is tricky, because VBA cannot do byval structs.
For x86 where the arguments are passed on the stack you could simply inline the struct fields:
Declare PtrSafe Function MonitorFromPoint Lib "user32.dll" ( _
ByVal X As Long, _
ByVal Y As Long, _
ByVal dwFlags As Long) As LongPtr
That won't work for x64, because the POINT
struct is 8 bytes in size, which means it goes into a single register under x64. For that to be doable from VBA, you must declare the function in a way that accepts the entire 8 bytes of the struct as a single integer.
LongLong
fits the bill, but it does not exist in 32-bit VBA.
Currency
could also work, provided VBA treats it as an integer, which I'm not sure it does and cannot verify right now.
The cheap and dirty solution therefore is:
Public Type POINTAPI
X As Long
Y As Long
End Type
#If Win64 Then
Private Type POINTAPI_AsLongLong
Value As LongLong
End Type
Private Declare PtrSafe Function MonitorFromPointInternal _
Lib "user32.dll" Alias "MonitorFromPoint" ( _
ByVal pt As LongLong, _
ByVal dwFlags As Long) As LongPtr
Public Function MonitorFromPoint(pt As POINTAPI, ByVal dwFlags As Long) As LongPtr
Dim t As POINTAPI_AsLongLong
LSet t = pt
MonitorFromPoint = MonitorFromPointInternal(t.Value, dwFlags)
End Function
#Else
Private Declare PtrSafe Function MonitorFromPointInternal _
Lib "user32.dll" Alias "MonitorFromPoint" ( _
ByVal X As Long, _
ByVal Y As Long, _
ByVal dwFlags As Long) As LongPtr
Public Function MonitorFromPoint(pt As POINTAPI, ByVal dwFlags As Long) As LongPtr
MonitorFromPoint = MonitorFromPointInternal(pt.X, pt.Y, dwFlags)
End Function
#End If