| //go:build windows | |
| package winapi | |
| import ( | |
| "errors" | |
| "reflect" | |
| "syscall" | |
| "unsafe" | |
| "golang.org/x/sys/windows" | |
| ) | |
| // Uint16BufferToSlice wraps a uint16 pointer-and-length into a slice | |
| // for easier interop with Go APIs | |
| func Uint16BufferToSlice(buffer *uint16, bufferLength int) (result []uint16) { | |
| hdr := (*reflect.SliceHeader)(unsafe.Pointer(&result)) | |
| hdr.Data = uintptr(unsafe.Pointer(buffer)) | |
| hdr.Cap = bufferLength | |
| hdr.Len = bufferLength | |
| return | |
| } | |
| // UnicodeString corresponds to UNICODE_STRING win32 struct defined here | |
| // https://docs.microsoft.com/en-us/windows/win32/api/ntdef/ns-ntdef-_unicode_string | |
| type UnicodeString struct { | |
| Length uint16 | |
| MaximumLength uint16 | |
| Buffer *uint16 | |
| } | |
| // NTSTRSAFE_UNICODE_STRING_MAX_CCH is a constant defined in ntstrsafe.h. This value | |
| // denotes the maximum number of wide chars a path can have. | |
| const NTSTRSAFE_UNICODE_STRING_MAX_CCH = 32767 | |
| // String converts a UnicodeString to a golang string | |
| func (uni UnicodeString) String() string { | |
| // UnicodeString is not guaranteed to be null terminated, therefore | |
| // use the UnicodeString's Length field | |
| return windows.UTF16ToString(Uint16BufferToSlice(uni.Buffer, int(uni.Length/2))) | |
| } | |
| // NewUnicodeString allocates a new UnicodeString and copies `s` into | |
| // the buffer of the new UnicodeString. | |
| func NewUnicodeString(s string) (*UnicodeString, error) { | |
| buf, err := windows.UTF16FromString(s) | |
| if err != nil { | |
| return nil, err | |
| } | |
| if len(buf) > NTSTRSAFE_UNICODE_STRING_MAX_CCH { | |
| return nil, syscall.ENAMETOOLONG | |
| } | |
| uni := &UnicodeString{ | |
| // The length is in bytes and should not include the trailing null character. | |
| Length: uint16((len(buf) - 1) * 2), | |
| MaximumLength: uint16((len(buf) - 1) * 2), | |
| Buffer: &buf[0], | |
| } | |
| return uni, nil | |
| } | |
| // ConvertStringSetToSlice is a helper function used to convert the contents of | |
| // `buf` into a string slice. `buf` contains a set of null terminated strings | |
| // with an additional null at the end to indicate the end of the set. | |
| func ConvertStringSetToSlice(buf []byte) ([]string, error) { | |
| var results []string | |
| prev := 0 | |
| for i := range buf { | |
| if buf[i] == 0 { | |
| if prev == i { | |
| // found two null characters in a row, return result | |
| return results, nil | |
| } | |
| results = append(results, string(buf[prev:i])) | |
| prev = i + 1 | |
| } | |
| } | |
| return nil, errors.New("string set malformed: missing null terminator at end of buffer") | |
| } |