Windows "syscalls" and Go pointers

586 views
Skip to first unread message

Constantine Shablya

unread,
Oct 25, 2020, 10:23:13 PM10/25/20
to golang-nuts
Hello,

I wish to call some Windows functions, some of which take pointers to types
which themselves contain pointers. For this purpose I intended to use
golang.org/x/sys/windows/mkwinsyscall.go and not cgo; in past I have implemented
a package that uses WASAPI by generating "syscall" bodies with mkwinsyscall.go
and doing syscall.SyscallN to call virtual functions in COM objects and so have
some prior experience.

Now I want to call RegisterClassW which takes a pointer to WNDCLASSW as its
parameter. One of the members of WNDCLASSW is lpszClassName of LPCWSTR type
(UTF-16 string). This puzzled me as to how I should approach allocating storage
for that string? Taking into account Go specification making special
reservations for pointers passed to cgo calls
intended to facilitate possible implementations of moving garbage collectors in
future) I thought I would have to allocate storage with, say, C.malloc, but that
would require using cgo which so far I have not had to use. I looked into how Go
standard library handles this, and I found crypto/x509/root_windows.go
checkChainSSLServerPolicy
build a chain of pointers and then pass it to
syscall.CertVerifyCertificateChainPolicy, which is in violation of the cgo
passing pointers requirement. Although this particular case does not use cgo, I
believe pointer passing restriction would still apply to it.

All of this made me puzzled, should I store a pointer returned by
syscall.UTF16PtrFromString (golang.org/x/sys/windows.UTF16PtrFromString) or use
C.malloc+copy? Or, if the answer isn't straightforward, is it fair to think that
use of pointer returned by UTF16PtrFromString is more likely to break with newer
versions of go than C.malloc+copy or is it expected that there will be some
amendments made to that function when a hypothetical moving GC implementation
lands?

Constantine Shablya

unread,
Oct 26, 2020, 9:10:53 AM10/26/20
to golang-nuts
Amending question above: is it ok for a function that takes pointer to a pointer
(so f(x *unsafe.Pointer) or f(**T)) to write a pointer (*x = unsafe.Pointer(...)
or *x = (*T)(...)) or should it store an uintptr (as in f(x *uintptr)) and that
uintptr then be cast to a pointer to appropriate type?

peterGo

unread,
Oct 26, 2020, 7:03:36 PM10/26/20
to golang-nuts
"I want to call RegisterClassW which takes a pointer to WNDCLASSW as its parameter. One of the members of WNDCLASSW is lpszClassName of LPCWSTR type (UTF-16 string). This puzzled me as to how I should approach allocating storage for that string?"


Win32 API:

RegisterClassW function:
https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-registerclassw

Registers a window class for subsequent use in calls to the CreateWindow or CreateWindowEx function.

Note:  The RegisterClass function has been superseded by the RegisterClassEx function.

RegisterClassExW function:
https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-registerclassexw

Registers a window class for subsequent use in calls to the CreateWindow or CreateWindowEx function.


For RegisterClassExW you can take a look at the Go source code:
https://go.googlesource.com/go

In src/runtime/syscall_windows_test.go:

    func TestRegisterClass(t *testing.T) {
        kernel32 := GetDLL(t, "kernel32.dll")
        user32 := GetDLL(t, "user32.dll")
        mh, _, _ := kernel32.Proc("GetModuleHandleW").Call(0)
        cb := syscall.NewCallback(func(hwnd syscall.Handle, msg uint32, wparam, lparam uintptr) (rc uintptr) {
            t.Fatal("callback should never get called")
            return 0
        })
        type Wndclassex struct {
            Size       uint32
            Style      uint32
            WndProc    uintptr
            ClsExtra   int32
            WndExtra   int32
            Instance   syscall.Handle
            Icon       syscall.Handle
            Cursor     syscall.Handle
            Background syscall.Handle
            MenuName   *uint16
            ClassName  *uint16
            IconSm     syscall.Handle
        }
        name := syscall.StringToUTF16Ptr("test_window")
        wc := Wndclassex{
            WndProc:   cb,
            Instance:  syscall.Handle(mh),
            ClassName: name,
        }
        wc.Size = uint32(unsafe.Sizeof(wc))
        a, _, err := user32.Proc("RegisterClassExW").Call(uintptr(unsafe.Pointer(&wc)))
        if a == 0 {
            t.Fatalf("RegisterClassEx failed: %v", err)
        }
        r, _, err := user32.Proc("UnregisterClassW").Call(uintptr(unsafe.Pointer(name)), 0)
        if r == 0 {
            t.Fatalf("UnregisterClass failed: %v", err)
        }
    }

For lpszClassName:
 
    name := syscall.StringToUTF16Ptr("test_window")

Peter
Reply all
Reply to author
Forward
0 new messages