You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Rust中向Windows DLL传递回调函数?VB6/C#已实现

Rust调用带回调的Windows DLL解决方案

问题背景

需要在Rust中调用一个要求传入回调函数的Windows DLL(DeviceDrv.dll),该功能已在VB6和C#中实现,但自行编写的Rust代码编译通过却无法正常工作。

参考实现代码

VB6实现

Public Declare Sub Init Lib "DeviceDrv.dll" Alias "INIT" (ByRef iCallbackAddress As Long)

使用示例:

Public Sub InitialiseCallback(ByVal iStation As Integer, ByVal iValue As Single)
    ' 回调逻辑
End Sub

Call Init(AddressOf InitialiseCallback)

C#实现

[DllImport("DeviceDrv.dll", EntryPoint = "INIT", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, ExactSpelling = false)]
public static extern void Init(IntPtr callback);

使用示例:

public delegate void InitialiseCallback(int station, float value);

public static void InitCallback(int station, float value)
{
    // 回调逻辑
}

// 初始化回调并传入
var cb = new InitialiseCallback(InitCallback);
GCHandle gch = GCHandle.Alloc(cb);
var callbackPointer = Marshal.GetFunctionPointerForDelegate(cb);
Init(callbackPointer);

原Rust代码问题

原代码编译无错但无法工作,核心问题有两点:

  • 错误使用静态链接方式(kind = "static"),但目标是动态链接库
  • 调用约定不匹配(使用extern "C"而非DLL要求的stdcall)

原代码:

#![allow(non_snake_case)]
#[link(name = "DeviceDrv", kind = "static")]
extern {
    pub fn INIT(callback: unsafe extern "C" fn(i32, f32));
}

#[no_mangle]
pub unsafe extern "C" fn cb(station: i32, value: f32) {
    // 回调逻辑
}

crate::dd::INIT(cb);

正确的Rust等效代码

#![allow(non_snake_case)]

// 动态链接Windows DLL,默认使用dylib类型,无需额外指定kind
#[link(name = "DeviceDrv")]
extern "stdcall" {
    // 声明DLL导出的INIT函数,参数为stdcall调用约定的回调指针
    pub fn INIT(callback: unsafe extern "stdcall" fn(i32, f32));
}

// 定义符合stdcall调用约定的回调函数,#[no_mangle]确保函数名不被混淆
#[no_mangle]
pub unsafe extern "stdcall" fn cb(station: i32, value: f32) {
    // 这里编写你的回调处理逻辑
    println!("回调触发:工位={}, 值={}", station, value);
}

// 封装初始化调用
pub unsafe fn init_device() {
    // 传入回调函数指针给DLL
    INIT(cb);
}

关键修正说明

  1. 链接方式修正:移除kind = "static",Rust在Windows平台下默认会按动态链接库(dylib)处理.dll文件,无需显式声明静态链接。
  2. 调用约定匹配:将extern "C"改为extern "stdcall",与VB6、C#代码使用的调用约定保持一致——Windows系统API和多数VB/C#调用的DLL默认使用stdcall,调用约定不匹配会导致栈异常或参数传递错误。
  3. 函数指针类型一致:确保传入INIT的回调函数指针类型,与DLL期望的调用约定、参数类型完全匹配。

如果回调需要长期被DLL调用(程序运行期间多次触发),需确保回调函数不会被编译器优化掉:全局函数默认具备全局生命周期,无需额外处理;若使用闭包作为回调,需用std::mem::forget或持有其所有权以延长生命周期。

内容的提问来源于stack exchange,提问作者user2948475

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 03:05:48