使用CGO封裝Windows API
Go使用C的庫非常簡單,通過cgo這個(gè)工具基本上可以說是無縫集成了。下面就演示一下用cgo在Windows下面封裝API的過程。注意,請把Go更新到最新一個(gè)Weekly版本。
首先,在$GOPATH\src(如果不知道$GOPATH是什么,請移步這里看詳細(xì)信息)下面新建一個(gè)文件夾“w32api”,然后在其內(nèi)新建一個(gè)文件“kernel32.go”,內(nèi)容如下。
package w32api
// #define WIN32_LEAN_AND_MEAN
// #include <windows.h>
import "C"
import "syscall"
func GetCurrentDirectory() string {
if bufLen := C.GetCurrentDirectoryW(0, nil); bufLen != 0 {
buf := make([]uint16, bufLen)
if bufLen := C.GetCurrentDirectoryW(bufLen, (*C.WCHAR)(&buf[0])); bufLen != 0 {
return syscall.UTF16ToString(buf)
}
}
return ""
}
保存,打開命令行,運(yùn)行
go build w32api
go install w32api
此時(shí),w32api這個(gè)包就編譯完成了,用用看吧。
寫一個(gè)testapp,代碼如下。
package main
import "w32api"
func main() {
println(w32api.GetCurrentDirectory())
}
運(yùn)行之后應(yīng)該就能看到該文件的當(dāng)前目錄在控制臺被打印出來了。感覺如何?是不是簡單到令人發(fā)指了?這就是為什么Go在很短的時(shí)間內(nèi)就擁有了很多第三方庫的秘密,呵呵。
現(xiàn)在重點(diǎn)介紹幾個(gè)要點(diǎn),先從kernel32.go的內(nèi)容說起。
// #define WIN32_LEAN_AND_MEAN// #include <windows.h>
import "C"
這三行應(yīng)該很熟悉,定義了相關(guān)的宏和需要引用的頭文件。這里需要注意的是 import “C” 與上一行注釋之間不能有空行!否則編譯會(huì)失敗。
之后,就可以用"C.”去引用C庫里的函數(shù)了,這個(gè)前綴還可以引用簡單類型,如C.char, C.schar (signed char), C.uchar (unsigned char), C.short, C.ushort (unsigned short), C.int, C.uint (unsigned int), C.long, C.ulong (unsigned long), C.longlong (long long), C.ulonglong (unsigned long long), C.float, C.double。
如果是struct, union和enum的話,需要加上如下前綴,struct_、union_和enum_,比如 C.struct_MSG。
對于字符串的處理比較特殊,cgo提供的字符串處理函數(shù)只能處理char類型,這對于Windows上的程序員來說太不夠了,因?yàn)榇蠖鄶?shù)情況調(diào)用的都是Unicode方式的API。我很早之前就提過Bug,且這個(gè)Bug一度被標(biāo)上了Go1的標(biāo)簽,但最近又被從Go1的范疇里剔除了,理由是wchar_t很少見。
沒辦法了,只能自己先湊活著解決吧!其實(shí)也簡單,wchar_t其實(shí)對應(yīng)到Go的uint16類型,所以如果要用buffer的話,可以用slice來代替,就像上面代碼里寫的方法。
buf := make([]uint16, bufLen)
if bufLen := C.GetCurrentDirectoryW(bufLen, (*C.WCHAR)(&buf[0])); bufLen != 0 {
return syscall.UTF16ToString(buf)
}
如果某個(gè)函數(shù)僅僅只是返回wchar_t指針的話,可以用下面代碼得到Go的string。
func UTF16PtrToString(cstr *uint16) string {
if cstr != nil {
us := make([]uint16, 0, 256)
for p := uintptr(unsafe.Pointer(cstr)); ; p += 2 {
u := *(*uint16)(unsafe.Pointer(p))
if u == 0 {
return string(utf16.Decode(us))
}
us = append(us, u)
}
}
return ""
}
另外,cgo目前在windows下面僅支持配合dll使用,還無法做到靜態(tài)編譯*.lib。
以上就是cgo使用的初步介紹,你已經(jīng)可以開始動(dòng)手自己玩玩了。也許你更感興趣的是如何用Go調(diào)用C++寫的庫,恩,好問題,后面我會(huì)介紹一種更加簡單的封裝方式——swig,這個(gè)工具大家也許已經(jīng)知道了,它能自動(dòng)生成封裝層!盡請期待吧!
浙公網(wǎng)安備 33010602011771號