最近的项目中,使用了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的坑(居然有这么多没听过的名词)的更多相关文章

  1. Windows平台Go调用DLL的坑

    最近的项目中,使用了GO来开发一些服务中转程序.业务比较简单,但是有一些业务需要复用原有C++开发的代码.而在WINDOWS,用CGO方式来集成C/C++代码并不是太方便.所以用DLL把C++的代码封 ...

  2. C++利用模板在Windows上快速调用DLL函数

    更新日志 --------- 2021/08/01 更新V2.2 增加 GetHmodule 函数 - 允许用户获取HMODULE以验证加载DLL是否成功. 2021/08/03 更新V2.3 增加 ...

  3. windows应用中调用DLL一步步试验

    试验环境: PC:win10 build 10143 IDE: vs2015 RC WinPhone: win10 build 10136 简单界面,点按钮,算加法 一.主程用C++ 1.新建visu ...

  4. 动态调用DLL函数有时正常,有时报Access violation的异常

    动态调用DLL函数有时正常,有时报Access violation的异常 typedef int (add *)(int a,int b); void test() {     hInst=LoadL ...

  5. c#调用c++ dll 入坑记录

    1.DLL引用坑 [DllImport("NetDLL.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConve ...

  6. 在Windows中实现Java调用DLL(转载)

    本文提供调用本地 C 代码的 Java 代码示例,包括传递和返回某些常用的数据类型.本地方法包含在特定于平台的可执行文件中.就本文中的示例而言,本地方法包含在 Windows 32 位动态链接库 (D ...

  7. Windows下C语言调用dll动态链接库

    dll是windows下的动态链接库文件,下面记录一下在windows下如何调用C语言开发的dll动态链接库. 1.dll动态链接库的源代码 hello_dll.c #include "st ...

  8. war包部署在tomcat下,使用windows service服务方式启动tomcat服务器,在包含调用dll的模块,报dll找不到问题的解决办法

    问题描述: 开发了一个需要调用dll的java web程序,在idea开发环境下运行调试没问题,可以正常运行,在tomcat/bin下,运行批处理startup.bat,启动tomcat服务器,也可以 ...

  9. C/C++:Windows编程—调用DLL程序的2种方法(转载)

    文章为转载,原文出处https://blog.csdn.net/qq_29542611/article/details/86618902 前言先简单介绍下DLL.DLL:Dynamic Link Li ...

随机推荐

  1. Python多线程下的_strptime问题

    Python多线程下的_strptime问题 由于Python的datetime和time中的_strptime方法不支持多线程,运行时会报错: import datetimeimport threa ...

  2. Activity 和 Intent

    Activity 和 Intent 一.Intent指向Activity 二.利用 Intent 向第二个 Activity 传数据 三.利用 Intent 接受第二个 Activity 的返回值 四 ...

  3. xcode APP 打包以及提交apple审核详细流程(新版本更新提交审核)

    链接地址:http://blog.csdn.net/mad1989/article/details/8167529 打包发布APP流程真机测试和APP发布流程APP提交审核流程真机测试打包发布上传出错 ...

  4. AspNetCore.Hosting

    Microsoft.AspNetCore.Hosting 有关Hosting的基础知识 Hosting是一个非常重要,但又很难翻译成中文的概念.翻译成:寄宿,大概能勉强地传达它的意思.我们知道,有一些 ...

  5. 终于懂了:WM_PAINT中应该用BeginPaint与EndPaint这两个api,它们的功能正是使无效区域恢复(所以WM_PAINT里即使什么都不做,也必须写上BeginPaint与EndPaint)——Delphi里WM_PAINT消息的三个走向都做到了这一点 good

    程序本来是想实现鼠标单击改变背景颜色.可是,程序运行时,为什么没有任何消息触发,背景颜色就一直不断的改变了?WM_PAINT怎么被触发的 #include <windows.h> #inc ...

  6. /bin/bash^M: 坏的解释器: 没有那个文件或目录

    在Linux下编译cocos2d-x运行脚本的时候出现”/bin/bash^M: 坏的解释器: 没有那个文件或目录“这样的错误如下图. 解决方法: 使用在终端输入sed -i 's/\r$//' ma ...

  7. PEM文件格式具体解析

    PEM文件格式存档 Author:Roson sun sunxiao@tomonline-inc.com Time:2006-4-11 1.  描写叙述: Openssl使用PEM(RFC 1421- ...

  8. c# 未能载入文件或程序集

    近期做项目时碰到这个问题了.goole.百度了半天,整理了下面几种可能: DLL文件名称与载入时的DLL文件名称不一致, DLL文件根本不存在,即出现丢失情况, 载入DLL路径错误,即DLL文件存在, ...

  9. ASP.NET - 后台获取按钮绑定的值CommandArgument

    <asp:LinkButton runat="server" ID="resumelbtn" CommandArgument='<%# Eval(& ...

  10. HDU--杭电--3790--最短路径问题

    最短路径问题 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Sub ...