使用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 mode not 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")

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

比如:

var(

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

获得函数

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

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

比如:

procOpenProcess = kernel32DLL.NewProc(“OpenProcess”)

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

比如:

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

调用函数

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

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

数据类型

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

type (

         BOOL          uint32

         BOOLEAN       byte

         BYTE          byte

         DWORD         uint32

         DWORD64       uint64

         HANDLE        uintptr

         HLOCAL        uintptr

         LARGE_INTEGER int64

         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("%s failed: %v", funcname, err))

}



var (

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

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

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

)



func MessageBox(caption, text string, style uintptr) (result int) {

         var nargs 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)

         if callErr != 0 {

                   abort("Call MessageBox", callErr)

         }

         result = int(ret)

         return

}



func main() {

         defer syscall.FreeLibrary(kernel32)

         defer syscall.FreeLibrary(user32)



         var MB_YESNOCANCEL = 0x00000003

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

}



func init() {

         fmt.Print("Starting Up\n")

}

懒加载示例

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

package main



import (

         "fmt"

         "syscall"

         "unsafe"

)



func main() {

         var mod = syscall.NewLazyDLL("user32.dll")

         var proc = mod.NewProc("MessageBoxW")

         var MB_YESNOCANCEL = 0x00000003



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

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

                   uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("Done Title"))),

                   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

  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值