在Windows中调用Golang编写的DLL库

在Windows中调用Golang编写的DLL库 如何在Golang项目中使用newlazydll调用Golang库(dll)?(Windows环境)

错误信息: 致命错误:运行时执行期间出现意外信号 [信号 0xc0000005 代码=0x0 地址=0x0 程序计数器=0x623d43dd]

6 回复

目前插件仅支持 Linux 和 macOS 系统。

更多关于在Windows中调用Golang编写的DLL库的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


您好

如果我们没有获得更多信息,确实很难理解这种情况发生的原因。您想要实现什么功能?您采取了哪些步骤来达成目标?是否使用了任何代码?所以请尽可能详细说明,这样获得帮助的可能性就会增加。

此致,Johan

你好

第一个程序 dll.go 与第二个程序 project.go 几乎完全相同。你是不是贴错了 dll.go 程序的源代码?另外给你一个建议:如果你想让你的 Go 代码格式美观,可以在编辑框中用两行 go 和 将代码包裹起来(这三个字符是反引号)

johandalabacka:

再次问候

第一个程序 dll.go 几乎与第二个程序 project.go 完全相同。您是否粘贴了错误的 dll.go 程序源代码?另外提醒一下:如果您希望 Go 代码格式美观,请在编辑框中将其粘贴在 go 和 之间(这三个字符是反引号)

package main

import "C"

//export PrintBye
func PrintBye() string {
	return "HELLO"
}

func main() {
	// Need a main function to make CGO compile package as C shared library
}

我测试后得到了相同的结果。我尝试用调试器逐步跟踪,发现它在系统调用深处失败,实际上是在调用这段代码时:

ptr := proc.Addr()
r1, r2, err := syscall.Syscall(ptr, 0, 0, 0, 0)

我觉得我无法提供更多帮助了。我猜你做这个只是作为实验。如果你想在Go中真正使用用Go编写的动态库,可以使用新的模块系统,请参考:

Medium图标

使用插件编写模块化Go程序 – 学习Go编程语言…

缩略图

隐藏在Go 1.8版本引入的众多重要特性中的是一个新的Go插件系统。这个功能让程序员能够…

阅读时间:7分钟

在Windows环境中使用syscall.NewLazyDLL调用Golang编写的DLL时遇到"意外信号"错误(通常由内存访问违规引起),这通常是由于调用约定不匹配或参数传递错误导致的。以下是正确的实现方法:

1. 首先确保DLL是用正确的构建标签编译的

编译DLL时需要使用CGO并指定导出函数:

// main.go
package main

import "C"

//export Add
func Add(a, b int) int {
    return a + b
}

//export Hello
func Hello(name *C.char) *C.char {
    return C.CString("Hello, " + C.GoString(name))
}

func main() {
    // 空main函数,DLL不需要执行主逻辑
}

编译命令:

go build -buildmode=c-shared -o mylib.dll main.go

2. 正确的调用方式

package main

import (
    "fmt"
    "syscall"
    "unsafe"
)

func main() {
    // 加载DLL
    dll := syscall.NewLazyDLL("mylib.dll")
    
    // 获取函数地址
    addFunc := dll.NewProc("Add")
    helloFunc := dll.NewProc("Hello")
    
    // 调用Add函数(两个int参数,返回int)
    a, b := 5, 3
    ret, _, err := addFunc.Call(uintptr(a), uintptr(b))
    if err != syscall.Errno(0) {
        fmt.Printf("调用Add失败: %v\n", err)
        return
    }
    fmt.Printf("Add(%d, %d) = %d\n", a, b, int32(ret))
    
    // 调用Hello函数(字符串参数)
    name := "World"
    cname := syscall.StringToUTF16Ptr(name)
    ret, _, err = helloFunc.Call(uintptr(unsafe.Pointer(cname)))
    if err != syscall.Errno(0) {
        fmt.Printf("调用Hello失败: %v\n", err)
        return
    }
    
    // 处理返回的字符串
    defer syscall.FreeLibrary(dll.Handle)
    result := syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(ret))[:])
    fmt.Printf("Hello结果: %s\n", result)
}

3. 处理内存管理的完整示例

package main

import (
    "fmt"
    "syscall"
    "unsafe"
)

// 定义DLL函数原型
var (
    myDLL          *syscall.LazyDLL
    addFunc        *syscall.LazyProc
    helloFunc      *syscall.LazyProc
    freeStringFunc *syscall.LazyProc
)

func init() {
    myDLL = syscall.NewLazyDLL("mylib.dll")
    addFunc = myDLL.NewProc("Add")
    helloFunc = myDLL.NewProc("Hello")
    freeStringFunc = myDLL.NewProc("FreeString") // 假设DLL中有释放内存的函数
}

func CallAdd(a, b int) (int, error) {
    ret, _, err := addFunc.Call(uintptr(a), uintptr(b))
    if err != syscall.Errno(0) {
        return 0, err
    }
    return int(ret), nil
}

func CallHello(name string) (string, error) {
    cname, err := syscall.BytePtrFromString(name)
    if err != nil {
        return "", err
    }
    
    ret, _, err := helloFunc.Call(uintptr(unsafe.Pointer(cname)))
    if err != syscall.Errno(0) {
        return "", err
    }
    
    // 转换返回的C字符串为Go字符串
    result := syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(ret))[:])
    
    // 释放DLL分配的内存
    if freeStringFunc.Find() == nil {
        freeStringFunc.Call(uintptr(unsafe.Pointer(ret)))
    }
    
    return result, nil
}

func main() {
    // 测试Add函数
    result, err := CallAdd(10, 20)
    if err != nil {
        fmt.Printf("错误: %v\n", err)
        return
    }
    fmt.Printf("10 + 20 = %d\n", result)
    
    // 测试Hello函数
    msg, err := CallHello("Golang")
    if err != nil {
        fmt.Printf("错误: %v\n", err)
        return
    }
    fmt.Printf("消息: %s\n", msg)
}

关键注意事项:

  1. 参数类型转换:确保所有参数正确转换为uintptr
  2. 调用约定:Golang DLL使用stdcall调用约定
  3. 内存管理:字符串参数需要正确转换,返回的字符串需要适当处理
  4. 错误处理:检查syscall.Errno(0)来判断调用是否成功

信号0xc0000005错误通常是由于访问了无效的内存地址,确保函数签名和参数传递完全匹配可以解决这个问题。

回到顶部