第一部分 完整示例
以下是一个完整的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;
}
编译和运行注意事项
-
编译C++测试程序:
-
使用Visual Studio命令提示符 (x86):
bashcl /EHsc test_dll.cpp
-
-
关键要点:
- 调用约定:使用
__cdecl
(默认) 或__stdcall
- 内存管理:
- DLL分配的内存必须由DLL提供的函数释放
- 字符串操作遵循"谁分配谁释放"原则
- 错误处理:检查所有返回值和空指针
- 类型兼容:
i32
↔int
*const i8
↔const char*
*mut c_void
↔void*
- 调用约定:使用
-
部署要求:
- 32位DLL只能用于32位应用程序
- 依赖的Rust运行时静态链接到DLL中
- 不需要额外安装Rust运行时
这个示例展示了三种常见的DLL使用场景:简单数学运算、字符串处理和回调函数,包含了完整的内存管理实践。
第二部分 如何安装32位编译环境
看起来你的构建环境缺少32位目标所需的编译工具链和库。这个问题通常可以通过以下步骤解决:
解决方案:
-
安装32位目标平台
bashrustup 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环境变量:
bashvcvarsall.bat x86
-
清理并重建:
bashcargo 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
常见问题诊断:
-
工具链冲突:
- 确保没有
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
-
-
检查系统路径:
cmdwhere link.exe where cl.exe
确保显示的是Visual Studio工具路径
完成这些步骤后,你应该能成功编译32位DLL。如果仍然遇到问题,可以运行cargo build -vv --target i686-pc-windows-msvc --release
查看详细构建日志以定位具体问题。