Rust 靜態調用 C++


使用 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

其它的解決辦法:

  1. 合並代碼, 不拆分成多個庫的形式
  2. 嘗試在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 鏈接錯誤, 意味着找不到符號.

解決辦法:

  1. 在C++代碼中使用內聯編譯注釋指定導入庫, 微軟 API 文檔有說明每個 API 所在的頭文件和Library等信息.
#pragma comment(lib, "User32.lib") // MessageBoxW
#pragma comment(lib, "Shell32.lib") // Shell_NotifyIconW 
  1. 在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...
檢測到進程退出

END


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM