lxn / win

A Windows API wrapper package for the Go Programming Language
Other
1.19k stars 312 forks source link

Issue with field alignment padding of the RAWMOUSE struct #106

Open nunomluz opened 4 years ago

nunomluz commented 4 years ago

While using GetRawInputData, I've stumbled upon an issue where the value of UsButtonFlags is apparently placed inside UsButtonData.

After looking at the code, I noticed the Pad_cgo_0 field and got to read a bit about the beautiful world of field alignment in C. I still don't know much about it but here's what worked in my case:

type MYRAWMOUSE struct {
    UsFlags            uint16
    Pad_cgo_0          [2]byte
    UsButtonFlags      uint16
    UsButtonData       uint16
    UlRawButtons       uint32
    LLastX             int32
    LLastY             int32
    UlExtraInformation uint32
}

By just moving that padding above UsButtonFlags it seems to work just fine, even for the info in LLastX and LLastY. I was wondering about creating a PR but then noticed this might work differently depending on the target architecture. Can you please give me your thoughts on this? Is it safe to move that padding field, or will I have to look out for its position depending on my compiler target?

EDIT: I did some more investigation on this and here's an interesting read on the subject: http://www.catb.org/esr/structure-packing/

I've also checked the paddings using this code:

/*
#include <windows.h>
*/
import "C"
import (
    "github.com/davecgh/go-spew/spew"
)

func main() {
    var test C.RAWMOUSE
    spew.Dump(test)
}

Built for both 32bit and 64bit Windows:

set GOARCH=386 && go build -o ./build/Debug/test_offset_386.exe ./cmd/testoffset
set GOARCH=amd64 && go build -o ./build/Debug/test_offset_amd64.exe ./cmd/testoffset

Here's the output for 32bit:

$ ./build/Debug/test_offset_386.exe
(main._Ctype_struct_tagRAWMOUSE) {
 usFlags: (main._Ctype_ushort) 0,
 _: ([2]uint8) (len=2 cap=2) {
  00000000  00 00                                             |..|
 },
 anon0: ([4]uint8) (len=4 cap=4) {
  00000000  00 00 00 00                                       |....|
 },
 ulRawButtons: (main._Ctype_ulong) 0,
 lLastX: (main._Ctype_long) 0,
 lLastY: (main._Ctype_long) 0,
 ulExtraInformation: (main._Ctype_ulong) 0
}

Here's the output for 64bit:

$ ./build/Debug/test_offset_amd64.exe
(main._Ctype_struct_tagRAWMOUSE) {
 usFlags: (main._Ctype_ushort) 0,
 _: ([2]uint8) (len=2 cap=2) {
  00000000  00 00                                             |..|
 },
 anon0: ([4]uint8) (len=4 cap=4) {
  00000000  00 00 00 00                                       |....|
 },
 ulRawButtons: (main._Ctype_ulong) 0,
 lLastX: (main._Ctype_long) 0,
 lLastY: (main._Ctype_long) 0,
 ulExtraInformation: (main._Ctype_ulong) 0
}

You might need to check the RAWMOUSE struct and the Windows data type docs to make some sense of the above output. The output is pretty much the same and the padding is set above the usButtonFlags+usButtonData inner struct. Maybe I need an actual 32bit machine for it to be different (not sure). I wonder if the current Golang structure in this repo is prepared for 32bit builds since the commit was made in July 2012, or if this has been a bug all along 🤔