使用 Rust 創建 32 位 DLL 的完整指南
在 Rust 中創建 32 位 DLL 需要特定的工具鏈配置和編譯選項。以下是詳細步驟和最佳實踐:
環境準備
1. 安裝 Rust 工具鏈
# 安裝 Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh# 安裝 32 位目標
rustup target add i686-pc-windows-gnu # GNU ABI
# 或
rustup target add i686-pc-windows-msvc # MSVC ABI
2. 安裝 MinGW-w64 (GNU ABI 需要)
# Ubuntu/Debian
sudo apt install gcc-mingw-w64-i686# Arch Linux
sudo pacman -S mingw-w64-gcc
創建 DLL 項目
1. 新建庫項目
cargo new --lib mydll
cd mydll
2. 配置 Cargo.toml
[package]
name = "mydll"
version = "0.1.0"
edition = "2021"[lib]
crate-type = ["cdylib"] # 關鍵:指定為 C 兼容動態庫[dependencies]
libc = "0.2" # 用于 C 類型兼容
編寫 DLL 代碼
基本示例 (src/lib.rs)
use std::ffi::CStr;
use std::os::raw::{c_char, c_int};// 導出函數必須使用 extern "C" 和 #[no_mangle]
#[no_mangle]
pub extern "C" fn add_numbers(a: c_int, b: c_int) -> c_int {a + b
}#[no_mangle]
pub extern "C" fn greet(name: *const c_char) -> *const c_char {let c_str = unsafe { CStr::from_ptr(name) };let name_str = c_str.to_str().unwrap_or("stranger");let greeting = format!("Hello, {}!", name_str);let c_greeting = std::ffi::CString::new(greeting).unwrap();c_greeting.into_raw() // 調用方需要釋放內存
}// 內存釋放函數
#[no_mangle]
pub extern "C" fn free_string(s: *mut c_char) {unsafe {if s.is_null() {return;}let _ = std::ffi::CString::from_raw(s);}
}
編譯 32 位 DLL
GNU ABI 編譯
cargo build --target i686-pc-windows-gnu --release
MSVC ABI 編譯
cargo build --target i686-pc-windows-msvc --release
輸出文件:target/i686-pc-windows-gnu/release/mydll.dll
創建頭文件 (mydll.h)
#ifdef __cplusplus
extern "C" {
#endif__declspec(dllimport) int add_numbers(int a, int b);
__declspec(dllimport) const char* greet(const char* name);
__declspec(dllimport) void free_string(char* s);#ifdef __cplusplus
}
#endif
在 C/C++ 中使用 DLL
C++ 示例 (test.cpp)
#include <iostream>
#include <Windows.h>
#include "mydll.h"int main() {// 顯式加載 DLLHMODULE dll = LoadLibraryA("mydll.dll");if (!dll) {std::cerr << "Failed to load DLL" << std::endl;return 1;}// 獲取函數指針auto add_func = (int(*)(int, int))GetProcAddress(dll, "add_numbers");auto greet_func = (const char*(*)(const char*))GetProcAddress(dll, "greet");auto free_func = (void(*)(char*))GetProcAddress(dll, "free_string");if (!add_func || !greet_func || !free_func) {std::cerr << "Failed to get function pointers" << std::endl;FreeLibrary(dll);return 1;}// 使用函數int result = add_func(5, 7);std::cout << "5 + 7 = " << result << std::endl;const char* greeting = greet_func("World");std::cout << greeting << std::endl;free_func(const_cast<char*>(greeting));FreeLibrary(dll);return 0;
}
高級主題
1. 處理結構體
#[repr(C)]
pub struct Point {pub x: i32,pub y: i32,
}#[no_mangle]
pub extern "C" fn create_point(x: i32, y: i32) -> Box<Point> {Box::new(Point { x, y })
}#[no_mangle]
pub extern "C" fn distance(p1: *const Point, p2: *const Point) -> f64 {unsafe {let p1 = &*p1;let p2 = &*p2;let dx = (p2.x - p1.x) as f64;let dy = (p2.y - p1.y) as f64;(dx * dx + dy * dy).sqrt()}
}
2. 回調函數
type Callback = extern "C" fn(i32);#[no_mangle]
pub extern "C" fn register_callback(cb: Callback) {for i in 0..5 {cb(i);}
}
3. 錯誤處理
#[repr(C)]
pub enum ErrorCode {Success = 0,InvalidInput,CalculationError,
}#[no_mangle]
pub extern "C" fn safe_divide(a: f64, b: f64, result: *mut f64
) -> ErrorCode {if b == 0.0 {return ErrorCode::InvalidInput;}unsafe {*result = a / b;}ErrorCode::Success
}
構建優化
1. 減小 DLL 大小
# Cargo.toml
[profile.release]
opt-level = "z" # 優化大小
lto = true # 鏈接時優化
codegen-units = 1 # 減少并行編譯
panic = "abort" # 禁用panic展開
2. 移除符號信息
# 安裝 strip 工具
cargo install cargo-strip# 構建并剝離符號
cargo build --target i686-pc-windows-gnu --release
cargo strip --target i686-pc-windows-gnu
調試技巧
1. 使用 Dependency Walker
Dependency Walker 分析 DLL 導出函數
2. Rust 調試符號
# Cargo.toml
[profile.release]
debug = true # 保留調試符號
3. 日志輸出
#[no_mangle]
pub extern "C" fn debug_log(msg: *const c_char) {let c_str = unsafe { CStr::from_ptr(msg) };println!("[DEBUG] {}", c_str.to_str().unwrap_or(""));
}
常見問題解決
1. 內存管理問題
- Rust 分配的內存必須在 Rust 中釋放
- 使用
Box::into_raw
和Box::from_raw
轉換所有權
2. ABI 兼容性
- 使用
#[repr(C)]
確保結構體布局 - 避免使用 Rust 特有類型(如 String, Vec)
- 使用
libc
crate 中的 C 類型
3. 線程安全
// 標記線程安全函數
#[no_mangle]
pub extern "C" fn thread_safe_function() {// 使用互斥鎖等同步機制
}
4. 入口點函數
// DLL 入口點 (可選)
#[no_mangle]
#[allow(non_snake_case)]
pub extern "system" fn DllMain(_hinstDLL: isize,_fdwReason: u32,_lpvReserved: isize,
) -> i32 {1 // 成功
}
完整項目結構
mydll/
├── Cargo.toml
├── src/
│ └── lib.rs
├── include/
│ └── mydll.h
└── examples/└── test.cpp
通過遵循這些步驟和最佳實踐,您可以創建高效、穩定的 32 位 DLL,并輕松集成到各種 Windows 應用程序中。