utils.go 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. //go:build windows
  2. package winapi
  3. import (
  4. "errors"
  5. "reflect"
  6. "syscall"
  7. "unsafe"
  8. "golang.org/x/sys/windows"
  9. )
  10. // Uint16BufferToSlice wraps a uint16 pointer-and-length into a slice
  11. // for easier interop with Go APIs
  12. func Uint16BufferToSlice(buffer *uint16, bufferLength int) (result []uint16) {
  13. hdr := (*reflect.SliceHeader)(unsafe.Pointer(&result))
  14. hdr.Data = uintptr(unsafe.Pointer(buffer))
  15. hdr.Cap = bufferLength
  16. hdr.Len = bufferLength
  17. return
  18. }
  19. // UnicodeString corresponds to UNICODE_STRING win32 struct defined here
  20. // https://docs.microsoft.com/en-us/windows/win32/api/ntdef/ns-ntdef-_unicode_string
  21. type UnicodeString struct {
  22. Length uint16
  23. MaximumLength uint16
  24. Buffer *uint16
  25. }
  26. // NTSTRSAFE_UNICODE_STRING_MAX_CCH is a constant defined in ntstrsafe.h. This value
  27. // denotes the maximum number of wide chars a path can have.
  28. const NTSTRSAFE_UNICODE_STRING_MAX_CCH = 32767
  29. // String converts a UnicodeString to a golang string
  30. func (uni UnicodeString) String() string {
  31. // UnicodeString is not guaranteed to be null terminated, therefore
  32. // use the UnicodeString's Length field
  33. return windows.UTF16ToString(Uint16BufferToSlice(uni.Buffer, int(uni.Length/2)))
  34. }
  35. // NewUnicodeString allocates a new UnicodeString and copies `s` into
  36. // the buffer of the new UnicodeString.
  37. func NewUnicodeString(s string) (*UnicodeString, error) {
  38. buf, err := windows.UTF16FromString(s)
  39. if err != nil {
  40. return nil, err
  41. }
  42. if len(buf) > NTSTRSAFE_UNICODE_STRING_MAX_CCH {
  43. return nil, syscall.ENAMETOOLONG
  44. }
  45. uni := &UnicodeString{
  46. // The length is in bytes and should not include the trailing null character.
  47. Length: uint16((len(buf) - 1) * 2),
  48. MaximumLength: uint16((len(buf) - 1) * 2),
  49. Buffer: &buf[0],
  50. }
  51. return uni, nil
  52. }
  53. // ConvertStringSetToSlice is a helper function used to convert the contents of
  54. // `buf` into a string slice. `buf` contains a set of null terminated strings
  55. // with an additional null at the end to indicate the end of the set.
  56. func ConvertStringSetToSlice(buf []byte) ([]string, error) {
  57. var results []string
  58. prev := 0
  59. for i := range buf {
  60. if buf[i] == 0 {
  61. if prev == i {
  62. // found two null characters in a row, return result
  63. return results, nil
  64. }
  65. results = append(results, string(buf[prev:i]))
  66. prev = i + 1
  67. }
  68. }
  69. return nil, errors.New("string set malformed: missing null terminator at end of buffer")
  70. }