From 7fc4e5ec1444df20dd195ae7c3bbfcd7114d3faa Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Thu, 6 Jun 2019 19:29:06 +0200 Subject: [PATCH] windows: add GUID handling functions Virtually every project that works with x/sys/window's GUIDs winds up rolling their own version of this in one way or another. So let's add the correct win32 wrappers for it, so that these are always generated, parsed, and converted in the uniform correct way. Change-Id: I35f4b4ab5fc681d3e16fc5bbaf2cb20031eb3f12 Reviewed-on: https://go-review.googlesource.com/c/sys/+/180938 Run-TryBot: Jason Donenfeld TryBot-Result: Gobot Gobot Reviewed-by: Brad Fitzpatrick --- windows/syscall_windows.go | 39 +++++++++++++++++++++++++++++++++ windows/syscall_windows_test.go | 23 +++++++++++++++++++ windows/zsyscall_windows.go | 30 +++++++++++++++++++++++++ 3 files changed, 92 insertions(+) diff --git a/windows/syscall_windows.go b/windows/syscall_windows.go index bea993b2..ede2c945 100644 --- a/windows/syscall_windows.go +++ b/windows/syscall_windows.go @@ -286,6 +286,9 @@ func NewCallbackCDecl(fn interface{}) uintptr { //sys SetVolumeLabel(rootPathName *uint16, volumeName *uint16) (err error) = SetVolumeLabelW //sys SetVolumeMountPoint(volumeMountPoint *uint16, volumeName *uint16) (err error) = SetVolumeMountPointW //sys MessageBox(hwnd Handle, text *uint16, caption *uint16, boxtype uint32) (ret int32, err error) [failretval==0] = user32.MessageBoxW +//sys clsidFromString(lpsz *uint16, pclsid *GUID) (err error) [failretval!=0] = ole32.CLSIDFromString +//sys stringFromGUID2(rguid *GUID, lpsz *uint16, cchMax int) (chars int) = ole32.StringFromGUID2 +//sys coCreateGuid(pguid *GUID) (ret error) = ole32.CoCreateGuid // syscall interface implementation for other packages @@ -1241,3 +1244,39 @@ func Readlink(path string, buf []byte) (n int, err error) { return n, nil } + +// GUIDFromString parses a string in the form of +// "{XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}" into a GUID. +func GUIDFromString(str string) (GUID, error) { + guid := GUID{} + str16, err := syscall.UTF16PtrFromString(str) + if err != nil { + return guid, err + } + err = clsidFromString(str16, &guid) + if err != nil { + return guid, err + } + return guid, nil +} + +// GenerateGUID creates a new random GUID. +func GenerateGUID() (GUID, error) { + guid := GUID{} + err := coCreateGuid(&guid) + if err != nil { + return guid, err + } + return guid, nil +} + +// String returns the canonical string form of the GUID, +// in the form of "{XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}". +func (guid GUID) String() string { + var str [100]uint16 + chars := stringFromGUID2(&guid, &str[0], len(str)) + if chars <= 1 { + return "" + } + return string(utf16.Decode(str[:chars-1])) +} diff --git a/windows/syscall_windows_test.go b/windows/syscall_windows_test.go index 423fa9ec..b157625f 100644 --- a/windows/syscall_windows_test.go +++ b/windows/syscall_windows_test.go @@ -5,6 +5,7 @@ package windows_test import ( + "fmt" "io/ioutil" "os" "path/filepath" @@ -169,3 +170,25 @@ func TestPseudoTokens(t *testing.T) { t.Fatal("The real process token does not have the same as the pseudo thread token after impersonating self") } } + +func TestGUID(t *testing.T) { + guid, err := windows.GenerateGUID() + if err != nil { + t.Fatal(err) + } + if guid.Data1 == 0 && guid.Data2 == 0 && guid.Data3 == 0 && guid.Data4 == [8]byte{} { + t.Fatal("Got an all zero GUID, which is overwhelmingly unlikely") + } + want := fmt.Sprintf("{%08X-%04X-%04X-%04X-%012X}", guid.Data1, guid.Data2, guid.Data3, guid.Data4[:2], guid.Data4[2:]) + got := guid.String() + if got != want { + t.Fatalf("String = %q; want %q", got, want) + } + guid2, err := windows.GUIDFromString(got) + if err != nil { + t.Fatal(err) + } + if guid2 != guid { + t.Fatalf("Did not parse string back to original GUID = %q; want %q", guid2, guid) + } +} diff --git a/windows/zsyscall_windows.go b/windows/zsyscall_windows.go index a7663b5f..55d2de15 100644 --- a/windows/zsyscall_windows.go +++ b/windows/zsyscall_windows.go @@ -42,6 +42,7 @@ var ( modmswsock = NewLazySystemDLL("mswsock.dll") modcrypt32 = NewLazySystemDLL("crypt32.dll") moduser32 = NewLazySystemDLL("user32.dll") + modole32 = NewLazySystemDLL("ole32.dll") modws2_32 = NewLazySystemDLL("ws2_32.dll") moddnsapi = NewLazySystemDLL("dnsapi.dll") modiphlpapi = NewLazySystemDLL("iphlpapi.dll") @@ -221,6 +222,9 @@ var ( procSetVolumeLabelW = modkernel32.NewProc("SetVolumeLabelW") procSetVolumeMountPointW = modkernel32.NewProc("SetVolumeMountPointW") procMessageBoxW = moduser32.NewProc("MessageBoxW") + procCLSIDFromString = modole32.NewProc("CLSIDFromString") + procStringFromGUID2 = modole32.NewProc("StringFromGUID2") + procCoCreateGuid = modole32.NewProc("CoCreateGuid") procWSAStartup = modws2_32.NewProc("WSAStartup") procWSACleanup = modws2_32.NewProc("WSACleanup") procWSAIoctl = modws2_32.NewProc("WSAIoctl") @@ -2406,6 +2410,32 @@ func MessageBox(hwnd Handle, text *uint16, caption *uint16, boxtype uint32) (ret return } +func clsidFromString(lpsz *uint16, pclsid *GUID) (err error) { + r1, _, e1 := syscall.Syscall(procCLSIDFromString.Addr(), 2, uintptr(unsafe.Pointer(lpsz)), uintptr(unsafe.Pointer(pclsid)), 0) + if r1 != 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func stringFromGUID2(rguid *GUID, lpsz *uint16, cchMax int) (chars int) { + r0, _, _ := syscall.Syscall(procStringFromGUID2.Addr(), 3, uintptr(unsafe.Pointer(rguid)), uintptr(unsafe.Pointer(lpsz)), uintptr(cchMax)) + chars = int(r0) + return +} + +func coCreateGuid(pguid *GUID) (ret error) { + r0, _, _ := syscall.Syscall(procCoCreateGuid.Addr(), 1, uintptr(unsafe.Pointer(pguid)), 0, 0) + if r0 != 0 { + ret = syscall.Errno(r0) + } + return +} + func WSAStartup(verreq uint32, data *WSAData) (sockerr error) { r0, _, _ := syscall.Syscall(procWSAStartup.Addr(), 2, uintptr(verreq), uintptr(unsafe.Pointer(data)), 0) if r0 != 0 {