使用 cmake 的局限性
.lib 合並問題
cmake 不會將多個 .lib 合並, 因此可能需要使用 add_custom_command
命令手動使用 MSVC 工具 lib.exe
來創建最終具有 C ABI 的 .lib 靜態庫文件供Rust調用.
set(Target "output")
add_library("${Target}" STATIC lib.cpp)
target_include_directories("${App}" PUBLIC "${CMAKE_HOME_DIRECTORY}/src")
target_link_libraries("${App}" Win32Helper)
很遺憾, output.lib 中對另一個靜態庫Win32Helper的調用是未尋址的! 可以使用cygwin工具 nm
來查看符號:
U表示“未定義”——對象有對符號的引用但沒有定義
T表示在文本段中全局定義——對象定義並導出符號
nm output.lib | grep win32_init
U win32_init
其它的解決辦法:
- 合並代碼, 不拆分成多個庫的形式
- 嘗試在Rust中將多個.lib都進行鏈接 (繁瑣, 實驗后未成功)
set(Target "output")
file(GLOB_RECURSE files RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" ../Win32Helper/*.c)
add_library("${Target}" STATIC lib.cpp ${files})
Win32 API 符號鏈接問題
由於 cmake 導出 .lib 時忽略靜態庫和動態庫依賴, 造成 API 調用在 Rust 中形成 fatal error LNK1120
鏈接錯誤, 意味着找不到符號.
解決辦法:
- 在C++代碼中使用內聯編譯注釋指定導入庫, 微軟 API 文檔有說明每個 API 所在的頭文件和Library等信息.
#pragma comment(lib, "User32.lib") // MessageBoxW
#pragma comment(lib, "Shell32.lib") // Shell_NotifyIconW
- 在Rust中進行鏈接
#[link(name = "User32", kind = "dylib")]
#[link(name = "Shell32", kind = "dylib")]
#[link(name = "Z:\\Github\\Win32Helper\\build\\src\\Rust-ffi\\Debug\\output", kind = "static")]
extern "C" {
pub fn test() -> c_int;
}
動態調用符號
#[link(name = "User32", kind = "dylib")]
extern "C" {
fn GetModuleHandleA(lpModuleName: *const u8) -> *mut u8;
fn GetProcAddress(hModule: *mut u8, lpProcName: *const u8) -> *mut u8;
}
#[no_mangle]
extern "C" fn DllMain(_: *mut u8, reason: u32, _: *mut u8) -> u32 {
match reason {
1 => {
println!("連接到進程!");
intro();
}
0 => {
println!("檢測到進程退出");
}
_ => ()
}
return 1;
}
fn intro() {
unsafe {
let module = GetModuleHandleA("Kernel32\0".as_ptr());
println!("得到模塊 -> {:?}.", module);
let symbol = GetProcAddress(module, b"ExitProcess\0".as_ptr());
println!("得到符號 -> {:?}.", symbol);
type ExitProcess = unsafe extern "C" fn(u32);
let exit_process: ExitProcess = std::mem::transmute(symbol);
exit_process(0);
}
}
將Rust動態庫注入到node進程,提示node版本
use std::ptr::null_mut;
#[link(name = "User32", kind = "dylib")]
extern "C" {
fn GetModuleHandleA(lpModuleName: *const u8) -> *mut u8;
fn GetProcAddress(hModule: *mut u8, lpProcName: *const u8) -> *mut u8;
fn MessageBoxA(hWnd: *mut u8, lpText: *const u8, lpCaption: *const u8, uType: u32) -> u32;
}
#[no_mangle]
extern "C" fn DllMain(_: *mut u8, reason: u32, _: *mut u8) -> u32 {
match reason {
1 => {
println!("連接到進程!");
intro();
}
0 => {
println!("檢測到進程退出");
}
_ => ()
}
return 1;
}
fn intro() {
unsafe {
let module = GetModuleHandleA("\0".as_ptr());
println!("得到模塊 -> {:?}.", module);
let symbol = GetProcAddress(module, b"uv_version_string\0".as_ptr());
println!("得到符號 -> {:?}.", symbol);
type Func = extern "C" fn() -> *const u8;
let uv_version: Func = std::mem::transmute(symbol);
let version = uv_version();
println!("得到版本 -> {:?}.", version);
MessageBoxA(null_mut(), version, "Got version!".as_ptr(), 0);
}
}
node將打印以下輸出並彈出消息框
連接到進程!
得到模塊 -> 0x0.
得到符號 -> 0x7ff6d72a1440.
得到版本 -> 0x7ff6d83a5674.
exiting...
檢測到進程退出