Golang 调用 Visual Studio 2022 编译的C动态链接库DLL(入门)

2023年 10月 11日 73.5k 0

主要记录一下调用方法和不同数据类型的传参和转换.

需要注意的: 在go中调用C的方法,基本上参数值/类型和返回类型都为uintptr(uint ptr 应该是无符号整型指针类型),具体可以看下边代码和注释

流程:

  • 先编写C代码,然后生成动态dll
  • 将dll放到需要调用的路径下(go代码可以指定路径)
  • 编写go代码,测试运行结果.
  • 需要了解的:

  • 如何调用C的callback
  • syscall.FreeLibrary()的使用时机
  • 一.C代码示例

    IInterface.h

    #ifdef LAUNCH_H
    #define LAUNCH_H
    
    #define LAUNCH_C_API __declspec(dllexport)
    
    #ifdef __cplusplus
    extern "C" //告诉编译器是C语言代码
    {
    #endif
    	LAUNCH_C_API int __stdcall Init_c(int a, int b);
    	LAUNCH_C_API int __stdcall GetName_c(int a, int b);
    	LAUNCH_C_API int __stdcall ExpireTime();
    
    #ifdef __cplusplus
    }
    #endif
    
    #endif // LAUNCH_H
    
    

    launch.c

    #include "IInterface_C.h"
    #include "stdio.h"
    
    //函数的实现
    __declspec(dllexport) int __stdcall Init_c(int a, int b)
    {
    	printf("%d %dn", a, b);
    	return a+b;
    }
    
    __declspec(dllexport) int __stdcall GetName_c(int a, int b)
    {
    	printf("%d %dn", a, b);
    	return 0;
    }
    
    int a = 1699637857;
    //单纯返回一个常量
    __declspec(dllexport) int __stdcall ExpireTime()
    {
    	return a;
    }
    

    二.GO代码示例(不知道为什么GO代码没格式化???)

    package main  
      
    import (  
    "fmt"  
    "os"  
    "syscall"  
    "time"  
    "unsafe"  
    )  
      
    // 调用win api  
    var (  
    // user32, _ = syscall.LoadLibrary("user32.dll")  
    // messageBox, _ = syscall.GetProcAddress(user32, "MessageBoxW")  
    )  
      
    func main() {  
    
    //加载dll
    dll, err := syscall.LoadDLL("test.dll")  //这里可以参数可以指定路径
    if err != nil {  
    fmt.Println("dll未找到:", err)  
    return  
    }  
    
    //找C函数
    //proc, err := dll.FindProc("Init_c")  //C函数1
    proc, err := dll.FindProc("ExpireTime") //C函数2
    if err != nil {  
    fmt.Println("没有找到对应函数:", err)  
    return  
    }  
    
    //调用C函数,并传值
    //call, _, err := proc.Call(TestNum1(1, 2), TestNum2(3, 4))  
    //if err != nil {  
    //fmt.Println("TestNum1 call 的err:", err)//一般err都不为空,此处err信息可以判断具体执行结果
    //}
    
    //此处call的类型可以直接和数值类型进行对比.
    if call > 0 {  
    fmt.Println("TestNum1 call 的结果:", call)  
    }  
    //启一个go程测试第二个C函数
    go func() {  
    for {  
    call, _, err := proc.Call(GetExpireTime()) //这里直接调用的一个空方法,实际上做法很多,还没有具体测试 
    if err != nil {  
    fmt.Println("GetExpireTime call 的err:", err)  
    }  
    if call > 0 {  
    fmt.Println("GetExpireTime call 的结果:", int64(call))  
    }  
    res := CalTime(int64(call))  //传入dll返回的unix时间戳做对比,返回类型为bool
    if res == false {  
    SendMessageBox("过期提醒", "时间到了..")  //这里自己封装了一个win api的原生弹框
    os.Exit(0) //实际上弹框会阻塞进程退出.简单测试
    }  
    fmt.Println("距离过期还有:", int64(call)-time.Now().Unix(), "秒")  
    time.Sleep(1 * time.Second)  
    }  
    }()  
    select {}  
    }  
      
      //------------------------------------------一些函数实现
      
      //封装的winAPi方法,具体使用可以去msdn查询
      //此处有个疑惑,有一些dll的调用需要释放,还没研究.
    func SendMessageBox(title, msg string) {  
    user32 := syscall.NewLazyDLL("user32.dll")  
    msgBox := user32.NewProc("MessageBoxW")  
    msgBox.Call(IntPtr(0), strToPtr(msg), strToPtr(title), IntPtr(0))  
    }  
      
    // 计算时间  
    func CalTime(ExpireTime int64) bool {  
    if time.Now().Unix() >= ExpireTime {  
    fmt.Println("时间过期")  
    return false  
    }  
    fmt.Println("-----------")  
    return true  
    }  
    
    //字符串转指针,实际结果为*uint16
    func strToPtr(s string) uintptr {  
    b, _ := syscall.UTF16PtrFromString(s)
    return uintptr(unsafe.Pointer(b))  
    }  
      
    //方法名字起的不是很好,实际上是转换为uintptr类型
    func IntPtr(i int) uintptr {  
    return uintptr(i)  
    }  
    
    // 正常数值  
    func TestNum1(x, y int) uintptr {  
    z := x + y  
    return uintptr(z)  
    }  
    // 正常数值
    func TestNum2(x, y int) uintptr {  
    z := x + y  
    return uintptr(z)  
    }  
      
    // 返回字符串(本身字符串就是一个指针,并不是基础数据类型)  
    func testString(s string) uintptr {  
    b, _ := syscall.BytePtrFromString(s)  
    return uintptr(unsafe.Pointer(b))  
    }  
      
    // 返回数值指针  
    func testNumPtr(i *int32) uintptr {  
    return uintptr(unsafe.Pointer(i))  
    }
    

    测试结果调用C函数:Init_c():

    image.png

    测试结果调用C函数:ExpireTime():

    image.png

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论