Rust:如何开发32位的DLL动态库

第一部分 完整示例

以下是一个完整的32位Rust DLL示例,包含所有必要的步骤和代码:

完整示例:创建32位DLL

1. 确保安装32位工具链
bash 复制代码
rustup target add i686-pc-windows-msvc
2. 创建项目
bash 复制代码
cargo new my_32bit_dll --lib
cd my_32bit_dll
3. 修改 Cargo.toml
toml 复制代码
[package]
name = "my_32bit_dll"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]  # 关键:生成动态链接库

[dependencies]
windows = { version = "0.54", features = [
    "Win32_System_Diagnostics_Debug",
    "Win32_Foundation"
]}
4. 修改 src/lib.rs
rust 复制代码
use std::ffi::{c_void, CString};
use windows::Win32::System::Diagnostics::Debug::OutputDebugStringA;

// 简单的DLL入口点(可选)
#[no_mangle]
#[allow(non_snake_case)]
pub extern "stdcall" fn DllMain(
    _hinst_dll: *const c_void,
    reason: u32,
    _reserved: *mut c_void,
) -> bool {
    match reason {
        1 => debug_print("DLL_PROCESS_ATTACH"),
        0 => debug_print("DLL_PROCESS_DETACH"),
        2 => debug_print("DLL_THREAD_ATTACH"),
        3 => debug_print("DLL_THREAD_DETACH"),
        _ => {}
    }
    true
}

// 导出函数示例1:数学运算
#[no_mangle]
pub extern "C" fn calculate(a: i32, b: i32) -> i32 {
    (a * b) + (a + b)
}

// 导出函数示例2:字符串转换
#[no_mangle]
pub extern "C" fn to_uppercase(input: *const i8) -> *mut i8 {
    if input.is_null() {
        return std::ptr::null_mut();
    }
    
    let c_str = unsafe { std::ffi::CStr::from_ptr(input) };
    let r_str = c_str.to_string_lossy().to_uppercase();
    
    let c_string = match std::ffi::CString::new(r_str) {
        Ok(s) => s,
        Err(_) => return std::ptr::null_mut(),
    };
    
    // 返回新分配的内存(调用者需要释放)
    let ptr = unsafe { libc::malloc(c_string.as_bytes().len() + 1) } as *mut i8;
    if ptr.is_null() {
        return std::ptr::null_mut();
    }
    
    unsafe {
        std::ptr::copy_nonoverlapping(
            c_string.as_ptr() as *const i8,
            ptr,
            c_string.as_bytes().len() + 1
        );
    }
    
    ptr
}

// 导出函数示例3:注册回调
pub type Callback = unsafe extern "C" fn(i32, *const i8);

#[no_mangle]
pub extern "C" fn process_with_callback(values: *const i32, count: i32, callback: Callback) {
    if values.is_null() || callback.is_null() {
        return;
    }
    
    let values_slice = unsafe { std::slice::from_raw_parts(values, count as usize) };
    
    for (i, &value) in values_slice.iter().enumerate() {
        let message = format!("Processing #{}: {}", i + 1, value);
        let c_message = CString::new(message).unwrap();
        unsafe { callback(value, c_message.as_ptr()) };
    }
}

// 辅助函数:输出调试信息
fn debug_print(msg: &str) {
    let msg_with_newline = CString::new(format!("[MyDLL] {}\n", msg)).unwrap();
    unsafe {
        OutputDebugStringA(msg_with_newline.as_ptr());
    }
}

// 重要:释放内存的函数
#[no_mangle]
pub extern "C" fn free_memory(ptr: *mut c_void) {
    if !ptr.is_null() {
        unsafe { libc::free(ptr) };
    }
}
5. 编译32位DLL
bash 复制代码
cargo build --target i686-pc-windows-msvc --release

生成的DLL路径:
target/i686-pc-windows-msvc/release/my_32bit_dll.dll

C++ 测试代码 (test_dll.cpp)

cpp 复制代码
#include <Windows.h>
#include <iostream>
#include <vector>

typedef int(__cdecl* CalculateFunc)(int, int);
typedef char*(__cdecl* ToUpperFunc)(const char*);
typedef void(__cdecl* ProcessCallbackFunc)(int, const char*);
typedef void(__cdecl* ProcessWithCallbackFunc)(const int*, int, ProcessCallbackFunc);
typedef void(__cdecl* FreeMemoryFunc)(void*);

void callback(int value, const char* message) {
    std::cout << "Callback: " << value << " - " << message << std::endl;
}

int main() {
    // 加载DLL
    HMODULE dll = LoadLibraryA("my_32bit_dll.dll");
    if (!dll) {
        std::cerr << "无法加载DLL! 错误码: " << GetLastError() << std::endl;
        return 1;
    }

    // 获取函数指针
    CalculateFunc calculate = (CalculateFunc)GetProcAddress(dll, "calculate");
    ToUpperFunc to_upper = (ToUpperFunc)GetProcAddress(dll, "to_uppercase");
    ProcessWithCallbackFunc process = (ProcessWithCallbackFunc)GetProcAddress(dll, "process_with_callback");
    FreeMemoryFunc free_mem = (FreeMemoryFunc)GetProcAddress(dll, "free_memory");
    
    if (!calculate || !to_upper || !process || !free_mem) {
        std::cerr << "无法找到函数!" << std::endl;
        FreeLibrary(dll);
        return 1;
    }

    // 测试calculate函数
    std::cout << "calculate(3, 5) = " << calculate(3, 5) << std::endl;  // 应该输出 3*5 + 3+5 = 15+8 = 23

    // 测试to_uppercase函数
    const char* input = "Hello Rust DLL!";
    char* result = to_upper(input);
    if (result) {
        std::cout << "Uppercase: " << result << std::endl;
        free_mem(result);  // 必须释放内存
    }

    // 测试回调函数
    std::vector<int> numbers = {10, 20, 30, 40};
    process(numbers.data(), (int)numbers.size(), callback);

    // 清理
    FreeLibrary(dll);
    std::cout << "测试完成! 按回车键退出..." << std::endl;
    std::cin.get();
    return 0;
}

编译和运行注意事项

  1. 编译C++测试程序

    • 使用Visual Studio命令提示符 (x86):

      bash 复制代码
      cl /EHsc test_dll.cpp
  2. 关键要点

    • 调用约定:使用 __cdecl (默认) 或 __stdcall
    • 内存管理:
      • DLL分配的内存必须由DLL提供的函数释放
      • 字符串操作遵循"谁分配谁释放"原则
    • 错误处理:检查所有返回值和空指针
    • 类型兼容:
      • i32int
      • *const i8const char*
      • *mut c_voidvoid*
  3. 部署要求

    • 32位DLL只能用于32位应用程序
    • 依赖的Rust运行时静态链接到DLL中
    • 不需要额外安装Rust运行时

这个示例展示了三种常见的DLL使用场景:简单数学运算、字符串处理和回调函数,包含了完整的内存管理实践。

第二部分 如何安装32位编译环境

看起来你的构建环境缺少32位目标所需的编译工具链和库。这个问题通常可以通过以下步骤解决:

解决方案:

  1. 安装32位目标平台

    bash 复制代码
    rustup target add i686-pc-windows-msvc
  2. 安装Visual Studio构建工具(如果尚未安装):

    • 打开Visual Studio Installer
    • 确保已安装:
      • "使用C++的桌面开发"工作负载
      • "MSVC v143 - VS 2022 C++ x64/x86 build tools"
      • "Windows 10 SDK (10.0.19041.0)或更新版本"
  3. 设置MSVC环境变量

    bash 复制代码
    vcvarsall.bat x86
  4. 清理并重建

    bash 复制代码
    cargo clean
    cargo build --target i686-pc-windows-msvc --release

如果问题仍然存在:

1. 检查工具链状态:
bash 复制代码
rustup show

确认i686-pc-windows-msvc出现在已安装目标列表中

2. 验证MSVC安装:

运行开发者命令行提示符并检查:

cmd 复制代码
cl /?

应该看到32位x86架构选项

3. 简化测试(验证工具链):

创建一个新的测试项目:

bash 复制代码
cargo new test_dll --lib
cd test_dll

修改Cargo.toml:

toml 复制代码
[lib]
crate-type = ["cdylib"]

修改src/lib.rs:

rust 复制代码
#[no_mangle]
pub extern "C" fn simple_add(a: i32, b: i32) -> i32 {
    a + b
}

尝试构建:

bash 复制代码
cargo build --target i686-pc-windows-msvc --release
4. 替代方案(如果MSVC问题无法解决):

使用GNU工具链替代:

bash 复制代码
rustup target add i686-pc-windows-gnu
cargo build --target i686-pc-windows-gnu --release

常见问题诊断:

  1. 工具链冲突

    • 确保没有RUSTFLAGS环境变量覆盖
    • 尝试更新Rust: rustup update
  2. Windows SDK问题

  3. 检查系统路径

    cmd 复制代码
    where link.exe
    where cl.exe

    确保显示的是Visual Studio工具路径

完成这些步骤后,你应该能成功编译32位DLL。如果仍然遇到问题,可以运行cargo build -vv --target i686-pc-windows-msvc --release查看详细构建日志以定位具体问题。

相关推荐
白应穷奇3 小时前
Diesel的高性能特性: 深入理解Rust ORM的性能优化
后端·rust
ChinaRainbowSea6 小时前
Windows 安装 RabbitMQ 消息队列超详细步骤(附加详细操作截屏)
java·服务器·windows·后端·rabbitmq·ruby
十五年专注C++开发6 小时前
vcpkg: 一款免费开源的C++包管理器
linux·开发语言·c++·windows·包管理器
Warren9811 小时前
Vue2博客项目笔记(第一天)
java·开发语言·javascript·vue.js·windows·笔记·ecmascript
jie*18 小时前
小杰数据结构——题库——拂衣便欲沧海去,但许明月随吾身
数据结构·windows·python
Hello.Reader20 小时前
Rust ⽣成 .wasm 的极致瘦⾝之道
开发语言·rust·wasm
专注VB编程开发20年20 小时前
用ADO操作EXCEL文件创建表格,删除表格CREATE TABLE,DROP TABLE
服务器·windows·excel·ado·创建表格·删除表格·读写xlsx
梦魇星虹1 天前
Dell电脑Windows系统更新后声卡驱动无法识别插线耳机问题
windows·电脑