Параметр
Итак, в основном, у вас есть отображение
int* buffer
→ buffer uintptr
int argc
→ unsafe.Pointer(&argsCount)
, где &argsCount
- указатель на int
char* argv[]
→ unsafe.Pointer(&args)
, где args
равно []string
const char* fileName
→ unsafe.Pointer(syscall.StringToUTF16Ptr(fileName))
const char* key
, const char* prefix
, const char* version
- то же, что и выше.
Проблемы
Что с этим не так.
Передача uintptr
значений, содержащих адреса живых объектов Go между функциями, запрещена.(Подробнее об этом позже.)
Адрес аргумента функции argsCount
передается argc
.Адрес на типичной товарной платформе / ОС, такой как amd64 / Windows, является безумно огромным значением - если интерпретировать его как число.
Моя ставка заключается в том, что функция вылетает при попытке прочитать столько элементов из argv
- заставляет его читать память, которую ваш процесс не отображал.
Здесь две проблемы:
Передача адреса значения слайса в видеаргумент, ожидающий адрес первого элемента этого среза, неверен.
Это потому, что значение среза (в настоящее время в реализации «ссылки» Go, которую вы предположительно используете) представляет собой структуру из 3 аргументов:адрес базового массива данных, количество элементов в этом массиве, которое можно использовать, и общее количество таких элементов в этом массиве, которые могут использоваться без перераспределения функцией append
при вызове значения слайса.
Когда у вас есть args []string
и вы делаете &args
, вы получите адрес этой структуры, а не адрес первого элемента этого среза.
Чтобы сделать последнее, используйте &args[0]
.
В Go строка (обычно, и давайте предположим, что это так) содержит символы, закодированные как UTF-8.Как правило, это не то, с чем может столкнуться нативный код C ++ для Windows, когда он говорит, что хочет char *
.
. Полагаю, вам нужно сначала создать подходящую вещь для argv
, что-то вроде
argv := make([]unsafe.Pointer, 0, len(args))
for i, s := range args {
argv[i] = unsafe.Pointer(syscall.StringToUTF16Ptr(s))
}
и затем передайте &argv[0]
вызываемому абоненту.
Но см. Ниже для syscall.StringToUTF16Ptr()
.
Подготовка, которую вы делаете для передачи строковых данных остальным аргументам, имеющим тип const char*
, представляется правильной, но только если вызываемый объект действительно означает, что char
является 16-разрядным целым числом.
Другими словами, исходный код и набор инструментов , использованные для компиляции этой библиотеки, должны были убедиться, что char
действительно wchar_t
или WCHAR
.
Если да, то, что вы делаете, должно быть в порядке;в противном случае это не так.Вы должны проверить это.
Примечание о передаче uintptr
s между выражениями
Go содержит сборку мусора, и поэтому во время выполнения он должен знать все указатели на всев данный момент живые объекты.Пока существует переменная, содержащая указатель на блок памяти, выделенный во время выполнения программы, этот блок не будет собираться мусором.
Значение unsafe.Pointer
считается правильной ссылкой на памятьблок , но значение uintptr
отсутствует. Это означает, что когда код
p := new(someType)
u := uintptr(unsafe.Pointer(&p))
foo(u)
return
работает, GC может освободить объект, выделенный p
как только p
был назначен его адрес - просто потому, что p
является единственной ссылкой на этот объект, а u
- нет.
Теперь рассмотрим, что GC в эталонной реализации Go выполняетсяодновременно с кодом вашей программы.Это означает, что когда foo
выполняется, то же самое может быть и GC, и он может смести ваш экземпляр someType
прямо из-под ног foo
.
В качестве исключения из этого общего правила, GoКомпилятор гарантирует, что все преобразования типов uintptr(unsafe.Pointer)
, происходящие в одном и том же языковом выражении , защищены от GC.Так что, взяв наш предыдущий пример, можно делать
foo(uintptr(unsafe.Pointer(new(someType)))
return
и
p := new(someType)
v := unsafe.Pointer(&p)
foo(uintptr(v))
return
, поскольку преобразование типа в uintptr
происходит в одном выражении, которое является вызовом функции.
Как следствиеКроме того, вы не должны передавать указатели на объекты Go как uintptr
s , если только они не содержат указателей, полученных со стороны "C".