Струны UTF-16 в COM - PullRequest
       0

Струны UTF-16 в COM

0 голосов
/ 14 февраля 2020

Пожалуйста, кто-нибудь, объясните Go магия указателей Я пытаюсь вызвать функцию COM

func (p *IServer) Authorize(user, pass string) error {
  UserName := ole.SysAllocString(login.UserName)
  defer ole.SysFreeString(UserName)
  UserPsw := ole.SysAllocString(login.UserPsw)
  defer ole.SysFreeString(UserPsw)  

  // HRESULT IServer::Authorize([in] BSTR UserName, [in] BSTR UserPsw, [out] VARIANT* SID, [out, retval] long* Result)
  hr, _, _ := Call(p.VTable().Authorize,
    uintptr(unsafe.Pointer(p)),
    uintptr(unsafe.Pointer(UserName)),
    uintptr(unsafe.Pointer(UserPsw)),
    uintptr(unsafe.Pointer(sid)),
    uintptr(unsafe.Pointer(&res)))

   ...
}

Этот код работает хорошо, но когда я заменяю преобразование на

UserName := syscall.StringToUTF16Ptr(login.UserName)
UserPsw := syscall.StringToUTF16Ptr(login.UserPsw)

Это вызывает НАРУШЕНИЕ ДОСТУПА! Источник из go -ole

func SysAllocString(v string) (ss *int16) {
  pss, _, _ := procSysAllocString.Call(uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(v))))
  ss = (*int16)(unsafe.Pointer(pss))
  return
}

Что я делаю не так?

Обновление: в C / C ++ указатель wchar * корректно работает как 386 / amd64

void Authorize(IServer* p wchar* user, wchar* pass) {
   p.Authorize(user, pass ....
}

1 Ответ

1 голос
/ 14 февраля 2020

SysAllocString возвращает тип BSTR, объект типа com.

 typedef struct {
 #ifdef _WIN64
     DWORD pad;
 #endif
     DWORD size;
     union {
         char ptr[1];
         WCHAR str[1];
         DWORD dwptr[1];
     } u; // take it as a starting point of the string 
 } bstr_t;

Другими словами, это та же строка в кодировке utf16, но с префиксом в виде ее размера (длина символов Unicode, умноженная на размер wchar_t (2-4 байта)). По причине оптимизации он также имеет отступы.

Из-за своего плавающего размера лучше использовать пакет ole, а не изобретать велосипед. Если вы хотите реализовать это самостоятельно, а wchar_t имеет размер int16 (2 байта), то вам нужно сделать следующее:

(полупсевдокод, я его не тестировал)

type BSTR *uint16

func SysAllocString(str string) (result BSTR) {
    // DWORD == int32 == rune
    const padf = "\x00" // only for 64 bit system
    const sizef = "\x00"

    // int32 == 4 byte 
    // int16 == 2 byte
    const wordSize = unsafe.Sizeof(int16(0))

    utf16 := utf16.Encode([]rune(padf + sizef + str))

    /* pad is on index 0 and 1 */
    size :=  &utf16[2 /* 0 for 32 bit system */]

    // set "size" field as unicode charachers length multypled by size of wchar_t
    *(*rune)(unsafe.Pointer(size)) = rune((len(utf16)-2) * int(wordSize))


    result = BSTR(&utf16[0])

    return
}

// ...
bstr := SysAllocString(login.UserName)

uintptr(unsafe.Pointer(bstr))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...