Windows平台Go调用DLL的坑(居然有这么多没听过的名词)
最近的项目中,使用了GO来开发一些服务中转程序。业务比较简单,但是有一些业务需要复用原有C++开发的代码。而在WINDOWS,用CGO方式来集成C/C++代码并不是太方便。所以用DLL把C++的代码封装起来,然后提供基本的API来完成复用。在这个过程中遇到了一些问题及解决方法,记录下来,也给遇到类似或者同样问题的人一个借鉴。
如果你还不清楚怎么在GO中调用DLL,可以参考这篇文章《WindowDLLs》。
Callback的限制
在WINDOWS下调用一些API时会要求传入回调函数,在C/C++下使用非常简单,直接传入函数指针就可以了。但是在GO这种有GC特性,又有运行时库的语言要稍微麻烦一点。
GO为了解决这种回调要求,在syscall包里提供了NewCallback和NewCallbackCDecl两个函数来帮助用户解决回调的问题。具体的Callback机制这里先不说了,只是说一下在GO里,Callback的使用是有限制的。而且对传入的GO函数也是有对应的要求。在我看的go1.4正式版本中,src/runtime/syscall_windows.go line 71:会检查callback的数量是否超过了最大的限制值,这个限制值目前是2000。如果超过就会抛出一个异常。这个是非常蛋疼的事情。而且从GO的ISSUE库里,这个问题的解决是一推再推。
GO的ISSUE里关于CALLBACK的事情已经很明确了,就是要让大家复用CALLBACK,也就是用全局函数来搞。在golang-china讨论组里,minux给予了这样的回答:
我说一下callback上限的原因。由于系统调用callback函数的时候不提供任何其他的参数,导致区分不同的callback只能通过被调用函数的地址,也就是说,一个Go的callback函数必须对应一个单独的C函数地址
旧的机制是对每个Go函数,在堆上动态构造一个对应的C函数。这样在 Go 1.1 之前的时候没问题,因为当时函数闭包也需要可执行的堆,但是 1.1 修改了函数的表示方式,闭包不再需要动态代码生成了,为了把 Windows 上也不再需要可执行的堆,必须想另外一个办法来实现 callback,新的机制参加 issue 5494,是我提议的。既然不动态生成代码,很明显的一个问题就是 callback 的总数会有一个上限。这是这有任何办法的了。
其实一般的 Windows 程序也不会有那么多 callback,2000个绝对是绰绰有余的;之所以 Go 这里很容易用光,是因为可能大家愿意使用 closure,而不是一个全局函数做 callback,使用全局函数做 callback (C/C++程序就是这么做的),2000个绝对是绰绰有余;但是由于闭包每次建立都是不同的,就算你其实就一个地方需要创建 callback,用闭包的话2000次就用光了所有的 callback。使用 callback 的时候建议用全局函数,也不要用 method,因为那样也是闭包,2000 个绝对足够足够。
所以如果你要使用CALLBACK的时候,尽量的想办法复用,否则会很囧。
栈溢出(0xC00000FD, _chkstk)
这个问题比较囧。我封装的DLL里用到了另外同事写的代码,他的风格是把函数写的巨大,在C++下运行一切正常,但是当在GO写的程序里调用的时候,内部抛出了0xC00000FD的错误,也就是栈溢出。
发生的地方是_chkstk。这个函数是VS下的C++编译器在代码生成的时候加进去的,所以别想着通过参数啊什么的干掉了。这个函数的作用是说当你的函数内有超过了一页大小的变量时,编译器会把在函数头插入对_chkstk的调用代码,_chkstk会检查栈的大小是否足够该函数的局部变量使用,如果不够,它会访问栈的GUARD PAGE,然后会触发系统内核检查到该错误,这个时候操作系统会扩展栈的大小。首先这里就有矛盾了,GO语言本身会扩展栈,而为了做到这点就要把堆拿来当栈使用,但是这个时候的问题是,它和OS默认的栈空间不同,导致内核检查到的错误是不可扩展的,这个时候OS也搞不定了,只能让程序挂掉了。而且OS本身支持的栈扩展是有限制的,不像GO实现的栈扩展,WINDOWS下这个扩展最终会触发到一个无法申请的栈地址,如果无法扩展栈,还是要让程序挂掉。可以点这里《What is the purpose of the _chkstk() function》查看更详细的解释。所以问题的本质就是局部变量太大(自己反汇编DLL发现确实有个对象变量体积巨大...),解决方法就是该把变量该放到堆里的放到堆里的就放到堆里。最后的效果是,再也不抛出这个0xC00000FD的异常了。
感想
其实上面用到的C++代码,可以用GO直接重写的,但是考虑到时间成本,最后还是放弃了。CALLBACK的问题我是暂时重写了对应的API代码来搞定。
GO在写网络通信,并发场景时比较嗨皮,但是这种跨语言的交互操作实在是太蛋疼。只能且行且蛋疼了
Update:
这个问题后来被我提交给GO的开发团队了。具体详情可以见这里:https://github.com/golang/go/issues/9457
按照minux的说法就是需要在使用DLL时的,导入runtime/cgo即可。是说编译器编译的时候,发现没有使用CGO的话,就会把系统线程的栈大小设置为64KB,而且是不扩展的;但是如果使用 了CGO,就会变大默认栈,同时变为可扩展的。
最后还是得吐槽下,What the fuck!对于GO的这种使用方法真心不喜欢,实在是太蛋疼。CGO方式还要依赖GCC之类的玩意。WINDOWS下想愉快的玩耍根本不是太可能。改天再说下用GO来的感受。
http://www.cnblogs.com/concurrency/p/4170657.html
Windows平台Go调用DLL的坑(居然有这么多没听过的名词)的更多相关文章
- Windows平台Go调用DLL的坑
最近的项目中,使用了GO来开发一些服务中转程序.业务比较简单,但是有一些业务需要复用原有C++开发的代码.而在WINDOWS,用CGO方式来集成C/C++代码并不是太方便.所以用DLL把C++的代码封 ...
- C++利用模板在Windows上快速调用DLL函数
更新日志 --------- 2021/08/01 更新V2.2 增加 GetHmodule 函数 - 允许用户获取HMODULE以验证加载DLL是否成功. 2021/08/03 更新V2.3 增加 ...
- windows应用中调用DLL一步步试验
试验环境: PC:win10 build 10143 IDE: vs2015 RC WinPhone: win10 build 10136 简单界面,点按钮,算加法 一.主程用C++ 1.新建visu ...
- 动态调用DLL函数有时正常,有时报Access violation的异常
动态调用DLL函数有时正常,有时报Access violation的异常 typedef int (add *)(int a,int b); void test() { hInst=LoadL ...
- c#调用c++ dll 入坑记录
1.DLL引用坑 [DllImport("NetDLL.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConve ...
- 在Windows中实现Java调用DLL(转载)
本文提供调用本地 C 代码的 Java 代码示例,包括传递和返回某些常用的数据类型.本地方法包含在特定于平台的可执行文件中.就本文中的示例而言,本地方法包含在 Windows 32 位动态链接库 (D ...
- Windows下C语言调用dll动态链接库
dll是windows下的动态链接库文件,下面记录一下在windows下如何调用C语言开发的dll动态链接库. 1.dll动态链接库的源代码 hello_dll.c #include "st ...
- war包部署在tomcat下,使用windows service服务方式启动tomcat服务器,在包含调用dll的模块,报dll找不到问题的解决办法
问题描述: 开发了一个需要调用dll的java web程序,在idea开发环境下运行调试没问题,可以正常运行,在tomcat/bin下,运行批处理startup.bat,启动tomcat服务器,也可以 ...
- C/C++:Windows编程—调用DLL程序的2种方法(转载)
文章为转载,原文出处https://blog.csdn.net/qq_29542611/article/details/86618902 前言先简单介绍下DLL.DLL:Dynamic Link Li ...
随机推荐
- Codeforces 468D Tree
题目 给出一棵带边权的树,求一个排列\(p\),使得\(\sum_{i=1}^{n}{dis(i, p_i)}\)的值最大,其中\(dis(v, u)\)表示\(v\)到\(u\)的距离. 算法 这题 ...
- delphi如何加上spliter分割条,任意调整大小
如题1:如何把一个panel分割成四个小的panle 2:也就是如何加上spliter,分割条,任意调整大小 3.如何有独立的handle使用多个总共5个为什么呢,你放4个panel 然后放split ...
- Project configuration is not up-to-date with pom.xml错误解决方法
导入一个Maven项目之后发现有一个如下的错误: Project configuration is not up-to-date with pom.xml. Run project configura ...
- Linux经常使用命令(十二) - less
less 工具也是对文件或其他输出进行分页显示的工具.应该说是linux正统查看文件内容的工具.功能极其强大. less 的使用方法比起 more 更加的有弹性.使用了 less 时.更easy用来查 ...
- ASP.NET - Web.config文件详解
周金桥:asp.net夜话之十一:web.config详解 链接:http://zhoufoxcn.blog.51cto.com/792419/166441/
- Qt显示调用vs中的dll
网上看到很多文章写调用vc的dll,但我尝试了总是出问题,下面结合参考别人的文章,实现了Qt显示调用vs中c接口的dll. 具体直接上代码: vs中的代码: TMax.h: #ifdef TMAX # ...
- 内部框架——axure线框图部件库介绍
网页框架代码<iframe border=0 name=lantk src="要嵌入的网页地址" width=400 height=400 allowTransparency ...
- 基于visual Studio2013解决C语言竞赛题之1064互质数差1验证
题目 解决代码及点评 /* 64. 任意两个互质的自然数, 经过若干次加减后,总可获得结果为1的数值. 所谓互质数(即互素的数),是指这两个数除 1外再没有其它公因数. 如14,9为 ...
- arm-linux-gcc下载与安装
在RHEL 5平台上安装配置arm-linux-gcc 2011-02-23 19:35:40| 分类: 嵌入式开发环境 | 标签: |字号大中小 订阅 . 在linux平台上安装好的基础上,开 ...
- Swift - 使用UIImagePickerController从相册选择照片并展示
1,UIImagePickerController介绍 (1)选择相册中的图片或者拍照,都是通过UIImagePickerController控制器实例化一个对象,然后通过self.presentVi ...