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查看详细构建日志以定位具体问题。

相关推荐
problc4 小时前
Flutter桌面应用实战:Windows系统代理切换工具开发
windows·flutter
love530love6 小时前
怎么更新 cargo.exe ?(Rust 工具链)
人工智能·windows·python·rust·r语言
Source.Liu11 小时前
【typenum】 23 倒序存储的无符号整数(private.rs片段)
rust
咸甜适中11 小时前
rust语言(1.88.0)sqlite数据库rusqlite库(0.37.0)学习笔记
数据库·rust·sqlite·rusqlite
jinlei200911 小时前
在python 代码中调用rust 源码库操作步骤
开发语言·python·rust
Univin1 天前
8.25作业
数据结构·windows
徐子元竟然被占了!!1 天前
Windows Server 2019 DateCenter搭建 FTP 服务器
运维·服务器·windows
winkel_wang1 天前
身份管理与安全 (Protect identities)
windows·安全
正经教主1 天前
[特殊字符]Windows 资源监视器使用指南:查端口以后不用敲命令了
windows
m0_480502641 天前
Rust 登堂 之 函数式编程(三)
开发语言·后端·rust