You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 10:06:00