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 {