Проблема
var filename string = "test_file"
strptr := &filename
fileNamePtr := (*uint16)(unsafe.Pointer(strptr))
неверна на нескольких уровнях:
Строка в Go - это значение типа struct
, содержащее два поля: указатель на первый байт данных строки и целое число, содержащее длину строки (в байтах) - в основном это определяется так:
type string struct {
ptr *byte
len int
}
Следовательно, беря адрес Go Строковая переменная принимает адрес ячейки памяти, в которой содержится этот указатель на данные строки (поле ptr
выше).
Чтобы получить адрес первого байта данных строки можно было бы сделать &filename[0]
. Но это все равно неправильно в вашем случае - потерпите меня.
Go строки содержат непрозрачные байты.
В Go есть несколько мест, которые do предполагает определенную кодировку Go строк, а именно UTF-8 , и это то, что вы прочитали бы в любом учебном материале в Go, но на самом деле они могут содержать непрозрачные байты , закодированный с использованием любой кодировки или вообще без кодирования.
Это означает, что способ перекодирования строки в некоторую целевую кодировку должен решаться в каждом конкретном случае с учетом кодирования исходной строки.
К счастью, ваш конкретный случай является самым простым.
Так как Go файлы исходного кода определены для кодирования в UTF-8, Go строки, которые были определены как строковые литералы (и ваш filename
переменной присваивается значение, определенное строковым литералом), кодируются в кодировке UTF-8.
UTF-8 представляет собой кодировку переменной длины , которая использует от 1 до 4 байтов на кодированный Кодовая точка Unicode - dep заканчивая его целочисленным значением.
Функция Win32 API, которую вы намереваетесь вызывать, хочет, чтобы строка была закодирована в UTF-16 .
UTF-16 - это кодировка фиксированной длины, которая использует 2 байт на кодовую точку Unicode, которую он кодирует.
Думаю, к настоящему времени должно быть очевидно, что создание «переинтерпретации» приведения указателя, указывающего на строку в кодировке UTF-8, к указателю, указывающему на UTF-16 -кодированная строка не будет ничего делать с содержимым этой строки: они останутся закодированными в UTF-8.
Решение
Итак, сначала необходимо выполнить правильное преобразование: подсчитать количество кодовых точек Unicode («рун»), содержащихся в исходной строке, выделить в два раза больше байтов для новой строки, а затем выполнить итерации по рунам в исходной строке по одному -one, правильное кодирование каждого в строку назначения (Windows использует формат с прямым порядком байтов для UTF-16).
Хотя вы можете свернуть свою собственную реализацию, как описано выше, Go уже имеет ее в своем встроенный Пакет syscall
в виде функции
func UTF16FromString(s string) ([]uint16, error)
.
Таким образом, ваш код должен выглядеть примерно так:
u16fname, err := syscall.UTF16FromString(filename)
if err != nil {
// fail
}
windows.CreateFile(&u16fname[0], ...)
Обратите внимание, что вы можете увидеть что доступно в пакете syscall
, прочитав вывод go doc syscall
.
Если вы не в целевой ОС, запустите GOOS=windows go doc syscall
.
И обратите внимание, что https://golang.org/pkg/syscall отображает документацию для GOOS=linux
, поэтому бесполезно читать, когда вы хотите использовать Windows -specifi c stdlib code.
Если вы ' Интересно, что в вашем случае, когда вы передали адрес значения указателя в CreateFileW
, эта функция начала интерпретировать необработанную память, начиная с 1-го байта значения указателя 64-бит, как четыре последовательных UTF-16- затем закодированные символы перешли к полю длины строкового значения, которое содержало значение 0x0000000000000009
- длину строки «test_file» в байтах, - поэтому CreateFileW
прочитал первый 0x0009
, интерпретировал его как символ TAB и затем остановился на 0x0000
, поскольку это NUL в кодировке UTF-16 (w он завершает строки в «широком» Win32 API).
Возможно, ему также удалось остановить раньше - в зависимости от фактического значения указателя: если в верхнем слове было 0x0000
, это значение служило бы NUL -terminator.