golangdll详解

发布时间:2023-05-18

一、golangdll 加载失败

在使用 golangdll 时,有时会出现加载失败的情况。造成这种情况的原因有很多种,比如 dll 文件不存在或路径有误,dll 文件中的函数名与调用方不一致等等。 下面是一个简单的示例,演示了如何在 Go 语言中使用 golangdll。该示例中定义了一个简单的 dll 文件,其中包含了一个名为 "Add" 的函数,可以计算两个整数的和。

// main.go
package main
import (
	"fmt"
	"syscall"
)
func main() {
	// 加载dll
	dll, err := syscall.LoadDLL("mydll.dll")
	if err != nil {
		fmt.Println(err)
		return
	}
	// 查找dll中的函数
	add, err := dll.FindProc("Add")
	if err != nil {
		fmt.Println(err)
		return
	}
	// 调用函数
	ret, _, err := add.Call(1, 2)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(ret)
}

如果 dll 文件不存在,运行该程序后将会输出以下错误信息:

The system cannot find the file specified.

如果 dll 文件中的函数名与调用方不一致,运行该程序后将会输出以下错误信息:

The specified procedure could not be found.

二、golangdll 函数导出

在 golangdll 中,需要将函数导出才能供其他程序调用。通过在函数声明前加上 //export 注释,可以将函数导出。 下面是一个简单的示例,演示了如何在 Go 语言中导出函数。该示例中定义了一个名为 "Add" 的函数,可以计算两个整数的和。

// mydll.go
package main
//export Add
func Add(a, b int) int {
	return a + b
}
import "C"
func main(){}

在 dll 文件中,需要将该函数编译成导出函数。下面是一个使用 MinGW 来编译 dll 文件的示例:

gcc -c mydll.go -o mydll.o
gcc -shared mydll.o -o mydll.dll

三、golangdll 函数调用

在 Go 语言中调用 golangdll 函数与调用普通函数类似,不同之处在于需要使用 syscall 包来加载 dll 文件,并使用 FindProc 方法查找 dll 文件中的函数。 下面是一个简单的示例,演示了如何在 Go 语言中调用 dll 文件中的函数。该示例中定义了一个名为 "Add" 的函数,在 Go 语言中调用该函数计算两个整数的和。

// main.go
package main
import (
	"fmt"
	"syscall"
)
func main() {
	// 加载dll
	dll, err := syscall.LoadDLL("mydll.dll")
	if err != nil {
		fmt.Println(err)
		return
	}
	// 查找dll中的函数
	add, err := dll.FindProc("Add")
	if err != nil {
		fmt.Println(err)
		return
	}
	// 调用函数
	ret, _, err := add.Call(1, 2)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(ret)
}

四、golangdll 调用约定

在使用 golangdll 时,需要了解不同平台下的调用约定。 在 Windows 平台下,所有的函数调用使用的是 stdcall 调用约定。 下面是一个简单的示例,演示了如何在 Windows 平台下使用 stdcall 调用约定:

// mydll.go
package main
//export Add
func Add(a, b int) int {
	return a + b
}
import "C"
func main(){}

在 Linux 平台下,所有的函数调用使用的是 cdecl 调用约定。 下面是一个简单的示例,演示了如何在 Linux 平台下使用 cdecl 调用约定:

// mydll.go
package main
//export Add
func Add(a, b int) int {
	return a + b
}
// 指定调用约定
//go:cgo_export_dynamic Add
//go:cgo_import_dynamic Add Add. libc.so.6
//go:cgo_export_static Add
//go:cgo_export_static_RET Add
import "C"
func main(){}

五、golangdll 路径设置

在使用 golangdll 时,需要指定 dll 文件的路径,否则将无法加载 dll 文件。 在 Windows 平台下,可以通过设置 PATH 环境变量来指定 dll 文件的路径。 在 Linux 平台下,可以通过设置 LD_LIBRARY_PATH 环境变量来指定库文件的路径。 下面是一个简单的示例,演示了如何在 Go 语言中设置 Windows 平台下的 dll 文件路径:

// main.go
package main
import (
	"fmt"
	"os"
	"path/filepath"
	"syscall"
)
func main() {
	// 获取dll文件路径
	path, _ := filepath.Abs("./mydll.dll")
	// 设置dll文件路径
	os.Setenv("PATH", path)
	// 加载dll
	dll, err := syscall.LoadDLL("mydll.dll")
	if err != nil {
		fmt.Println(err)
		return
	}
	// 查找dll中的函数
	add, err := dll.FindProc("Add")
	if err != nil {
		fmt.Println(err)
		return
	}
	// 调用函数
	ret, _, err := add.Call(1, 2)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(ret)
}

下面是一个简单的示例,演示了如何在 Go 语言中设置 Linux 平台下的库文件路径:

// main.go
package main
import (
	"fmt"
	"os"
	"path/filepath"
	"syscall"
)
func main() {
	// 获取库文件路径
	path, _ := filepath.Abs("./libmydll.so")
	// 设置库文件路径
	os.Setenv("LD_LIBRARY_PATH", path)
	// 加载库文件
	dll, err := syscall.LoadLibrary("libmydll.so")
	if err != nil {
		fmt.Println(err)
		return
	}
	// 查找库中的函数
	add, err := syscall.GetProcAddress(dll, "Add")
	if err != nil {
		fmt.Println(err)
		return
	}
	// 调用函数
	ret, _, err := syscall.Syscall(uintptr(add), 2, 1, 2, 0)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(ret)
}