咨询个go编译DLL并调用的报错的问题

hellsam · 2022-12-01 00:30:47 · 3115 次点击 · 大约8小时之前 开始浏览    置顶
这是一个创建于 2022-12-01 00:30:47 的主题,其中的信息可能已经有所发展或是发生改变。

【问题原因:】

1:我在 func PP2 函数 使用map转JSON ,json.Marshal(obj) 返回[]byte 在string(str) 得到string 但是返回 客户端直接报错 ,如果我直接使用 str :="JSON信息", 客户端调用OK。 【初步怀疑DLL服务端不能使用GO代码】,不知道有没有人遇到过这个问题。

2:原理很简单:就是 客户端吧数据传给DLL,然后DLL处理完毕,返回特定字符串我!并非整数 需要字符串返回的

3:我听说CGO调用CString后 貌似需要 C.free是否字符串 防止内存溢出, 不知道加哪里的

dll服务端代码部分:



package main

// #include <stdio.h>
// #include <stdlib.h>
import (
    "C"
)
import (
    "fmt"
    //"unsafe"
)

//export PP2
func PP2(msg2 *C.char) *C.char {
    //只能使用str :="字符串",如果使用以下map组合JSON 客户端调用马上报错
    //map1 := make(map[string]string)
    //map1["a"] = "1"
    //map1["b"] = "2"
    //map1["c"] = "3"
    //json1, _ := Serialize(map1)*/ //这个函数用的是json.Marshal(obj) 返回[]byte 转换后实际还是string
    //var str string = string(json1)

    str := `{"a":"111","b":"2","c":"3"}`
    msg2 = C.CString(str)
    return msg2
}

func Serialize(obj interface{}) (b []byte, err error) {
    b, err = json.Marshal(obj)
    if err != nil {
        return nil, err
    }
    return b, nil
}

调用客户端:

func main() {

    //重来
    dll3 := syscall.NewLazyDLL("mokuai2.dll")

    f := dll3.NewProc("PP2")

    r, _, _ := f.Call(uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("AAAA")))) //StringBytePtr

    //获取C返回指针
    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))
    }

    name := string(data)

    fmt.Println(name)
}

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

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

3115 次点击  
加入收藏 微博
12 回复  |  直到 2023-01-28 14:02:32
jan-bar
jan-bar · #1 · 2年之前

我怀疑syscall.StringToUTF16Ptr用的不对,改为syscall.StringBytePtr试试看。前者转换的结果是双字节数据,和*C.char内存布局不同。

hellsam
hellsam · #2 · 2年之前
jan-barjan-bar #1 回复

我怀疑`syscall.StringToUTF16Ptr`用的不对,改为`syscall.StringBytePtr`试试看。前者转换的结果是双字节数据,和`*C.char`内存布局不同。

刚试了 也不行

jan-bar
jan-bar · #3 · 2年之前
hellsamhellsam #2 回复

#1楼 @jan-bar 刚试了 也不行

image.png

但是我试过可以啊。还有就是关于dll用法,一般都不会返回数据吧,而是传入参数(分配的内存,例如结构体之类的),由dll往入参内存里面写入数据,并返回写入数据长度。我看window相关dll都是这样方式返回调用方数据。你这里由dll申请C内存msg2 = C.CString(str)如果不找地方释放感觉会有内存泄露。通过入参传入内存块是调用方的内存,所以释放也是调用方的事情。

hellsam
hellsam · #4 · 2年之前
jan-barjan-bar #3 回复

#2楼 @hellsam ![image.png](https://static.golangjob.cn/221201/a9658d2274f76a4f73a3f1314b039f6b.png) 但是我试过可以啊。还有就是关于dll用法,一般都不会返回数据吧,而是传入参数(分配的内存,例如结构体之类的),由dll往入参内存里面写入数据,并返回写入数据长度。我看window相关dll都是这样方式返回调用方数据。你这里由dll申请C内存`msg2 = C.CString(str)`如果不找地方释放感觉会有内存泄露。通过入参传入内存块是调用方的内存,所以释放也是调用方的事情。

因为你直接用了 str :="字符串啊" 你尝试如上map转字符串返回试试 ,马上客户端 奔溃,

你说的 free释放写哪里啊

jan-bar
jan-bar · #5 · 2年之前
hellsamhellsam #4 回复

#3楼 @jan-bar 因为你直接用了 str :="字符串啊" 你尝试如上map转字符串返回试试 ,马上客户端 奔溃, 你说的 free释放写哪里啊

我也懵逼了,我发现导出下面这个方法,其他地方调用dll都报错,我看貌似是申请了堆内存。你的str:="xxx"实际上是栈内存,函数调用完就释放了。但是只要我在函数里面使用的方法会申请堆内存就会报错。我平时都是调用别人的dll,没用用go编写dll,我也坐等大神解惑额。还有就是cgo申请的内存不被Go接管,所以必须手动释放,因此msg2 = C.CString(str)申请了C内存,没有报错,至于啥地方释放就得你编写代码额。详情参考:Go源码注释

image.png

//export PrintBye
func PrintBye() {
    fmt.Println("From DLL: Bye!")
}
jan-bar
jan-bar · #6 · 2年之前
hellsamhellsam #4 回复

#3楼 @jan-bar 因为你直接用了 str :="字符串啊" 你尝试如上map转字符串返回试试 ,马上客户端 奔溃, 你说的 free释放写哪里啊

找到一个类似问题 点击跳转 ,这个人用go生成dll,python调用没毛病,Go调用就报错。

我还找到了一个本站别的 帖子 也没解决。

还找到这篇 文章,里面重点是 通俗点说就是我Go语言的指针你们其他语言别想用!,但是我还是不清楚具体问题。

我还是放弃追查这个问题了,感觉就是Go有GC的问题,封装成dll,申请堆内存后GC应该会有协程去释放对象导致的吧。用无GC语言编写的dll应该没问题,个人猜测。

hellsam
hellsam · #7 · 2年之前
jan-barjan-bar #6 回复

#4楼 @hellsam 找到一个类似问题 [点击跳转](https://stackoverflow.com/questions/66141445/import-dll-which-build-from-go-and-runtime-error) ,这个人用go生成dll,python调用没毛病,Go调用就报错。 我还找到了一个本站别的 [帖子](https://studygolang.com/topics/11897) 也没解决。 还找到这篇 [文章](https://www.jianshu.com/p/907ade8993a9),里面重点是 `通俗点说就是我Go语言的指针你们其他语言别想用!`,但是我还是不清楚具体问题。 我还是放弃追查这个问题了,感觉就是Go有GC的问题,封装成dll,申请堆内存后GC应该会有协程去释放对象导致的吧。用无GC语言编写的dll应该没问题,个人猜测。

嗯好的谢谢大佬, 大佬GO想做独立插件编译好的,看来dll的要放弃了 本想 win dll, linux .so 看来win实现不了 了, 还有什么方法代替这种吗?

jan-bar
jan-bar · #8 · 2年之前
hellsamhellsam #7 回复

#6楼 @jan-bar 嗯好的谢谢大佬, 大佬GO想做独立插件编译好的,看来dll的要放弃了 本想 win dll, linux .so 看来win实现不了 了, 还有什么方法代替这种吗?

rpc最方便,但是需要网络连接以及各种三方库

或者exec.Command启动你的插件,通过标准输入输出和你编译好的插件进行交互,其实这种已经比较类似dll那种方式了,我见过有人这样做。

或者exec.Command启动你的插件,然后你这个插件启用了http之类的服务器,你的程序通过http调用。

hellsam
hellsam · #9 · 2年之前
jan-barjan-bar #8 回复

#7楼 @hellsam `rpc`最方便,但是需要网络连接以及各种三方库 或者`exec.Command`启动你的插件,通过标准输入输出和你编译好的插件进行交互,其实这种已经比较类似dll那种方式了,我见过有人这样做。 或者`exec.Command`启动你的插件,然后你这个插件启用了http之类的服务器,你的程序通过http调用。

确实exec.Command 是个很实用的方法,不过这种我打算做备用方式了, 目前在用pingo基于RPC 还不是很理想,进程超时卡死 很容易出现多个进程存在。 写插件系统这点还是解释型语言比较强大

jan-bar
jan-bar · #10 · 2年之前

看需求吧,你也可以用嵌入语言,例如go-lua,插件用lua脚本写,go提供接口就行。go语言已经有很多嵌入语言了,你可以搜搜看。

你说的 <https://github.com/dullgiulio/pingo> 这个库么?进程超时,那是插件应该处理的事情吧。

<https://github.com/dullgiulio/pingo/issues/4> 这里面也有一个用标准输入输出做插件的项目,看来很多人都想到了这些点。

twgh
twgh · #11 · 2年之前

都有人用go编译dll给易语言、c++用了, 有人封装了gin给易语言用, 可看出go编译的dll应该没啥问题, 但是要注意写法。

https://bbs.125.la/forum.php?mod=viewthread&tid=14663404&highlight=go%E8%AF%AD%E8%A8%80

go调用dll也是有很多注意的点, 我调用过c++的dll,windows api, go语言编译的dll倒是没调用过,我觉得有问题也是出在数据转换、传递上。

nuan1989
nuan1989 · #12 · 2年之前
twghtwgh #11 回复

#### 都有人用go编译dll给易语言、c++用了, 有人封装了gin给易语言用, 可看出go编译的dll应该没啥问题, 但是要注意写法。 [https://bbs.125.la/forum.php?mod=viewthread&tid=14663404&highlight=go%E8%AF%AD%E8%A8%80](https://bbs.125.la/forum.php?mod=viewthread&tid=14663404&highlight=go%E8%AF%AD%E8%A8%80) #### go调用dll也是有很多注意的点, 我调用过c++的dll,windows api, go语言编译的dll倒是没调用过,我觉得有问题也是出在数据转换、传递上。

你说的这个"go编译dll给易语言", 我去看了, 是用gcc实现的, 你的炫彩界面库, 用syscall的Call调用, 你这种调用方式不释放变量会不会内存泄漏?

添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传