golang 调用 c++ DLL 如何获取 dll 返回的 string 类型值

gouser · 2017-05-18 02:18:24 · 7293 次点击 · 大约8小时之前 开始浏览    置顶
这是一个创建于 2017-05-18 02:18:24 的主题,其中的信息可能已经有所发展或是发生改变。

我用 golang 调用 c++ dll,调用成功,传值成功(代码省略其他无用的,仅细节代码):

inst := syscall.MustLoadDLL("test.dll");
some_func := inst.MustFindProc("test_function");

s1: = "aaa"  // 传递参数给 c++ dll
s2:= "bbb"   // 传递参数给 c++ dll
s3:=""          // 我希望从这个参数得到返回值

some_func.Call( uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(s1),
                        uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(s2)
                        uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(s3))

现在在 c++ 的 test_function 里面能够给收到传递过来的数据了,但是,我该如何给 s3 赋值呢?试了很多种办法,s3 的值在 go 这边看不到,不是很懂这个原理,求大神指点。 另外我的要求其实很简单,就是从参数,或者返回值能获取到 c++ 返回的字符串类型的值(不是 int),求大神帮忙给个办法。

冰天雪地赤身Luo体360°托马斯旋转烧香拜佛跪破膝盖 求解救办法...


有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

7293 次点击  
加入收藏 微博
8 回复  |  直到 2021-09-08 14:10:51
mortemnh
mortemnh · #1 · 8年之前

C++返回值应该是 char ,返回的是指针,然后在Go中对这个int类型进行转换变成 指向 byte的指针,解引用就读出了这个 字节的值。C++中char 类型是连续内存结构,最后以'0x00‘结尾,所以每读取依次 byte 对传回来的 指针进行偏移1 操作直到读取到'0x00‘ 认为是结尾。再把这个[]byte转换成 string就可以了。 首先要明确就是就是C++中的编码形式。

gouser
gouser · #2 · 8年之前
mortemnhmortemnh #1 回复

C++返回值应该是 char* ,返回的是指针,然后在Go中对这个int类型进行转换变成 指向 byte的指针,解引用就读出了这个 字节的值。C++中char* 类型是连续内存结构,最后以'0x00‘结尾,所以每读取依次 byte 对传回来的 指针进行偏移1 操作直到读取到'0x00‘ 认为是结尾。再把这个[]byte转换成 string就可以了。 首先要明确就是就是C++中的编码形式。

首先三叩九拜跪谢大神。 其次有几个问题想请教下:

  1. 目前 c++ 这边如果返回一个指针,显然不能返回栈上开辟的,也就是应该搞个 new 出来的玩意返回,否则c++函数返回后,go这边得到的是一个已经被释放了的,但是问题来了,go得到这个指针后,又不能释放,是不是造成了内存泄漏?

  2. c++ 这边的返回 char 应该如何被 go 转换呢,新手不懂 ^_^

再次六体投地请大神解答

CodyGuo
CodyGuo · #3 · 8年之前

发出你的头文件和dll

Cynhard
Cynhard · #4 · 8年之前
  1. 会造成泄漏。C返回的指针不会被Go引用计数,因此也不会被垃圾回收。解决方法可以在dll中实现释放的接口,在Go中调用。
  2. 一楼解释得很明白了,这里略做补充:syscall.Proc.Call返回uintptr表示内存地址,直接操作这个地址即可,以下代码未经仔细推敲,仅供参考:
    r, _, _ := some_func.Call()
    p := (*byte)(unsafe.Pointer(r))
    data := make([]byte, 0)
    for *p != 0 {
        data = append(data, *p)
        r += unsafe.Sizeof(byte(0))
        p = (*byte)(unsafe.Pointer(r))
    }
    s := string(data)
xjjrocker
xjjrocker · #5 · 8年之前

s3 := make([]byte, 256) call里面就用 uintptr(unsafe.Pointer(&s3[0])) 这样的s3就可以接收返回值了

746166858
746166858 · #6 · 7年之前

test_function(char s1, char s2, char* s3) 这是你的原函数么

seejob
seejob · #7 · 7年之前
gousergouser #2 回复

#1楼 @mortemnh 首先三叩九拜跪谢大神。 其次有几个问题想请教下: 1. 目前 c++ 这边如果返回一个指针,显然不能返回栈上开辟的,也就是应该搞个 new 出来的玩意返回,否则c++函数返回后,go这边得到的是一个已经被释放了的,但是问题来了,go得到这个指针后,又不能释放,是不是造成了内存泄漏? 2. c++ 这边的返回 char 应该如何被 go 转换呢,新手不懂 ^_^ 再次六体投地请大神解答

楼主 解决了吗

jan-bar
jan-bar · #8 · 4年之前

最近我也遇到相关问题,查阅go源码,结合自己的理解,得到解决方案。不过有几个方法是go1.17才有的,如果不是go1.17的话去找go源码吧。我是通过追踪os.Args的window源码搞出来的。

func UintPtrToString(r uintptr) string {
    p := (*uint16)(unsafe.Pointer(r))
    if p == nil {
        return ""
    }

    n, end, add := 0, unsafe.Pointer(p), unsafe.Sizeof(*p)
    for *(*uint16)(end) != 0 {
        end = unsafe.Add(end, add)
        n++
    }
    return string(utf16.Decode(unsafe.Slice(p, n)))
}
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传