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

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


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


流程:

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

需要了解的:

  1. 如何调用C的callback
  2. syscall.FreeLibrary()的使用时机

一.C代码示例

IInterface.h

c 复制代码
#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

c 复制代码
#include "IInterface_C.h"
#include "stdio.h"

//函数的实现
__declspec(dllexport) int __stdcall Init_c(int a, int b)
{
	printf("%d %d\n", a, b);
	return a+b;
}

__declspec(dllexport) int __stdcall GetName_c(int a, int b)
{
	printf("%d %d\n", a, b);
	return 0;
}

int a = 1699637857;
//单纯返回一个常量
__declspec(dllexport) int __stdcall ExpireTime()
{
	return a;
}

二.GO代码示例(不知道为什么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():


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

相关推荐
豆浆Whisky36 分钟前
深入剖析Go Channel:从底层原理到高阶避坑指南|Go语言进阶(5)
后端·go
唐青枫3 小时前
如何用Go写一个benchmark 解析器及Web UI 数据可视化?
go
大鹏dapeng17 小时前
使用 gone.WrapFunctionProvider 快速接入第三方服务(下)—— LLM接入支持 openAI 和 deepseek
go·openai·deepseek
一个热爱生活的普通人1 天前
如何在大模型API调用中实现function calling配合流式输出
llm·go·mcp
王中阳Go1 天前
一文弄懂用Go实现MCP服务
后端·go
jenemy1 天前
《Go 语言权威指南》学习笔记:HTML 和文本模板
go
用户3856714969741 天前
Go Test
go
DemonAvenger1 天前
从 sync.Map 看 Go 并发安全数据结构:原理、实践与踩坑经验
分布式·架构·go
唐青枫1 天前
Go 字符串四种拼接方式的性能对比
go
用户422190773432 天前
06. 使用模板进行交互
go