Go调用DLL Syscall传递二级指针:C# DllImport代码迁移问题
问题解决方法
错误原因
你当前代码的核心问题是多取了一次指针地址,传入了三级指针而非DLL要求的二级指针。C#中ref IntPtr greetings的语义是:传入一个IntPtr类型变量的内存地址,DLL函数会把返回的一级指针值写入这个地址对应的内存空间。你原有代码中handleP := &handle已经是二级指针,再取&handleP就变成了三级指针,因此DLL写入的内容没有存储到你预期的handle变量中。
正确实现代码
package main import ( "fmt" "syscall" "unsafe" ) var ( MyDll = syscall.MustLoadDLL("my.dll") greet = MyDll.MustFindProc("Greet") ) func Greet(name string) (syscall.Handle, error) { nameP, err := syscall.UTF16PtrFromString(name) if err != nil { return 0, err } // 定义一级指针变量,DLL会把返回的指针值写入这个变量的内存空间 var greetingsPtr syscall.Handle // 直接传入greetingsPtr的地址,也就是二级指针,对应C#的ref IntPtr r1, _, _ := greet.Call( uintptr(unsafe.Pointer(nameP)), uintptr(unsafe.Pointer(&greetingsPtr)), ) if r1 == 0 { return 0, fmt.Errorf("greet call failed") } // 如需读取greetingsPtr指向的宽字符字符串,可使用如下代码: // greetStr := syscall.UTF16PtrToString((*uint16)(unsafe.Pointer(greetingsPtr))) return greetingsPtr, nil }
注意事项
- 内存释放:如果DLL内部分配了
greetings指向的内存,需要确认是否有配套的释放导出函数,调用完成后手动释放内存避免泄漏 - 调用约定:32位Windows环境下
syscall.Call默认使用Stdcall约定,你原C#代码声明的是Cdecl,32位场景建议改用CGO调用DLL避免栈不平衡问题,64位Windows下两种约定统一无需额外处理 - 编码匹配:确认DLL入参
name的编码要求,若为UTF-8则不能使用UTF16PtrFromString,需要传入以\0结尾的UTF-8字节数组指针
相关学习资源
- Go标准库
syscall、unsafe包官方文档:包含所有API的定义、用法和注意事项 - 《Go语言高级编程》:系统讲解Go底层编程、C/C++互操作、unsafe包原理、Windows系统调用的各类场景
- Go官方Wiki cgo专题:覆盖Go和原生代码互操作的常见问题、最佳实践,可直接复用在DLL调用场景
内容的提问来源于stack exchange,提问作者Clark McCauley
相关产品推荐
相关产品推荐

