Rust:如何开发32位的DLL动态库
第一部分 完整示例
以下是一个完整的32位Rust DLL示例,包含所有必要的步骤和代码:
完整示例:创建32位DLL
1. 确保安装32位工具链
rustup target add i686-pc-windows-msvc
2. 创建项目
cargo new my_32bit_dll --lib
cd my_32bit_dll
3. 修改 Cargo.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
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
cargo build --target i686-pc-windows-msvc --release
生成的DLL路径:
target/i686-pc-windows-msvc/release/my_32bit_dll.dll
C++ 测试代码 (test_dll.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() {// 加载DLLHMODULE 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;
}
编译和运行注意事项
-
编译C++测试程序:
- 使用Visual Studio命令提示符 (x86):
cl /EHsc test_dll.cpp
- 使用Visual Studio命令提示符 (x86):
-
关键要点:
- 调用约定:使用
__cdecl(默认) 或__stdcall - 内存管理:
- DLL分配的内存必须由DLL提供的函数释放
- 字符串操作遵循"谁分配谁释放"原则
- 错误处理:检查所有返回值和空指针
- 类型兼容:
i32↔int*const i8↔const char**mut c_void↔void*
- 调用约定:使用
-
部署要求:
- 32位DLL只能用于32位应用程序
- 依赖的Rust运行时静态链接到DLL中
- 不需要额外安装Rust运行时
这个示例展示了三种常见的DLL使用场景:简单数学运算、字符串处理和回调函数,包含了完整的内存管理实践。
第二部分 如何安装32位编译环境
看起来你的构建环境缺少32位目标所需的编译工具链和库。这个问题通常可以通过以下步骤解决:
解决方案:
-
安装32位目标平台
rustup target add i686-pc-windows-msvc -
安装Visual Studio构建工具(如果尚未安装):
- 打开Visual Studio Installer
- 确保已安装:
- "使用C++的桌面开发"工作负载
- “MSVC v143 - VS 2022 C++ x64/x86 build tools”
- “Windows 10 SDK (10.0.19041.0)或更新版本”
-
设置MSVC环境变量:
vcvarsall.bat x86 -
清理并重建:
cargo clean cargo build --target i686-pc-windows-msvc --release
如果问题仍然存在:
1. 检查工具链状态:
rustup show
确认i686-pc-windows-msvc出现在已安装目标列表中
2. 验证MSVC安装:
运行开发者命令行提示符并检查:
cl /?
应该看到32位x86架构选项
3. 简化测试(验证工具链):
创建一个新的测试项目:
cargo new test_dll --lib
cd test_dll
修改Cargo.toml:
[lib]
crate-type = ["cdylib"]
修改src/lib.rs:
#[no_mangle]
pub extern "C" fn simple_add(a: i32, b: i32) -> i32 {a + b
}
尝试构建:
cargo build --target i686-pc-windows-msvc --release
4. 替代方案(如果MSVC问题无法解决):
使用GNU工具链替代:
rustup target add i686-pc-windows-gnu
cargo build --target i686-pc-windows-gnu --release
常见问题诊断:
-
工具链冲突:
- 确保没有
RUSTFLAGS环境变量覆盖 - 尝试更新Rust:
rustup update
- 确保没有
-
Windows SDK问题:
- 安装独立SDK: https://developer.microsoft.com/zh-cn/windows/downloads/windows-sdk/
- 设置环境变量:
set INCLUDE=C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\um;C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\shared set LIB=C:\Program Files (x86)\Windows Kits\10\Lib\10.0.19041.0\um\x86
-
检查系统路径:
where link.exe where cl.exe确保显示的是Visual Studio工具路径
完成这些步骤后,你应该能成功编译32位DLL。如果仍然遇到问题,可以运行cargo build -vv --target i686-pc-windows-msvc --release查看详细构建日志以定位具体问题。
