Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
APIs from QB64PE and parameters defined As Any and unions of types ?
#1
Question 
In trying to figure out some Raw Input API stuff to read the keyboard, I found a PowerBASIC thread which has API declarations that I want to try porting to QB64PE.

This includes some strange stuff - "AS ANY" and "UNION":

Code: (Select All)
BYREF pData AS ANY

...

TYPE RID_DEVICE_INFO_KEYBOARD
dwType                 AS DWORD
dwSubType              AS DWORD
dwKeyboardMode         AS DWORD
dwNumberOfFunctionKeys AS DWORD
dwNumberOfIndicators   AS DWORD
dwNumberOfKeysTotal    AS DWORD
END TYPE

UNION RID_DEVICE_INFO_UNION
'mouse   AS RID_DEVICE_INFO_MOUSE
keyboard AS RID_DEVICE_INFO_KEYBOARD
'hid     AS RID_DEVICE_INFO_HID
END UNION

TYPE RID_DEVICE_INFO
cbSize AS DWORD
dwType AS DWORD
RID_DEVICE_INFO_UNION
END TYPE

I did some googling to understand UNION and AS ANY and how those might be translated into QB64PE:

Those are all VB and VB.NET threads and they get pretty deep into it... In the "as any" thread they analyze what's happening down to the assembly level! My brain hurts! 

I just want to know if anyone has any clue how to get the below code working with QB64/PE or could recommend the right syntax to declare a type with a union and what to do about "as any" ? Much appreciated... 


Here's all of it: 


Code: (Select All)
'Raw Keyboard (HID) Input (discussion) - PowerBASIC Peer Support Community
'https://forum.powerbasic.com/forum/user-to-user-discussions/powerbasic-for-windows/55985-raw-keyboard-hid-input-discussion#post673309
'
'#6
'Pierre Bellisle
'23 Mar 2014, 11:04 AM
'Thank for sharing Jeremy.
'
'Here's an adaption...

TYPE RID_DEVICE_INFO_KEYBOARD
dwType                 AS DWORD
dwSubType              AS DWORD
dwKeyboardMode         AS DWORD
dwNumberOfFunctionKeys AS DWORD
dwNumberOfIndicators   AS DWORD
dwNumberOfKeysTotal    AS DWORD
END TYPE

UNION RID_DEVICE_INFO_UNION
'mouse   AS RID_DEVICE_INFO_MOUSE
keyboard AS RID_DEVICE_INFO_KEYBOARD
'hid     AS RID_DEVICE_INFO_HID
END UNION

TYPE RID_DEVICE_INFO
cbSize AS DWORD
dwType AS DWORD
RID_DEVICE_INFO_UNION
END TYPE

TYPE RAWKEYBOARD
MakeCode         AS WORD
Flags            AS WORD
Reserved         AS WORD
VKey             AS WORD
Message          AS DWORD
ExtraInformation AS DWORD
END TYPE

UNION RAWINPUTUNION
'mouse    AS RAWMOUSE
keyboard  AS RAWKEYBOARD
'hid      AS RAWHID
END UNION

TYPE RAWINPUTHEADER
dwType  AS DWORD
dwSize  AS DWORD
hDevice AS DWORD
wParam  AS LONG
END TYPE

TYPE RAWINPUT
header AS RAWINPUTHEADER
data   AS RAWINPUTUNION
END TYPE

TYPE RAWINPUTDEVICELIST
hDevice AS DWORD
dwType  AS DWORD
END TYPE

TYPE RAWINPUTDEVICE
usUsagePage AS WORD
usUsage     AS WORD
dwFlags     AS DWORD
hwndTarget  AS DWORD
END TYPE

%Edit                    = 101            : %WM_CHAR                 = &H0102???
%LabelInfo               = 201            : %WM_MOUSEMOVE            = &H0200???
                                          : %WM_APPCOMMAND           = &H0319???
%Hid_Left                = 33             : %WM_INPUT                = &H00FF???
%Hid_Right               = 34             : %KL_NAMELENGTH           = 9
%Hid_Bottom              = 66             : %WM_KEYUP                = &H0101???
                                          : %WM_CHAR                 = &H0102???
%RIDEV_EXINPUTSINK       = &H00001000     : %WM_DEADCHAR             = &H0103???
%WM_INITDIALOG           = &H0110???      : %WM_SYSKEYDOWN           = &H0104???
%NULL                    = 0              : %WM_SYSKEYUP             = &H0105???
                                          : %WM_SYSCHAR              = &H0106???
%RIDI_DEVICEINFO         = &H2000000B???  : %WM_SYSDEADCHAR          = &H0107???
%RIM_TYPEKEYBOARD        = 1&             : %WM_KEYDOWN              = &H0100???
%RIM_TYPEMOUSE           = 0&             : %WM_KEYUP                = &H0101???
%RIM_TYPEHID             = 2&             : %WM_CHAR                 = &H0102???
%RID_INPUT               = &H10000003???  : %WM_DEADCHAR             = &H0103???
%RI_KEY_MAKE             = 0??            : %WM_SYSKEYDOWN           = &H0104???
%RI_KEY_BREAK            = 1??            : %WM_SYSKEYUP             = &H0105???
%RI_KEY_E0               = 2??            : %WM_SYSCHAR              = &H0106???
%RI_KEY_E1               = 4??            : %WM_SYSDEADCHAR          = &H0107???
%RI_KEY_TERMSRV_SET_LED  = 8??            : %WM_UNICHAR              = &H0109???
%RI_KEY_TERMSRV_SHADOW   = &H10??         : %WM_NCACTIVATE           = &H0086???
                                          : %WM_COMMAND              = &H0111???
%VK_LEFT                 = &H25&          : %WM_SIZE                 = &H0005???
%VK_UP                   = &H26&          : %WM_DESTROY              = &H0002???
%VK_RIGHT                = &H27&          : %WM_NEXTDLGCTL           = &H28
%VK_DOWN                 = &H28&          : %WM_SETICON              = &H0080???
%VK_PRIOR                = &H21&          : %WM_APP                  = &H08000
%VK_NEXT                 = &H22&          : %WS_CAPTION              = &H00C00000&
%VK_END                  = &H23&          : %WS_MINIMIZEBOX          = &H00020000&
%VK_HOME                 = &H24&          : %WS_SYSMENU              = &H00080000&
%VK_INSERT               = &H2D&          : %HWND_DESKTOP            = 0???
%VK_DELETE               = &H2E&          : %GCL_HICONSM             = -34&
%VK_DIVIDE               = &H6F&          : %GCL_HICON               = -14&
%VK_NUMLOCK              = &H90&          : %ICON_SMALL              = 0&
%VK_SCROLL               = &H91&          : %ICON_BIG                = 1&
%VK_CONTROL              = &H11&          : %SIZE_MINIMIZED          = 1
%KEYEVENTF_KEYUP         = &H0002???
%EN_CHANGE               = &H0300???
%EN_KILLFOCUS            = &H0200???
%EN_SETFOCUS             = &H0100???
%EM_GETSEL               = &H00B0???
%EM_SETSEL               = &H00B1???

'Thank to José Roca
DECLARE FUNCTION RegisterRawInputDevices LIB "USER32.DLL" ALIAS "RegisterRawInputDevices"(BYREF pRawInputDevices AS RAWINPUTDEVICE, BYVAL uiNumDevices AS DWORD, BYVAL cbSize AS DWORD) AS LONG
DECLARE FUNCTION GetRawInputDeviceList LIB "USER32.DLL" ALIAS "GetRawInputDeviceList"(BYREF pRawInputDeviceList AS RAWINPUTDEVICELIST, BYREF puiNumDevices AS DWORD, BYVAL cbSize AS DWORD) AS DWORD
DECLARE FUNCTION GetRawInputDeviceInfo LIB "USER32.DLL" ALIAS "GetRawInputDeviceInfoA"(BYVAL hDevice AS DWORD, BYVAL uiCommand AS DWORD, BYREF pData AS ANY, BYREF pcbSize AS  DWORD) AS DWORD
DECLARE FUNCTION SendDlgItemMessage LIB "USER32.DLL" ALIAS "SendDlgItemMessageA"(BYVAL hWnd AS DWORD, BYVAL nIDDlgItem AS LONG, BYVAL Msg AS DWORD, BYVAL wParam AS DWORD, BYVAL lParam AS LONG) AS LONG
DECLARE FUNCTION GetRawInputData LIB "USER32.DLL" ALIAS "GetRawInputData"(BYVAL hRawInput AS DWORD, BYVAL uiCommand AS DWORD, BYREF pData AS ANY, BYREF pcbSize AS DWORD, BYVAL cbSizeHeader AS DWORD) AS DWORD
DECLARE FUNCTION GetKeyNameText LIB "USER32.DLL" ALIAS "GetKeyNameTextA"(BYVAL lParam AS LONG, BYREF lpString AS ASCIIZ, BYVAL cchSize AS DWORD) AS LONG
DECLARE FUNCTION MapVirtualKey LIB "USER32.DLL" ALIAS "MapVirtualKeyA"(BYVAL uCode AS DWORD, BYVAL uMapType AS DWORD) AS DWORD
DECLARE FUNCTION GetFocus LIB "USER32.DLL" ALIAS "GetFocus"() AS DWORD
DECLARE FUNCTION GetDlgItem LIB "USER32.DLL" ALIAS "GetDlgItem"(BYVAL HWND AS DWORD, BYVAL nIDDlgItem AS LONG) AS DWORD
DECLARE FUNCTION SendMessage LIB "USER32.DLL" ALIAS "SendMessageA"(BYVAL hWnd AS DWORD, BYVAL Msg AS DWORD, BYVAL wParam AS DWORD, BYVAL lParam AS LONG) AS LONG
DECLARE FUNCTION DestroyIcon LIB "USER32.DLL" ALIAS "DestroyIcon"(BYVAL hIcon AS DWORD) AS LONG
DECLARE FUNCTION PostMessage LIB "USER32.DLL" ALIAS "PostMessageA"(BYVAL hWnd AS DWORD, BYVAL Msg AS DWORD, BYVAL wParam AS DWORD, BYVAL lParam AS LONG) AS LONG
DECLARE FUNCTION SetClassLong LIB "USER32.DLL" ALIAS "SetClassLongA"(BYVAL hWnd AS DWORD, BYVAL nIndex AS LONG, BYVAL dwNewLong AS DWORD) AS DWORD
DECLARE FUNCTION ExtractIconEx LIB "SHELL32.DLL" ALIAS "ExtractIconExA"(BYREF lpszFile AS ASCIIZ, BYVAL nIconIndex AS LONG, BYREF phiconLarge AS DWORD, BYREF phiconSmall AS DWORD, BYVAL nIcons AS DWORD) AS DWORD
DECLARE FUNCTION SetDlgItemText LIB "USER32.DLL" ALIAS "SetDlgItemTextA"(BYVAL hDlg AS LONG, BYVAL nIDDlgItem AS LONG, lpString AS ASCIIZ) AS LONG
DECLARE SUB      Keybd_event LIB "USER32.DLL" ALIAS "keybd_event"(BYVAL bVk AS BYTE, BYVAL bScan AS BYTE, BYVAL dwFlags AS DWORD, BYVAL dwExtraInfo AS DWORD)

GLOBAL hDlg AS DWORD
'______________________________________________________________________________

CALLBACK FUNCTION DlgProc
LOCAL  RidDeviceInfo       AS RID_DEVICE_INFO
LOCAL  pRawInput           AS RAWINPUT POINTER
LOCAL  zKeyName            AS ASCIIZ * 50
STATIC CtrlClass           AS ASCIIZ * 50
LOCAL  sRawInput           AS STRING
LOCAL  sBuffer             AS STRING
LOCAL  ScanCode            AS DWORD
STATIC hidDevice           AS DWORD
STATIC hFocusBak           AS DWORD
LOCAL  RawInputDevCount    AS LONG
LOCAL  KeyboardTypeCount   AS LONG
LOCAL  RawInputDeviceIndex AS LONG
STATIC hidF9               AS LONG
LOCAL  ByteCount           AS LONG
STATIC SelStart            AS LONG
STATIC SelEnd              AS LONG

SELECT CASE CBMSG

   CASE %WM_INITDIALOG
     GetRawInputDeviceList(BYVAL %NULL, RawInputDevCount, SIZEOF(RAWINPUTDEVICELIST)) 'Get raw input device count
     DIM RawInputDevList(0 TO RawInputDevCount - 1) AS RAWINPUTDEVICELIST 'Prepare raw input device array
     GetRawInputDeviceList(RawInputDevList(0), RawInputDevCount, SIZEOF(RAWINPUTDEVICELIST)) 'Get raw input device

     DIM RawInputDev(RawInputDevCount) AS RAWINPUTDEVICE 'Prepare raw input device array
     FOR RawInputDeviceIndex = 0 TO RawInputDevCount - 1
       GetRawInputDeviceInfo(RawInputDevList(RawInputDeviceIndex).hDevice, %RIDI_DEVICEINFO, RidDeviceInfo, SIZEOF(RID_DEVICE_INFO)) 'Get raw input device info
       SELECT CASE RidDeviceInfo.dwtype 'Get raw input device type

         CASE %RIM_TYPEKEYBOARD 'Keyboard type
           RawInputDev(KeyboardTypeCount).usUsagePage = 1
           RawInputDev(KeyboardTypeCount).usUsage     = 6
           RawInputDev(KeyboardTypeCount).dwFlags     = %RIDEV_EXINPUTSINK 'Vista+, receive input in the background
           RawInputDev(KeyboardTypeCount).hwndTarget  = hDlg
           INCR KeyboardTypeCount 'Count of raw keyboard input device

         CASE %RIM_TYPEMOUSE 'Mouse raw input device
         CASE %RIM_TYPEHID 'Other raw input device, game controllers, joysticks, etc.

       END SELECT
     NEXT
     RegisterRawInputDevices(RawInputDev(0), KeyboardTypeCount, SIZEOF(RAWINPUTDEVICE)) 'Register raw input device(s)
     PostMessage(hDlg, %WM_APP, 0, 0)

   CASE %WM_INPUT 'Sent to the window that is getting raw input
     GetRawInputData(CBLPARAM, %RID_INPUT, BYVAL %NULL, ByteCount, SIZEOF(RAWINPUTHEADER)) 'Get size of raw input buffer
     sRawInput = NUL$(ByteCount) 'Set string for hid input
     GetRawInputData(CBLPARAM, %RID_INPUT, BYVAL STRPTR(sRawInput), ByteCount, SIZEOF(RAWINPUTHEADER))'Get hid input
     pRawInput = STRPTR(sRawInput) 'Set RawInput pointer

     sBuffer = "RawInput.Header.hDevice = " & HEX$(@pRawInput.header.hDevice, 8) & $CRLF 'Show handle
     sBuffer = sBuffer & "RawInput.Header.dwType = " & CHOOSE$(@pRawInput.header.dwType + 1, _
     "RIM_TYPEMOUSE", "RIM_TYPEKEYBOARD", "RIM_TYPEHID") & $CRLF 'Show type

     sBuffer = sBuffer & $CRLF
     sBuffer = sBuffer & "RawInput.data.Keyboard.vKey =" & STR$(@pRawInput.data.Keyboard.vKey) & _ '
                         ", Character is " & $DQ & CHR$(@pRawInput.data.Keyboard.vKey) & $DQ & $CRLF & $CRLF 'Show char

     ScanCode = MapVirtualKey(@pRawInput.data.Keyboard.vKey, 0) 'Create a scan code from vKey to get GetKeyNameText
     SELECT CASE @pRawInput.data.Keyboard.vKey
       CASE %VK_LEFT, %VK_UP, %VK_RIGHT, %VK_DOWN, %VK_PRIOR, %VK_NEXT, _
            %VK_END, %VK_HOME, %VK_INSERT, %VK_DELETE, %VK_DIVIDE, %VK_NUMLOCK
         ScanCode = ScanCode OR &H100 'Set extended bit
     END SELECT
     SHIFT LEFT ScanCode, 16 'Shift left
     GetKeyNameText(ScanCode, BYVAL VARPTR(zKeyName), SIZEOF(zKeyName)) 'Get key name like "Tab" or "Esc"
     sBuffer = sBuffer & "KeyName " & $DQ & zKeyName & $DQ & $CRLF

     sBuffer = sBuffer & $CRLF
     sBuffer = sBuffer & "RawInput.data.Keyboard.Message  =" & HEX$(@pRawInput.data.Keyboard.Message, 8) 'Show message
     SELECT CASE @pRawInput.data.Keyboard.Message
       CASE %WM_KEYDOWN    : sBuffer = sBuffer & " WM_KEYDOWN"    & $CRLF
       CASE %WM_KEYUP      : sBuffer = sBuffer & " WM_KEYUP"      & $CRLF
       CASE %WM_SYSKEYDOWN : sBuffer = sBuffer & " WM_SYSKEYDOWN" & $CRLF
       CASE %WM_SYSKEYDOWN : sBuffer = sBuffer & " WM_SYSKEYDOWN" & $CRLF
     END SELECT

     sBuffer = sBuffer & $CRLF
     sBuffer = sBuffer & "RawInput.Keyboard.MakeCode = " & HEX$(@pRawInput.data.Keyboard.MakeCode, 8) & $CRLF 'Show make code
     sBuffer = sBuffer & "RawInput.data.Keyboard.ExtraInformation = " & _
               HEX$(@pRawInput.data.Keyboard.ExtraInformation, 8) & $CRLF 'Show extra info
     IF (@pRawInput.data.Keyboard.Flags AND %RI_KEY_BREAK) THEN 'Show flags
       sBuffer = sBuffer & "Flag RI_KEY_BREAK" & $CRLF
     ELSE
       sBuffer = sBuffer & "Flag RI_KEY_MAKE" & $CRLF
     END IF
     IF (@pRawInput.data.Keyboard.Flags AND %RI_KEY_E0) THEN
       sBuffer = sBuffer & "Flag RI_KEY_E0" & $CRLF
     END IF
     IF (@pRawInput.data.Keyboard.Flags AND %RI_KEY_E1) THEN
       sBuffer = sBuffer & "Flag RI_KEY_E1" & $CRLF
     END IF
     IF (@pRawInput.data.Keyboard.Flags AND %RI_KEY_TERMSRV_SET_LED) THEN
       sBuffer = sBuffer & "Flag RI_KEY_TERMSRV_SET_LED" & $CRLF
     END IF
     IF (@pRawInput.data.Keyboard.Flags AND %RI_KEY_TERMSRV_SHADOW) THEN
       sBuffer = sBuffer & "Flag RI_KEY_TERMSRV_SHADOW" & $CRLF
     END IF

     SetDlgItemText(hDlg, %LabelInfo, BYVAL STRPTR(sBuffer))

   CASE %WM_CHAR
   CASE %WM_MOUSEMOVE
   CASE %WM_APPCOMMAND

   CASE %WM_APP
     SendDlgItemMessage(hDlg, %Edit, %EM_SETSEL, -2, -2) 'Move caret at the end
     Keybd_event(%VK_CONTROL, 0, 0, 0) 'Simulate Control key
     Keybd_event(%VK_CONTROL, 0, %KEYEVENTF_KEYUP, 0) 'Simulate Control key

   CASE %WM_COMMAND
     SELECT CASE CBCTL
       CASE %Edit
         IF HIWRD(CBWPARAM) = %EN_CHANGE THEN
         END IF
         IF (CBCTLMSG = %EN_KILLFOCUS) THEN
           SendMessage(CBLPARAM, %EM_GETSEL, VARPTR(SelStart), VARPTR(SelEnd))
         END IF
         IF (CBCTLMSG = %EN_SETFOCUS) THEN
           SendMessage(CBLPARAM, %EM_SETSEL, SelStart, SelEnd)
         END IF
     END SELECT

   CASE %WM_NCACTIVATE
     IF CBWPARAM = 0 THEN 'Application loose focus
       hFocusBak = GetFocus()
     ELSEIF hFocusBak THEN
       SendMessage(hDlg, %WM_NEXTDLGCTL, hFocusBak, 1)
       hFocusBak = 0
     END IF

  END SELECT

END FUNCTION
'______________________________________________________________________________

FUNCTION PBMAIN()
LOCAL hIconBig   AS DWORD
LOCAL hIconSmall AS DWORD

DIALOG FONT "Segoe UI", 9
DIALOG NEW %HWND_DESKTOP, "GetRawInputDevice / GetRawInputData", , , 230, 150, _
%WS_CAPTION OR %WS_MINIMIZEBOX OR %WS_SYSMENU, 0 TO hDlg

ExtractIconEx("msctf.dll", 15, BYVAL VARPTR(hIconBig), BYVAL VARPTR(hIconSmall), 1)
SetClassLong(hDlg, %GCL_HICONSM, hIconSmall)
SetClassLong(hDlg, %GCL_HICON, hIconBig)
SendMessage(hDlg, %WM_SETICON, %ICON_SMALL, hIconSmall)
SendMessage(hDlg, %WM_SETICON, %ICON_BIG, hIconBig)

CONTROL ADD TEXTBOX, hDlg, %Edit, "Type also in another app...", 5, 5, 220, 12

CONTROL ADD LABEL, hDlg, %LabelInfo, "GetRawInputDevice / GetRawInputData", 5, 20, 220, 125

DIALOG SHOW MODAL hDlg CALL DlgProc

DestroyIcon(hIconSmall)
DestroyIcon(hIconBig)

END FUNCTION
Reply
#2
A "union{}" is a mixed type in C. It is comparable to a *)"struct{}" in C with the difference that all variables share the same memory space; the memory space is determined by the compiler based on the longest variable.
*)A C-struct{} is comparable to Type ... End Type in Basic.

Code: (Select All)

union beispiel
{
  float preis;
  int anzahl;
} beispiel_fuer_zwei;

The "As Any" command in Visual Basic is used to remove the parameter type check when passing parameters. Simply put, it is a hint to the programmer that there is a type difference.

Code: (Select All)

Declare Function xyz ( . . .ByVal lpString As Any) As Integer
Reply
#3
I'm pretty sure in this context the `ByRef pData As Any` is just their way of expressing the `void *` type in C. That itself more or less just means it's a pointer to data but the type of data is not specified. As far as QB64 is concerned, you'd just replace it with a ByVal _OFFSET.

KernelPanic explained the Union thing, I'd say in practice what you'll need to do in QB64 is use a memory buffer and copy the data into variables of the right kind based on the dwType entry, so a fair amount more annoying (since QB64 doesn't have actual unions to use). I'd probably just use `_MEMNEW()` to create a memory block of the right size and pass that in as `pData`. Then you can use `_MEMGET` to copy the union section into separate variables of the proper TYPE after checking the `dwType` value, and go from there.
Reply
#4
Check the code in that "GPT just rewrote my code" thread. The code there handles MEM for any type.
Tread on those who tread on you

Reply
#5
(05-30-2024, 09:02 PM)Kernelpanic Wrote: A "union{}" is a mixed type in C. It is comparable to a *)"struct{}" in C with the difference that all variables share the same memory space; the memory space is determined by the compiler based on the longest variable.
*)A C-struct{} is comparable to Type ... End Type in Basic.
...
The "As Any" command in Visual Basic is used to remove the parameter type check when passing parameters. Simply put, it is a hint to the programmer that there is a type difference.

Thanks for explaining... I had some followup questions but I think DSMan195276 answered those...

(05-30-2024, 09:55 PM)DSMan195276 Wrote: I'm pretty sure in this context the `ByRef pData As Any` is just their way of expressing the `void *` type in C. That itself more or less just means it's a pointer to data but the type of data is not specified. As far as QB64 is concerned, you'd just replace it with a ByVal _OFFSET.

KernelPanic explained the Union thing, I'd say in practice what you'll need to do in QB64 is use a memory buffer and copy the data into variables of the right kind based on the dwType entry, so a fair amount more annoying (since QB64 doesn't have actual unions to use). I'd probably just use `_MEMNEW()` to create a memory block of the right size and pass that in as `pData`. Then you can use `_MEMGET` to copy the union section into separate variables of the proper TYPE after checking the `dwType` value, and go from there.

Thank you, that's probably what I need. I'll give those a try!

Thanks again to you both, I was feeling dead in the water for a bit!

(05-30-2024, 10:14 PM)SpriggsySpriggs Wrote: Check the code in that "GPT just rewrote my code" thread. The code there handles MEM for any type.
Perfect... Thanks Spriggsy
Reply
#6
An example of how unions and structures are used, and how much memory is used.

To explain again: In a union, the memory requirement is equal to the largest variable. All variables in a union have to share this memory, so one have to know exactly which one one want to use.
In a structure, each variable gets its own memory space.

Code: (Select All)

//union und struct Beispiel - 31. Mai 2024

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
union uni_beispiel
{
int i_zahl;
float f_zahl;
char c_zeichen;
}u_beispiel;

struct stu_beispiel
{
int i_stzahl;
float f_stzahl;
char c_stzeichen;
}st_beispiel;

u_beispiel.i_zahl = 12;
printf("\nUnion-Zahl: %3d", u_beispiel.i_zahl);

u_beispiel.f_zahl = 44.15;
printf("\nUnion-Fliesskommazahl: %5.2f", u_beispiel.f_zahl);

u_beispiel.c_zeichen = 'A';
printf("\nUnion-Zeichen: %c", u_beispiel.c_zeichen);

//Anzeigen wieviel Speicher union und struct belegen
printf("\n\nGroesse im Speicher von struct: %3d Bytes", sizeof(struct stu_beispiel));
printf("\nGroesse im Speicher der union : %3d Bytes", sizeof(union uni_beispiel));

return(0);
}

In both cases, access is done using dot notation, as with the type declaration in Basic.

Code: (Select All)

Dim Shared Motorrad As MotorradModell
"Modell : ", Motorrad.Modell

[Image: union-struct-Speicherverbrauch2024-05-31.jpg]
Reply
#7
(Yesterday, 01:16 PM)Kernelpanic Wrote: An example of how unions and structures are used, and how much memory is used.

To explain again: In a union, the memory requirement is equal to the largest variable.
All variables in a union have to share this memory, so one have to know exactly which one one want to use.
In a structure, each variable gets its own memory space.

Thank you for explaining. 
I'm planning to give Spriggsy's _MEMGET handler a try, which allocates memory depending on type.
Reply
#8
(05-30-2024, 09:55 PM)DSMan195276 Wrote: I'm pretty sure in this context the `ByRef pData As Any` is just their way of expressing the `void *` type in C. That itself more or less just means it's a pointer to data but the type of data is not specified. As far as QB64 is concerned, you'd just replace it with a ByVal _OFFSET.

KernelPanic explained the Union thing, I'd say in practice what you'll need to do in QB64 is use a memory buffer and copy the data into variables of the right kind based on the dwType entry, so a fair amount more annoying (since QB64 doesn't have actual unions to use). I'd probably just use `_MEMNEW()` to create a memory block of the right size and pass that in as `pData`. Then you can use `_MEMGET` to copy the union section into separate variables of the proper TYPE after checking the `dwType` value, and go from there.
I've had to do a bunch of cleanup, changing the types to QB64PE types and whatnot. 

I know the DWORD types are _UNSIGNED LONG, but there are Windows handles like hwndTarget  AS DWORD, and those I think should be  _OFFSET. So it's not just a 1:1 conversion. 

Then there's weird stuff like variables defined as ASCIIZ * 50. 
I looked up the ASCIIZ and it's just a string terminated with a NULL string (chr$(0)), which I've seen done in the mouse code that Spriggsy helped me with. 
But I don't know what the *50 is... Does that mean it's limited to just 50 characters long? 

Also there's one bRawData[1] AS BYTE. I know BYTE is  _UNSIGNED _BYTE, but what's the [1] about? 

I haven't even gotten to the crazy part yet, using _MEMGET to fake the UNION stuff using Spriggsy's code... 

It's double crazy because they have UNIONs within UNIONs, and also UNIONs of temporary types defined inline. 

Here's where I'm at so far...

Code: (Select All)
TYPE RID_DEVICE_INFO_MOUSE
    dwId                AS _UNSIGNED LONG
    dwNumberOfButtons   AS _UNSIGNED LONG
    dwSampleRate        AS _UNSIGNED LONG
    fHasHorizontalWheel AS INTEGER
END TYPE

TYPE RID_DEVICE_INFO_KEYBOARD
    dwType                 AS _UNSIGNED LONG ' DWORD
    dwSubType              AS _UNSIGNED LONG ' DWORD
    dwKeyboardMode         AS _UNSIGNED LONG ' DWORD
    dwNumberOfFunctionKeys AS _UNSIGNED LONG ' DWORD
    dwNumberOfIndicators   AS _UNSIGNED LONG ' DWORD
    dwNumberOfKeysTotal    AS _UNSIGNED LONG ' DWORD
END TYPE

TYPE RID_DEVICE_INFO_HID
    dwVendorId      AS _UNSIGNED LONG ' DWORD
    dwProductId     AS _UNSIGNED LONG ' DWORD
    dwVersionNumber AS _UNSIGNED LONG ' DWORD
    usUsagePage     AS _UNSIGNED INTEGER ' USHORT
    usUsage         AS _UNSIGNED INTEGER ' USHORT
END TYPE

UNION RID_DEVICE_INFO_UNION
    'mouse            AS RID_DEVICE_INFO_MOUSE
    dwId                AS _UNSIGNED LONG
    dwNumberOfButtons   AS _UNSIGNED LONG
    dwSampleRate        AS _UNSIGNED LONG
    fHasHorizontalWheel AS INTEGER
   
    'keyboard         AS RID_DEVICE_INFO_KEYBOARD
    dwType                 AS _UNSIGNED LONG ' DWORD
    dwSubType              AS _UNSIGNED LONG ' DWORD
    dwKeyboardMode         AS _UNSIGNED LONG ' DWORD
    dwNumberOfFunctionKeys AS _UNSIGNED LONG ' DWORD
    dwNumberOfIndicators   AS _UNSIGNED LONG ' DWORD
    dwNumberOfKeysTotal    AS _UNSIGNED LONG ' DWORD
   
    'hid              AS RID_DEVICE_INFO_HID
    dwVendorId      AS _UNSIGNED LONG ' DWORD
    dwProductId     AS _UNSIGNED LONG ' DWORD
    dwVersionNumber AS _UNSIGNED LONG ' DWORD
    usUsagePage     AS _UNSIGNED INTEGER ' USHORT
    usUsage         AS _UNSIGNED INTEGER ' USHORT
END UNION

TYPE RID_DEVICE_INFO
    cbSize AS _UNSIGNED LONG ' DWORD
    dwType AS _UNSIGNED LONG ' DWORD
   
    'RID_DEVICE_INFO_UNION
    'mouse            AS RID_DEVICE_INFO_MOUSE
    dwId                AS _UNSIGNED LONG
    dwNumberOfButtons   AS _UNSIGNED LONG
    dwSampleRate        AS _UNSIGNED LONG
    fHasHorizontalWheel AS INTEGER
   
    'keyboard         AS RID_DEVICE_INFO_KEYBOARD
    dwType                 AS _UNSIGNED LONG ' DWORD
    dwSubType              AS _UNSIGNED LONG ' DWORD
    dwKeyboardMode         AS _UNSIGNED LONG ' DWORD
    dwNumberOfFunctionKeys AS _UNSIGNED LONG ' DWORD
    dwNumberOfIndicators   AS _UNSIGNED LONG ' DWORD
    dwNumberOfKeysTotal    AS _UNSIGNED LONG ' DWORD
   
    'hid              AS RID_DEVICE_INFO_HID
    dwVendorId      AS _UNSIGNED LONG ' DWORD
    dwProductId     AS _UNSIGNED LONG ' DWORD
    dwVersionNumber AS _UNSIGNED LONG ' DWORD
    usUsagePage     AS _UNSIGNED INTEGER ' USHORT
    usUsage         AS _UNSIGNED INTEGER ' USHORT
END TYPE

TYPE RAWKEYBOARD
    MakeCode         AS _UNSIGNED INTEGER ' WORD
    Flags            AS _UNSIGNED INTEGER ' WORD
    Reserved         AS _UNSIGNED INTEGER ' WORD
    VKey             AS _UNSIGNED INTEGER ' WORD
    Message          AS _UNSIGNED LONG ' DWORD
    ExtraInformation AS _UNSIGNED LONG ' DWORD
END TYPE

TYPE DUMMYSTRUCTNAME
    usButtonFlags AS _UNSIGNED INTEGER ' USHORT
    usButtonData AS _UNSIGNED INTEGER ' USHORT
END TYPE

TYPE DUMMYUNIONNAME
    ulButtons AS _UNSIGNED _OFFSET ' ULONG
    UNION
    DUMMYSTRUCTNAME
END TYPE

TYPE RAWMOUSE
    usFlags AS _UNSIGNED INTEGER ' USHORT
    UNION
    DUMMYUNIONNAME
    ulRawButtons AS _UNSIGNED _OFFSET ' ULONG
    lLastX AS LONG
    lLastY AS LONG
    ulExtraInformation AS _UNSIGNED _OFFSET ' ULONG
END TYPE

TYPE RAWKEYBOARD
    MakeCode AS _UNSIGNED INTEGER ' USHORT
    Flags AS _UNSIGNED INTEGER ' USHORT
    Reserved AS _UNSIGNED INTEGER ' USHORT
    VKey AS _UNSIGNED INTEGER ' USHORT
    Message AS _UNSIGNED LONG ' UINT
    ExtraInformation AS _UNSIGNED _OFFSET ' ULONG
END TYPE

TYPE RAWHID
    dwSizeHid AS _UNSIGNED LONG ' DWORD
    dwCount AS _UNSIGNED LONG ' DWORD
    bRawData AS _UNSIGNED _BYTE ' bRawData[1] AS BYTE
END TYPE

TYPE RAWINPUTHEADER
    dwType  AS _UNSIGNED LONG ' DWORD
    dwSize  AS _UNSIGNED LONG ' DWORD
    hDevice AS _UNSIGNED LONG ' DWORD <- should this be _OFFSET ?
    wParam  AS LONG
END TYPE

UNION RAWINPUTUNION
    mouse     AS RAWMOUSE
    keyboard  AS RAWKEYBOARD
    hid       AS RAWHID
END UNION

TYPE RAWINPUT
    header AS RAWINPUTHEADER
    data   AS RAWINPUTUNION
END TYPE

TYPE RAWINPUTDEVICELIST
    hDevice AS _UNSIGNED LONG ' DWORD <- should this be _OFFSET ?
    dwType  AS _UNSIGNED LONG ' DWORD
END TYPE

TYPE RAWINPUTDEVICE
    usUsagePage AS _UNSIGNED INTEGER ' WORD
    usUsage     AS _UNSIGNED INTEGER ' WORD
    dwFlags     AS _UNSIGNED LONG ' DWORD
    hwndTarget  AS _OFFSET ' DWORD
END TYPE

' ================================================================================================================================================================
' BEGIN CONSTANTS
' ================================================================================================================================================================
CONST COLOR_WINDOW = 5
CONST CS_HREDRAW = &H0002
CONST CS_VREDRAW = &H0001
CONST CW_USEDEFAULT = &H80000000
CONST DT_CENTER = &H00000001
CONST Edit = 101
CONST EM_GETSEL = &H00B0
CONST EM_SETSEL = &H00B1
CONST EN_CHANGE = &H0300
CONST EN_KILLFOCUS = &H0200
CONST EN_SETFOCUS = &H0100
CONST GCL_HICON = -14
CONST GCL_HICONSM = -34
CONST Hid_Bottom = 66
CONST Hid_Left = 33
CONST Hid_Right = 34
CONST HWND_DESKTOP = 0
CONST ICON_BIG = 1
CONST ICON_SMALL = 0
CONST IDC_ARROW = 32512
CONST IDI_APPLICATION = 32512
CONST KEYEVENTF_KEYUP = &H0002
CONST KL_NAMELENGTH = 9
CONST LabelInfo = 201
CONST MOUSE_ATTRIBUTES_CHANGED = &H04
CONST MOUSE_MOVE_ABSOLUTE = &H01
CONST MOUSE_MOVE_NOCOALESCE = &H08
CONST MOUSE_MOVE_RELATIVE = &H00
CONST MOUSE_VIRTUAL_DESKTOP = &H02
CONST NULL = 0
CONST RI_KEY_BREAK = 1
CONST RI_KEY_E0 = 2
CONST RI_KEY_E1 = 4
CONST RI_KEY_MAKE = 0
CONST RI_KEY_TERMSRV_SET_LED = 8
CONST RI_KEY_TERMSRV_SHADOW = &H10
CONST RID_INPUT = &H10000003
CONST RIDEV_EXINPUTSINK = &H00001000
CONST RIDI_DEVICEINFO = &H2000000B
CONST RIM_TYPEHID = 2
CONST RIM_TYPEKEYBOARD = 1
CONST RIM_TYPEMOUSE = 0
CONST SIZE_MINIMIZED = 1
CONST SW_SHOW = 5
CONST VK_CONTROL = &H11
CONST VK_DELETE = &H2E
CONST VK_DIVIDE = &H6F
CONST VK_DOWN = &H28
CONST VK_END = &H23
CONST VK_HOME = &H24
CONST VK_INSERT = &H2D
CONST VK_LEFT = &H25
CONST VK_NEXT = &H22
CONST VK_NUMLOCK = &H90
CONST VK_PRIOR = &H21
CONST VK_RIGHT = &H27
CONST VK_SCROLL = &H91
CONST VK_UP = &H26
CONST WM_APP = &H08000
CONST WM_APPCOMMAND = &H0319
CONST WM_CHAR = &H0102
CONST WM_CHAR = &H0102
CONST WM_CHAR = &H0102
CONST WM_COMMAND = &H0111
CONST WM_DEADCHAR = &H0103
CONST WM_DEADCHAR = &H0103
CONST WM_DESTROY = &H0002
CONST WM_INITDIALOG = &H0110
CONST WM_INPUT = &H00FF
CONST WM_KEYDOWN = &H0100
CONST WM_KEYUP = &H0101
CONST WM_KEYUP = &H0101
CONST WM_MOUSEMOVE = &H0200
CONST WM_NCACTIVATE = &H0086
CONST WM_NEXTDLGCTL = &H28
CONST WM_PAINT = &H000F
CONST WM_SETICON = &H0080
CONST WM_SIZE = &H0005
CONST WM_SYSCHAR = &H0106
CONST WM_SYSCHAR = &H0106
CONST WM_SYSDEADCHAR = &H0107
CONST WM_SYSDEADCHAR = &H0107
CONST WM_SYSKEYDOWN = &H0104
CONST WM_SYSKEYDOWN = &H0104
CONST WM_SYSKEYUP = &H0105
CONST WM_SYSKEYUP = &H0105
CONST WM_UNICHAR = &H0109
CONST WS_CAPTION = &H00C00000
CONST WS_CHILD = &H40000000
CONST WS_MAXIMIZEBOX = &H00010000
CONST WS_MINIMIZEBOX = &H00020000
CONST WS_OVERLAPPED = &H00000000
CONST WS_OVERLAPPEDWINDOW = WS_OVERLAPPED Or WS_CAPTION Or WS_SYSMENU Or WS_THICKFRAME Or WS_MINIMIZEBOX Or WS_MAXIMIZEBOX
CONST WS_SYSMENU = &H00080000
CONST WS_THICKFRAME = &H00040000
CONST WS_VISIBLE = &H10000000

' ================================================================================================================================================================
' END CONSTANTS
' ================================================================================================================================================================

' Thank to José Roca

DECLARE FUNCTION RegisterRawInputDevices LIB "USER32.DLL" ALIAS "RegisterRawInputDevices"( _
    BYREF pRawInputDevices AS RAWINPUTDEVICE, _
    BYVAL uiNumDevices AS _UNSIGNED LONG, _ ' DWORD
    BYVAL cbSize AS _UNSIGNED LONG _  ' DWORD
    ) AS LONG

DECLARE FUNCTION GetRawInputDeviceList LIB "USER32.DLL" ALIAS "GetRawInputDeviceList"( _
    BYREF pRawInputDeviceList AS RAWINPUTDEVICELIST, _
    BYREF puiNumDevices AS _UNSIGNED LONG, _ ' DWORD
    BYVAL cbSize AS _UNSIGNED LONG _  ' DWORD
    ) AS _UNSIGNED LONG ' DWORD

DECLARE FUNCTION GetRawInputDeviceInfo LIB "USER32.DLL" ALIAS "GetRawInputDeviceInfoA"( _
    BYVAL hDevice AS _UNSIGNED LONG, _ ' DWORD <- should this be _OFFSET?
    BYVAL uiCommand AS _UNSIGNED LONG, _ ' DWORD
    BYREF pData AS _OFFSET, _ ' ANY
    BYREF pcbSize AS _UNSIGNED LONG _ ' DWORD
    ) AS _UNSIGNED LONG ' DWORD

DECLARE FUNCTION SendDlgItemMessage LIB "USER32.DLL" ALIAS "SendDlgItemMessageA"( _
    BYVAL hWnd AS _OFFSET, _ ' DWORD
    BYVAL nIDDlgItem AS LONG, _
    BYVAL Msg AS _UNSIGNED LONG, _ ' DWORD
    BYVAL wParam AS _UNSIGNED LONG, _ ' DWORD
    BYVAL lParam AS LONG _
    ) AS LONG

DECLARE FUNCTION GetRawInputData LIB "USER32.DLL" ALIAS "GetRawInputData"( _
    BYVAL hRawInput AS _UNSIGNED LONG, _ ' DWORD
    BYVAL uiCommand AS _UNSIGNED LONG, _ ' DWORD
    BYREF pData AS _OFFSET, _ ' ANY
    BYREF pcbSize AS _UNSIGNED LONG, _ ' DWORD
    BYVAL cbSizeHeader AS _UNSIGNED LONG _ ' DWORD
    ) AS _UNSIGNED LONG ' DWORD

DECLARE FUNCTION GetKeyNameText LIB "USER32.DLL" ALIAS "GetKeyNameTextA"( _
    BYVAL lParam AS LONG, _
    BYREF lpString AS STRING, _ ' ASCIIZ = NULL-terminated string
    BYVAL cchSize AS _UNSIGNED LONG _ ' DWORD
    ) AS LONG

DECLARE FUNCTION MapVirtualKey LIB "USER32.DLL" ALIAS "MapVirtualKeyA"( _
    BYVAL uCode AS _UNSIGNED LONG, _ ' DWORD
    BYVAL uMapType AS _UNSIGNED LONG _ ' DWORD
    ) AS _UNSIGNED LONG ' DWORD

DECLARE FUNCTION GetFocus LIB "USER32.DLL" ALIAS "GetFocus"( _
    ) AS _UNSIGNED LONG ' DWORD

DECLARE FUNCTION GetDlgItem LIB "USER32.DLL" ALIAS "GetDlgItem"( _
    BYVAL HWND AS _OFFSET, _ ' DWORD
    BYVAL nIDDlgItem AS LONG _
    ) AS _UNSIGNED LONG ' DWORD

DECLARE FUNCTION SendMessage LIB "USER32.DLL" ALIAS "SendMessageA"( _
    BYVAL hWnd AS _OFFSET, _ ' DWORD
    BYVAL Msg AS _UNSIGNED LONG, _ ' DWORD
    BYVAL wParam AS _UNSIGNED LONG, _ ' DWORD
    BYVAL lParam AS LONG _
    ) AS LONG

DECLARE FUNCTION DestroyIcon LIB "USER32.DLL" ALIAS "DestroyIcon"( _
    BYVAL hIcon AS _UNSIGNED LONG _ ' DWORD
    ) AS LONG

DECLARE FUNCTION PostMessage LIB "USER32.DLL" ALIAS "PostMessageA"( _
    BYVAL hWnd AS _OFFSET, _ ' DWORD
    BYVAL Msg AS _UNSIGNED LONG, _ ' DWORD
    BYVAL wParam AS _UNSIGNED LONG, _ ' DWORD
    BYVAL lParam AS LONG _
    ) AS LONG

DECLARE FUNCTION SetClassLong LIB "USER32.DLL" ALIAS "SetClassLongA"( _
    BYVAL hWnd AS _OFFSET, _ ' DWORD
    BYVAL nIndex AS LONG, _
    BYVAL dwNewLong AS _UNSIGNED LONG _ ' DWORD
    ) AS DWORD

DECLARE FUNCTION ExtractIconEx LIB "SHELL32.DLL" ALIAS "ExtractIconExA"( _
    BYREF lpszFile AS STRING, _ ' ASCIIZ = NULL-terminated string
    BYVAL nIconIndex AS LONG, _
    BYREF phiconLarge AS _UNSIGNED LONG, _ ' DWORD
    BYREF phiconSmall AS _UNSIGNED LONG, _ ' DWORD
    BYVAL nIcons AS _UNSIGNED LONG _ ' DWORD
    ) AS _UNSIGNED LONG ' DWORD

DECLARE FUNCTION SetDlgItemText LIB "USER32.DLL" ALIAS "SetDlgItemTextA"( _
    BYVAL hDlg AS LONG, _
    BYVAL nIDDlgItem AS LONG, _
    lpString AS STRING _ ' ASCIIZ = NULL-terminated string
    ) AS LONG

DECLARE SUB Keybd_event LIB "USER32.DLL" ALIAS "keybd_event"( _
    BYVAL bVk AS BYTE, _
    BYVAL bScan AS BYTE, _
    BYVAL dwFlags AS _UNSIGNED LONG, _ ' DWORD
    BYVAL dwExtraInfo AS _UNSIGNED LONG _ ' DWORD
    )

GLOBAL hDlg AS _UNSIGNED LONG ' DWORD

CALLBACK FUNCTION DlgProc
    LOCAL  RidDeviceInfo       AS RID_DEVICE_INFO
    LOCAL  pRawInput           AS RAWINPUT POINTER
    LOCAL  zKeyName            AS STRING ' ASCIIZ * 50 = NULL-terminated string
    STATIC CtrlClass           AS STRING ' ASCIIZ * 50 = NULL-terminated string
    LOCAL  sRawInput           AS STRING
    LOCAL  sBuffer             AS STRING
    LOCAL  ScanCode            AS _UNSIGNED LONG ' DWORD
    STATIC hidDevice           AS _UNSIGNED LONG ' DWORD
    STATIC hFocusBak           AS _UNSIGNED LONG ' DWORD
    LOCAL  RawInputDevCount    AS LONG
    LOCAL  KeyboardTypeCount   AS LONG
    LOCAL  RawInputDeviceIndex AS LONG
    STATIC hidF9               AS LONG
    LOCAL  ByteCount           AS LONG
    STATIC SelStart            AS LONG
    STATIC SelEnd              AS LONG
   
    SELECT CASE CBMSG
   
        CASE %WM_INITDIALOG
            GetRawInputDeviceList(BYVAL %NULL, RawInputDevCount, SIZEOF(RAWINPUTDEVICELIST)) 'Get raw input device count
            DIM RawInputDevList(0 TO RawInputDevCount - 1) AS RAWINPUTDEVICELIST 'Prepare raw input device array
            GetRawInputDeviceList(RawInputDevList(0), RawInputDevCount, SIZEOF(RAWINPUTDEVICELIST)) 'Get raw input device
           
            DIM RawInputDev(RawInputDevCount) AS RAWINPUTDEVICE 'Prepare raw input device array
            FOR RawInputDeviceIndex = 0 TO RawInputDevCount - 1
                GetRawInputDeviceInfo(RawInputDevList(RawInputDeviceIndex).hDevice, %RIDI_DEVICEINFO, RidDeviceInfo, SIZEOF(RID_DEVICE_INFO)) 'Get raw input device info
                SELECT CASE RidDeviceInfo.dwtype 'Get raw input device type
                    CASE %RIM_TYPEKEYBOARD 'Keyboard type
                        RawInputDev(KeyboardTypeCount).usUsagePage = 1
                        RawInputDev(KeyboardTypeCount).usUsage     = 6
                        RawInputDev(KeyboardTypeCount).dwFlags     = %RIDEV_EXINPUTSINK 'Vista+, receive input in the background
                        RawInputDev(KeyboardTypeCount).hwndTarget  = hDlg
                        INCR KeyboardTypeCount 'Count of raw keyboard input device
                       
                    CASE %RIM_TYPEMOUSE 'Mouse raw input device
                    CASE %RIM_TYPEHID 'Other raw input device, game controllers, joysticks, etc.
                END SELECT
            NEXT
            RegisterRawInputDevices(RawInputDev(0), KeyboardTypeCount, SIZEOF(RAWINPUTDEVICE)) 'Register raw input device(s)
            PostMessage(hDlg, %WM_APP, 0, 0)
           
        CASE %WM_INPUT 'Sent to the window that is getting raw input
            GetRawInputData(CBLPARAM, %RID_INPUT, BYVAL %NULL, ByteCount, SIZEOF(RAWINPUTHEADER)) 'Get size of raw input buffer
            sRawInput = NUL$(ByteCount) 'Set string for hid input
            GetRawInputData(CBLPARAM, %RID_INPUT, BYVAL STRPTR(sRawInput), ByteCount, SIZEOF(RAWINPUTHEADER))'Get hid input
            pRawInput = STRPTR(sRawInput) 'Set RawInput pointer
            sBuffer = "RawInput.Header.hDevice = " & HEX$(@pRawInput.header.hDevice, 8) & chr$(13) & chr$(10) ' $CRLF 'Show handle
            sBuffer = sBuffer & "RawInput.Header.dwType = " & CHOOSE$(@pRawInput.header.dwType + 1, _
                "RIM_TYPEMOUSE", "RIM_TYPEKEYBOARD", "RIM_TYPEHID") & chr$(13) & chr$(10) ' $CRLF 'Show type
            sBuffer = sBuffer & chr$(13) & chr$(10) ' $CRLF
            sBuffer = sBuffer & "RawInput.data.Keyboard.vKey =" & STR$(@pRawInput.data.Keyboard.vKey) & _ '
                ", Character is " & $DQ & CHR$(@pRawInput.data.Keyboard.vKey) & $DQ & chr$(13) & chr$(10) & chr$(13) & chr$(10) ' $CRLF & $CRLF 'Show char
            ScanCode = MapVirtualKey(@pRawInput.data.Keyboard.vKey, 0) 'Create a scan code from vKey to get GetKeyNameText
           
            SELECT CASE @pRawInput.data.Keyboard.vKey
                CASE %VK_LEFT, %VK_UP, %VK_RIGHT, %VK_DOWN, %VK_PRIOR, %VK_NEXT, _
                    %VK_END, %VK_HOME, %VK_INSERT, %VK_DELETE, %VK_DIVIDE, %VK_NUMLOCK
                    ScanCode = ScanCode OR &H100 'Set extended bit
            END SELECT
            SHIFT LEFT ScanCode, 16 'Shift left
            GetKeyNameText(ScanCode, BYVAL VARPTR(zKeyName), SIZEOF(zKeyName)) 'Get key name like "Tab" or "Esc"
            sBuffer = sBuffer & "KeyName " & $DQ & zKeyName & $DQ & chr$(13) & chr$(10) ' $CRLF
            sBuffer = sBuffer & chr$(13) & chr$(10) ' $CRLF
            sBuffer = sBuffer & "RawInput.data.Keyboard.Message  =" & HEX$(@pRawInput.data.Keyboard.Message, 8) 'Show message
            SELECT CASE @pRawInput.data.Keyboard.Message
                CASE %WM_KEYDOWN    : sBuffer = sBuffer & " WM_KEYDOWN"    & chr$(13) & chr$(10) ' $CRLF
                CASE %WM_KEYUP      : sBuffer = sBuffer & " WM_KEYUP"      & chr$(13) & chr$(10) ' $CRLF
                CASE %WM_SYSKEYDOWN : sBuffer = sBuffer & " WM_SYSKEYDOWN" & chr$(13) & chr$(10) ' $CRLF
                CASE %WM_SYSKEYDOWN : sBuffer = sBuffer & " WM_SYSKEYDOWN" & chr$(13) & chr$(10) ' $CRLF
            END SELECT
            sBuffer = sBuffer & chr$(13) & chr$(10) ' $CRLF
            sBuffer = sBuffer & "RawInput.Keyboard.MakeCode = " & HEX$(@pRawInput.data.Keyboard.MakeCode, 8) & chr$(13) & chr$(10) ' $CRLF 'Show make code
            sBuffer = sBuffer & "RawInput.data.Keyboard.ExtraInformation = " & _
            HEX$(@pRawInput.data.Keyboard.ExtraInformation, 8) & chr$(13) & chr$(10) ' $CRLF 'Show extra info
            IF (@pRawInput.data.Keyboard.Flags AND %RI_KEY_BREAK) THEN 'Show flags
                sBuffer = sBuffer & "Flag RI_KEY_BREAK" & chr$(13) & chr$(10) ' $CRLF
            ELSE
                sBuffer = sBuffer & "Flag RI_KEY_MAKE" & chr$(13) & chr$(10) ' $CRLF
            END IF
            IF (@pRawInput.data.Keyboard.Flags AND %RI_KEY_E0) THEN
                sBuffer = sBuffer & "Flag RI_KEY_E0" & chr$(13) & chr$(10) ' $CRLF
            END IF
            IF (@pRawInput.data.Keyboard.Flags AND %RI_KEY_E1) THEN
                sBuffer = sBuffer & "Flag RI_KEY_E1" & chr$(13) & chr$(10) ' $CRLF
            END IF
            IF (@pRawInput.data.Keyboard.Flags AND %RI_KEY_TERMSRV_SET_LED) THEN
                sBuffer = sBuffer & "Flag RI_KEY_TERMSRV_SET_LED" & chr$(13) & chr$(10) ' $CRLF
            END IF
            IF (@pRawInput.data.Keyboard.Flags AND %RI_KEY_TERMSRV_SHADOW) THEN
                sBuffer = sBuffer & "Flag RI_KEY_TERMSRV_SHADOW" & chr$(13) & chr$(10) ' $CRLF
            END IF
           
            SetDlgItemText(hDlg, %LabelInfo, BYVAL STRPTR(sBuffer))
           
        CASE %WM_CHAR
        CASE %WM_MOUSEMOVE
        CASE %WM_APPCOMMAND
       
        CASE %WM_APP
            SendDlgItemMessage(hDlg, %Edit, %EM_SETSEL, -2, -2) 'Move caret at the end
            Keybd_event(%VK_CONTROL, 0, 0, 0) 'Simulate Control key
            Keybd_event(%VK_CONTROL, 0, %KEYEVENTF_KEYUP, 0) 'Simulate Control key
           
        CASE %WM_COMMAND
            SELECT CASE CBCTL
                CASE %Edit
                    IF HIWRD(CBWPARAM) = %EN_CHANGE THEN
                    END IF
                    IF (CBCTLMSG = %EN_KILLFOCUS) THEN
                        SendMessage(CBLPARAM, %EM_GETSEL, VARPTR(SelStart), VARPTR(SelEnd))
                    END IF
                    IF (CBCTLMSG = %EN_SETFOCUS) THEN
                        SendMessage(CBLPARAM, %EM_SETSEL, SelStart, SelEnd)
                    END IF
            END SELECT
           
        CASE %WM_NCACTIVATE
            IF CBWPARAM = 0 THEN 'Application loose focus
                hFocusBak = GetFocus()
            ELSEIF hFocusBak THEN
                SendMessage(hDlg, %WM_NEXTDLGCTL, hFocusBak, 1)
                hFocusBak = 0
            END IF
    END SELECT
   
END FUNCTION


FUNCTION PBMAIN()
    LOCAL hIconBig   AS _UNSIGNED LONG
    LOCAL hIconSmall AS _UNSIGNED LONG
   
    ' Dialog Boxes 03/12/2023
    ' https://learn.microsoft.com/en-us/windows/win32/api/_dlgbox/
    DIALOG FONT "Segoe UI", 9
   
    DIALOG NEW %HWND_DESKTOP, "GetRawInputDevice / GetRawInputData", , , 230, 150, _
    %WS_CAPTION OR %WS_MINIMIZEBOX OR %WS_SYSMENU, 0 TO hDlg
   
    ExtractIconEx("msctf.dll", 15, BYVAL VARPTR(hIconBig), BYVAL VARPTR(hIconSmall), 1)
    SetClassLong(hDlg, %GCL_HICONSM, hIconSmall)
    SetClassLong(hDlg, %GCL_HICON, hIconBig)
    SendMessage(hDlg, %WM_SETICON, %ICON_SMALL, hIconSmall)
    SendMessage(hDlg, %WM_SETICON, %ICON_BIG, hIconBig)
   
    CONTROL ADD TEXTBOX, hDlg, %Edit, "Type also in another app...", 5, 5, 220, 12
   
    CONTROL ADD LABEL, hDlg, %LabelInfo, "GetRawInputDevice / GetRawInputData", 5, 20, 220, 125
   
    DIALOG SHOW MODAL hDlg CALL DlgProc
   
    DestroyIcon(hIconSmall)
    DestroyIcon(hIconBig)
END FUNCTION ' PBMAIN
Reply
#9
I don't have tons of time at the moment, but to address a couple of the things you mentioned:

You're correct about the `_OFFSET` type. The member you identified (`hDevice`) is a `HANDLE` type which is the same size as a pointer.

Yes `ASCIIZ * 50` would be a 50 byte string, you'll need to check if the 50 includes the NUL byte at the end or not (if it does, then you can use `STRING * 50` for it, if the NUL byte is extra then it's a `STRING * 51`).

The ` bRawData[1]` thing is hard to explain, but basically it's a C concept where the size of the TYPE/struct is not really defined, instead there's an array of unknown length at the end of it. If you read the specs, it says that the size of the `bRawData` array is `dwSizeHid * dwCount`. I assume you then split the `bRawData` bytes up into `dwSizeHid`-sized chunks and each is a HID report or something.

Also, something to keep in mind is the C padding rules. Each type has a required alignment and the C compiler will insert empty space between the members of the struct to make sure each member hits its required alignment. QB64 does not do this, so you have to manually insert extra members for the padding. The majority of those structures are designed such that they don't need any padding, but maybe not all of them.
Reply
#10
(11 hours ago)DSMan195276 Wrote: I don't have tons of time at the moment, but to address a couple of the things you mentioned:

You're correct about the `_OFFSET` type. The member you identified (`hDevice`) is a `HANDLE` type which is the same size as a pointer.

Yes `ASCIIZ * 50` would be a 50 byte string, you'll need to check if the 50 includes the NUL byte at the end or not (if it does, then you can use `STRING * 50` for it, if the NUL byte is extra then it's a `STRING * 51`).

The ` bRawData[1]` thing is hard to explain, but basically it's a C concept where the size of the TYPE/struct is not really defined, instead there's an array of unknown length at the end of it. If you read the specs, it says that the size of the `bRawData` array is `dwSizeHid * dwCount`. I assume you then split the `bRawData` bytes up into `dwSizeHid`-sized chunks and each is a HID report or something.

Also, something to keep in mind is the C padding rules. Each type has a required alignment and the C compiler will insert empty space between the members of the struct to make sure each member hits its required alignment. QB64 does not do this, so you have to manually insert extra members for the padding. The majority of those structures are designed such that they don't need any padding, but maybe not all of them.
Thanks so much for taking the time to explain all that. All I can say is, Good God! Why does it have to be so complicated!? There's a reason I prefer BASIC! But I do appreciate your taking the time to include all those details - it's a big help.
Thanks again!
Reply




Users browsing this thread: 1 Guest(s)