Go语言十分简单,跨平台,非常适用于网络应用的开发,本人相当看好。不过很多时候,还是需要扩展Go让Go实现更多的功能,如操作底层硬件、创建窗口等,这个要求可以通过调用C的方式实现。
本文以windows下的Go开发为例,linux完全一样。当然,Windows上首先要安装MinGW.
http://www.hoverlees.com/blog/?p=1465
先做一个GO显示一个对话框窗口的简单示例。在go目录src/pkg下新建一个文件夹名为hover,新建一个hover.go文件,内容如下
package hover //#include <windows.h> import "C" func Msgbox(title string,body string) int{ C.MessageBox(nil,(*C.CHAR)(C.CString(body)),(*C.CHAR)(C.CString (title)),0); return 0; }
与其说这是一个go源码文件,还不如把它理解成C语言的模板文件,cgo编译时,会把这个文件的内容转换成C程序并使用gcc编译,所以本人强列推荐将这个程序当成C程序阅读。程序中第二行的注释会在转换成C语言时做为C的代码。Msgbox会调用Windows系统函数MessageBox,并传入对应参数。由于go的数据类型与C语言的数据类型在内存中的表示不同,所以需要转换。Go与C的数据类型对有对应的转换方式,具体可以参看文档。因为MessageBox的第二第三个参数是LPSTR(unsigned char*),所以在调用时需要将Go的string转换成LPSTR类型。
接下来编译和安装函数包:
go build hover go install hover
编译完成后,可以在程序中import这个hover库。下面写一个简单的程序调用刚刚写好的函数
package main import "hover" func main(){ hover.Msgbox("title","body"); }
最终生成的程序会弹出一个对话框,标题和内容都按程序指定的显示。
现实应用中,一般C语言提供的函数有很多复杂的逻辑要处理,如果像上面那样写,C又不像C,go有太多的类型转换又显得太复杂,真不是个好办法.所以希望C语言把所有逻辑在一个或几个函数中实现,交由golang调用函数的方式。
刚刚的例子,调用的是系统函数库提供的功能,如果要使用自己的函数库也很简单,自己的函数库跟常规的动态链接库实现一样(windows的dll,linux的so等),目前Go好像只支持动态库。静态链接方式可以生成go的包文件,但使用时会报not defined错误。不知道以后会不会支持。
下面就来实现一个自定义的函数库
//hover.h #include <windows.h> void HoverCreateMessageBox(char* title,char* body,int flag); //hover.c #include "hover.h" void HoverCreateMessageBox(char* title,char* body,int flag){ //这儿还可以做很多事 MessageBox(0,body,title,flag); }
这个库导出HoverCreateMessageBox函数,供接下来的Go调用。
编译成动态链接库:
gcc -c -o hover.o hover.c gcc -shared -o hover.dll hover.o -Wl,--out-implib,libhover.a
接下来我们要在上面实现的hover包中添加一个调用自己函数库的函数
package hover //#cgo LDFLAGS: -lhover //#include <windows.h> //#include <hover.h> import "C" func Msgbox(title string,body string) int{ C.MessageBox(nil,(*C.CHAR)(C.CString(body)),(*C.CHAR)(C.CString (title)),0); return 0; } func CreateMsgbox(title string,body string,flag int){ C.HoverCreateMessageBox(C.CString(body),C.CString(title),C.int (flag)); }
这个比刚刚的时候多了一些东西,首先是头部,加入了//#cgo LDFLAGS: -lhover 一行,这个是gcc的链接参数,也就是我们刚刚实现的库需要链接到生成的文件中,不然无法调用,这个跟C完全一样的理解,只是libhover.a需要放到include目录下,如果不是,需要指定CFLAG。然后多引入了hover.h头文件(我把它放到include所在的目录下了,如果不愿意放,可以添加//#cgo CFLAGS: -Imyincludedir ,这个作为gcc编译时的参数,也是C的知识。
后面添加了一个函数,调用刚刚库中的函数。
再次编译函数包:
go build hover go install hover
最后我们生成了hover包,写一个测试程序来调用一下
package main import "hover" func main(){ hover.Msgbox("title","body"); hover.CreateMsgbox("title","body",64);//MB_ICONINFORMATION=64 }
生成的程序顺利执行了我们实现的函数,输出了infomation图标的对话框,要调用系统的窗口功能也用同样的方式实现。
注意:例子中传的参数是英文字符串,如果要使用中文字符串,需要进行编码转换,因为windows使用gbk编码而go使用utf8,编码转换在go中做,如果是自己实现的函数库,也可以在C语言中转换.
其他注意事项:
1. go的包里的导出函数,第一个字母必须为大写,否则该函数不导出。
2. linux上动态库编译: gcc -c -o hover.o hover.c gcc -o libhover.so -shared -fPic hover.o