使用CGO封裝Windows API


Go使用C的庫非常簡單,通過cgo這個工具基本上可以說是無縫集成了。下面就演示一下用cgo在Windows下面封裝API的過程。注意,請把Go更新到最新一個Weekly版本。

首先,在$GOPATH\src(如果不知道$GOPATH是什么,請移步這里看詳細信息)下面新建一個文件夾“w32api”,然后在其內新建一個文件“kernel32.go”,內容如下。

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 ""
}

保存,打開命令行,運行

go build w32api

go install w32api

此時,w32api這個包就編譯完成了,用用看吧。

寫一個testapp,代碼如下。

package main

import "w32api"

func main() {
    println(w32api.GetCurrentDirectory())
}
運行之后應該就能看到該文件的當前目錄在控制台被打印出來了。感覺如何?是不是簡單到令人發指了?這就是為什么Go在很短的時間內就擁有了很多第三方庫的秘密,呵呵。

現在重點介紹幾個要點,先從kernel32.go的內容說起。

// #define WIN32_LEAN_AND_MEAN
// #include <windows.h>
import "C"

這三行應該很熟悉,定義了相關的宏和需要引用的頭文件。這里需要注意的是 import “C” 與上一行注釋之間不能有空行!否則編譯會失敗。

之后,就可以用"C.”去引用C庫里的函數了,這個前綴還可以引用簡單類型,如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提供的字符串處理函數只能處理char類型,這對於Windows上的程序員來說太不夠了,因為大多數情況調用的都是Unicode方式的API。我很早之前就提過Bug,且這個Bug一度被標上了Go1的標簽,但最近又被從Go1的范疇里剔除了,理由是wchar_t很少見。

沒辦法了,只能自己先湊活着解決吧!其實也簡單,wchar_t其實對應到Go的uint16類型,所以如果要用buffer的話,可以用slice來代替,就像上面代碼里寫的方法。

buf := make([]uint16, bufLen)
if bufLen := C.GetCurrentDirectoryW(bufLen, (*C.WCHAR)(&buf[0])); bufLen != 0 {
    return syscall.UTF16ToString(buf)
}
如果某個函數僅僅只是返回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使用,還無法做到靜態編譯*.lib。

 

以上就是cgo使用的初步介紹,你已經可以開始動手自己玩玩了。也許你更感興趣的是如何用Go調用C++寫的庫,恩,好問題,后面我會介紹一種更加簡單的封裝方式——swig,這個工具大家也許已經知道了,它能自動生成封裝層!盡請期待吧!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM