Go语言调用C

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

Leave a comment

Your email address will not be published. Required fields are marked *