Я пытаюсь реализовать MiniDumpWriteDump с обратным вызовом в Go.
Вызов MiniDumpWriteDump:
callback := syscall.NewCallback(miniDumpCallback)
var newCallbackRoutine MINIDUMP_CALLBACK_INFORMATION
newCallbackRoutine.CallbackParam = 0
newCallbackRoutine.CallbackRoutine = callback
ret, _, err := miniDumpWriteDump.Call(
uintptr(processHandle),
uintptr(processId),
uintptr(dumpFile),
uintptr(options),
0,
0,
uintptr(unsafe.Pointer(&newCallbackRoutine)),
)
Сама функция обратного вызова:
func miniDumpCallback(_ uintptr, CallbackInput *MINIDUMP_CALLBACK_INPUT, _ uintptr) uintptr {
fmt.Println(CallbackInput.ProcessId, CallbackInput.CallbackType)
return 1
}
Определения типов:
type MINIDUMP_CALLBACK_INPUT struct {
ProcessId win32.ULONG
ProcessHandle win32.HANDLE
CallbackType win32.ULONG
CallbackInfo uintptr
}
type MINIDUMP_CALLBACK_INFORMATION struct {
CallbackRoutine uintptr
CallbackParam uintptr
}
Вызывается обратный вызов, и некоторые поля получают правильные данные, но некоторые получают бессмысленные значения.
Например, обратный вызов выше правильно получает поле ProcessId CallbackInput, но получает случайные целые числа в качестве CallbackType, тогда как он должен получать перечисление MINIDUMP_CALLBACK_TYPE.
Выход:
12544 0
12544 1133445120
12544 12548
12544 13028
12544 1114112
12544 1023344640
12544 999620608
12544 990117888
12544 992542720
12544 1005518848
12544 1994850304
12544 1114112
12544 1994915840
На этот вопрос не было получено ни одного правильного ответа. Я попробовал решение, предложенное в комментарии, на который вы ссылаетесь, но оно не сработало. Эта версия кода более чистая и предлагает относительно более правильный способ регистрации обратного вызова. Я удалил предыдущий вопрос, потому что он не имел никакой ценности для сообщества и, кроме того, показывал неправильное использование функций обратного вызова. Я разместил это, новое с тем же названием, чтобы другие разработчики нашли более правильный и понятный код, когда попытаются изучить эту тему.
Я ничего не знаю о Go, но предполагаю, что проблема в выравнивании структуры MINIDUMP_CALLBACK_INPUT. Предполагая, что вы работаете на x64, выравнивание будет составлять 8 байт.
Итак, если я правильно понимаю, что-то вроде этого надо сделать, да? @Люк type MINIDUMP_CALLBACK_INPUT struct { ProcessId uint32 _ [8]byte ProcessHandle windows.Handle _ [8]byte CallbackType uint32 _ [8]byte CallbackInfo uintptr _ [8]byte }
Структуры в minidumpapiset.h имеют выравнивание по 4 байта (на всех платформах). Для 32-битных архитектур все должно работать, поскольку это естественное выравнивание для всех используемых здесь типов. Для 64-битных архитектур вам придется принудительно ограничить размещение памяти 4-байтовыми границами, и вы не сможете полагаться на естественную структуру памяти Go для структур.
Чтобы убедиться в этом, вы можете прочитать поле ProcessHandle, извлечь старшие 32 бита и посмотреть, соответствует ли это 32-битное значение ожидаемому значению MINIDUMP_CALLBACK_TYPE. Если да, то проблема в упаковке структуры. Насколько я понимаю, Go не поддерживает упаковку неестественных структур, поэтому вместо этого вашему обратному вызову придется получить срез байта и проанализировать отдельные поля.





Как следует из комментариев, проблема заключалась в выравнивании структуры.
Как объяснил @IInspectable, minidumpapiset.h, который экспортирует функцию MiniDumpWriteDump и структуру MINIDUMP_CALLBACK_INPUT, использует 4-байтовое выравнивание как для 32-битной, так и для 64-битной архитектуры, в то время как Go по умолчанию использует 8-байтовое выравнивание для 64-битной архитектуры и не предлагает автоматического способа его изменения.
Решение состоит в том, чтобы вручную прочитать структуру. Вот рабочий пример:
type MINIDUMP_CALLBACK_INPUT struct {
ProcessId uint32
ProcessHandle uintptr
CallbackType uint32
CallbackInfo uintptr}
func ptrToMinidumpCallbackInput(ptrCallbackInput uintptr) MINIDUMP_CALLBACK_INPUT{
var input MINIDUMP_CALLBACK_INPUT
input.ProcessId = *(*uint32)(unsafe.Pointer(ptrCallbackInput))
input.ProcessHandle = *(*uintptr)(unsafe.Pointer(ptrCallbackInput + unsafe.Sizeof(uint32(0))))
input.CallbackType = *(*uint32)(unsafe.Pointer(ptrCallbackInput + unsafe.Sizeof(uint32(0)) + unsafe.Sizeof(uintptr(0))))
input.CallbackInfo = *(*uintptr)(unsafe.Pointer(ptrCallbackInput + unsafe.Sizeof(uint32(0)) + unsafe.Sizeof(uintptr(0)) + unsafe.Sizeof(uint32(0))))
return input}
Исходный код должен нормально работать на 32-битных архитектурах, поскольку его заполнение (4 байта) совпадает с заполнением, которое использует minidumpapiset.h.
Почему вы решили удалить свой предыдущий вопрос (включая полученные полезные комментарии)?