• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

Go标准库syscall调用dll

武飞扬头像
小许code
帮助1

什么是系统调用

  了解syscall包之前先了解下什么是系统调用。系统调用是程序向操作系统内核请求服务的过程,通常包含硬件相关的服务(例如访问硬盘),创建新进程等。系统调用提供了一个进程和操作系统之间的接口。

fmt中的syscall

  最常见的关于syscall的使用是在fmt.Println中,具体代码的大家可以一步步往下看怎么调用的,这里使用了系统的syscall.Stdout

func Println(a ...interface{}) (n int, err error) {
	return Fprintln(os.Stdout, a...)
}

Stdout = NewFile(uintptr(syscall.Stdout), "/dev/stdout")

Go调用dll库

  dll是windows动态库,go去调用动态库使用的是syscall标准库,一般dll库会提供两个固定函数,申请内存和释放内存,先申请完内存再执行业务逻辑的函数,执行完后释放内存。

dll, err := syscall.LoadDLL("scan.dll")
//根据名称从dll中查找proc
MemoryStream_Get = dll.FindProc("AllocateMemory")
MemoryStream_Get.Call()

  主要就是三步:LoadDLL加载dll文件名,然后用FindProc判断查找调用的dll库函数名,然后Call进行调用。整体的调用方式还是比较简单。

  LoadDLL会返回一个结构体DLL,注意Handle是一个uinptr类型(uintptr 是 Go 内置类型,表示无符号整数,可存储一个完整的地址,常用于指针运算),也就是会返回方法的地址值,后面的传参和解析通过结合只 unsafe.Pointer 类型转换成 uintptr 类型,做完加减法后,转换成 unsafe.Pointer,通过 * 操作,取值或者修改值都可以。

type DLL struct {
	Name   string
	Handle Handle
}

调用dll如何传参

  Proc的Call()方法是可接收多个uintptr的所以在传参的试试需要将参数转为uintptr

func (p *Proc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {

  传递整型参数,将整型转为uintptr

func IntToPtr(n int) uintptr {
	return uintptr(n)
}

  传递字符串参数,将字符串转为uintptr,但是这里使用到了unsafe.Pointer,它是可以指向任意类型的指针,而syscall.StringBytePtr是将string转为 *btye指针。进而一步步转为uintptr

func StringToUintPtr(val string) uintptr {
	return uintptr(unsafe.Pointer(syscall.StringBytePtr(val)))
}

接收dll库中返回值

  读取调用库的返回值其实真正涉及到指针偏移的计算。因为uintptr指向的实际的整型地址值(申请内存方法会返回),我们可以根据返回的比如字符串在内存中长度来进行计算,比如长度是length(业务函数:GetDeviceInfo会返回),mem是实际调用dll库后返回的uintptr。因为调用dll库一般是返回char,所以这里转为byte即可。

type MemoryStream struct {
	handle    uintptr //空间地址值
	lenHandle uintptr //数据长度
}
//获取设备信息 m.handle是申请内存的地址
func (m *MemoryStream) GetDeviceInfo() (result string) {
	pd, _, _ := GetDeviceInfo.Call(m.handle)
	m.lenHandle = pd
	result = string(m.Bytes())
	return result
}
//返回内存的数据内容byte[]
func (m *MemoryStream) Bytes() []byte {
	buffer := new(bytes.Buffer)
	length := m.Size()
	mem := m.Memory()
	if length == 0 {
		return []byte{}
	}
	//根据长度,unsafe.Pointer进行指针运算
	for i := int64(0); i < length; i   {
		buffer.WriteByte(*(*byte)(unsafe.Pointer(mem   uintptr(i)))) //byte是uint8, sizeof长度是1
	}
	return buffer.Bytes()
}

总结

  syscall库支持对dll库的调用,当然它的功能很强大,可以实现很多我们没有接触过的业务场景。调用的方式比较清晰,但是设计到传参和解析返回值的时候需要用到unsafe.Pointer和uintpre之间的转换、dll库返回的char强制转换为byte,这一块有点逻辑转换。下次专门做个笔记记录下指针、uintptr、unsafe.Pointer之间的使用。

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgfbkgh
系列文章
更多 icon
同类精品
更多 icon
继续加载