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来的感触。