使用GO语言调用Windows API

一、背景

为了更好兼容Windows,有时候需要直接在Go程序里面去调用Windows系统的API,比如在Go程序里面直接控制Windows窗口。

二、环境搭建

WindowsGO的下载、安装和配置

Windows下GO的下载、安装和配置可参考:

GO语言下载、安装、配置


使用Visual Studio Code来搭建GO开发环境

采用微软开源的Visual Studio Code来搭建GO开发环境,可参考:

在Visual Studio Code中配置GO开发环境


在安装过程可能出现golint失败,原因是国内的网络屏蔽,golang.org被和谐。解决方案如下:

1.   在cmd中切换到“GOPATH”目录,利用git下载glint,即执行

git clone https://github.com/golang/lint.git

2.  复制%GOPATH%\src\github.com\golang\lint目录到%GOPATH%\src\golang.org\x


go build编译失败的问题

在windowns下用Go语言的cgo时我们会用到的GCC编译器,如果没有安装GCC编译器,在go build时会遇到如下错误:

cc1.exe: sorry, unimplemented: 64-bit modenot compiled in


一般通过安装MinGW解决,需要安装64位版本,可下载如下posix版本:

http://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/4.8.2/threads-posix/seh/


三、  调用Windows API

概述

我们可以利用syscall包和unsafe包与系统直接通信。可参考:

WindowsDLLs


注意unsafe包操作内存是不安全的,在使用的时候要了解我们能做什么和不能做什么,具体可参考官方文档:

Package unsafe


Windows API文档可参考官方文档:

Windows API Index


要点

加载DLL

第一步就是加载要使用的API所在的DLL。有两种方法:

1. 使用syscall.NewLazyDLL以懒加载方式加载DLL,返回*LazyDLL,只在第一次调用其函数时才加载DLL

比如:

var(

    kernel32DLL =syscall.NewLazyDLL("kernel32.dll")

2. 使用syscall.LoadLibrary来立即加载DLL

比如:

var(

    kernel32,_ = syscall.LoadLibrary("kernel32.dll")

获得函数

与加载DLL方式对应,采用不同方式获得函数:

1. 懒加载时调用dll. NewProc("ProcedureName")

比如:

procOpenProcess = kernel32DLL.NewProc(“OpenProcess”)

2. 立即加载时,调用syscall.GetProcAddress

比如:

findWindow, _ :=syscall.GetProcAddress(user32, "FindWindowW")

调用函数

与加载DLL方式对应,采用不同方式调用函数:

1. 懒加载时调用proc.Call函数

2. 立即加载时调用syscall.Syscall函数及其变体,在当前go1.12.3中,无法调用超过18个参数的函数

数据类型

Windows常见数据类型可以参考如下:

type (

         BOOL          uint32

         BOOLEAN       byte

         BYTE          byte

         DWORD         uint32

         DWORD64       uint64

         HANDLE        uintptr

         HLOCAL        uintptr

         LARGE_INTEGERint64

         LONG          int32

         LPVOID        uintptr

         SIZE_T        uintptr

         UINT          uint32

         ULONG_PTR     uintptr

         ULONGLONG     uint64

         WORD          uint16

)


字符串类型:

Windows中有2种类型:ANSI编码和UTF-16编码。一般使用UTF-16,可利用syscall.UTF16PtrFromString进行转换。


Call和Syscall函数里面传入的Windows

API函数的参数都是uintptr类型,对指针类型需要通过unsafe.Pointer函数来转换,比如:

uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(text)))

对数值类型可以直接转换,NULL可用0表示。

卸载DLL

不再需要使用DLL里的函数之后可以卸载DLL,可使用syscall.FreeLibrary来卸载。

示例

立即加载示例

以下示例以立即加载方式调用MessageBoxW API:

package main


import (

         "fmt"

         "syscall"

         "unsafe"

)


func abort(funcname string, err error) {

         panic(fmt.Sprintf("%sfailed: %v", funcname, err))

}


var (

         kernel32,_        =syscall.LoadLibrary("kernel32.dll")

         user32,_     =syscall.LoadLibrary("user32.dll")

         messageBox,_ = syscall.GetProcAddress(user32, "MessageBoxW")

)


func MessageBox(caption, text string, styleuintptr) (result int) {

         varnargs uintptr = 4

         ret,_, callErr := syscall.Syscall9(uintptr(messageBox),

                   nargs,

                   0,

                   uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(text))),

                   uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(caption))),

                   style,

                   0,

                   0,

                   0,

                   0,

                   0)

         ifcallErr != 0 {

                   abort("CallMessageBox", callErr)

         }

         result= int(ret)

         return

}


func main() {

         defersyscall.FreeLibrary(kernel32)

         defersyscall.FreeLibrary(user32)


         varMB_YESNOCANCEL = 0x00000003

         fmt.Printf("Return:%d\n", MessageBox("Done Title", "This test is Done.",MB_YESNOCANCEL))

}


func init() {

         fmt.Print("StartingUp\n")

}

懒加载示例

以下示例则以懒加载方式调用MessageBoxW API,实现上面示例的同样功能:

package main


import (

         "fmt"

         "syscall"

         "unsafe"

)


func main() {

         varmod = syscall.NewLazyDLL("user32.dll")

         varproc = mod.NewProc("MessageBoxW")

         varMB_YESNOCANCEL = 0x00000003


         ret,_, _ := proc.Call(0,

                   uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("Thistest is Done."))),

                   uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("DoneTitle"))),

                   uintptr(MB_YESNOCANCEL))

         fmt.Printf("Return:%d\n", ret)

}

四、  小结

在GO语言里面调用Windows API主要有两种方式:立即加载方式和懒加载方式。两种方式在获得函数和调用函数上稍有不同,但传参是类似的,需要小心处理。

五、  参考

GO语言下载、安装、配置

在Visual Studio Code中配置GO开发环境

WindowsDLLs

如何用Go调用Windows API

Package unsafe

Windows API Index

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容