如何在Rust crate作为依赖时生成额外DLL构建产物?
整合Rust音频捕获项目为可发布库的实现方案
问题背景
原有项目包含两个crate:main-program(负责DLL注入与IPC音频接收)和patch(编译为带音频钩子的DLL,通过IPC发送数据)。现在需要将二者整合为my-audio-capture-lib,让用户添加依赖后,能直接调用capture_audio_from_pid捕获指定进程音频,且用户构建自身项目时自动生成所需的注入DLL。
实现步骤
1. 重构项目结构
将原patch的DLL代码移至my-audio-capture-lib/src/dll目录,原main-program的注入、IPC接收代码移至src/lib.rs作为库的核心API。最终结构如下:
my-audio-capture-lib/ ├── src/ │ ├── lib.rs # 主库代码:封装capture_audio_from_pid API │ └── dll/ │ └── lib.rs # 原patch的DLL钩子与IPC发送代码 ├── Cargo.toml └── build.rs # 自动编译DLL的构建脚本
2. 配置Cargo.toml
在my-audio-capture-lib/Cargo.toml中定义两个编译目标:供用户依赖的常规Rust库,以及需要注入的DLL:
[package] name = "my-audio-capture-lib" version = "0.1.0" edition = "2021" # 主库:供用户依赖调用的常规Rust库 [lib] name = "my_audio_capture_lib" path = "src/lib.rs" crate-type = ["rlib"] # DLL目标:编译为注入用的动态链接库 [[lib]] name = "audio_patch_dll" path = "src/dll/lib.rs" crate-type = ["cdylib"] targets = ["x86_64-pc-windows-msvc", "i686-pc-windows-msvc"] # Windows平台依赖(注入与IPC需要) [target.'cfg(windows)'.dependencies] winapi = { version = "0.3", features = ["processthreadsapi", "memoryapi", "synchapi", "errhandlingapi"] } interprocess = "1.2" # 可选:简化IPC实现
3. 编写build.rs自动编译DLL
在build.rs中添加逻辑,确保用户构建项目时自动编译DLL,并传递DLL路径给主库:
use std::env; use std::process::Command; fn main() { let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); let target = env::var("TARGET").unwrap(); let profile = env::var("PROFILE").unwrap(); // 编译DLL目标 let status = Command::new("cargo") .arg("build") .arg("--lib") .arg("--target") .arg(&target) .arg(match profile.as_str() { "release" => "--release", _ => "", }) .arg("--package") .arg("my-audio-capture-lib") .arg("--lib-name") .arg("audio_patch_dll") .current_dir(&crate_dir) .status() .expect("Failed to build audio patch DLL"); assert!(status.success(), "DLL compilation failed"); // 传递DLL路径给主库,通过环境变量 let dll_path = format!( "{}/target/{}/{}/audio_patch_dll.dll", crate_dir, target, profile ); println!("cargo:rustc-env=AUDIO_PATCH_DLL_PATH={}", dll_path); }
4. 封装核心API
在src/lib.rs中实现capture_audio_from_pid函数,整合注入、IPC接收逻辑:
use std::env; use std::error::Error; use winapi::um::{processthreadsapi::OpenProcess, memoryapi::VirtualAllocEx, synchapi::CreateRemoteThread}; // 实现DLL注入逻辑(基于Windows API) fn inject_dll(target_pid: u32, dll_path: &str) -> Result<(), Box<dyn Error>> { // 核心流程: // 1. 打开目标进程 // 2. 在目标进程内存中分配空间写入DLL路径 // 3. 创建远程线程调用LoadLibraryW加载DLL Ok(()) } // 实现IPC音频接收逻辑(示例用命名管道) fn receive_audio() -> impl Iterator<Item = Vec<i16>> { // 基于interprocess或原生Windows API实现IPC接收 std::iter::empty() } /// 从指定PID的进程捕获音频 /// 返回音频数据迭代器,每个元素为一段PCM音频样本 pub fn capture_audio_from_pid(target_pid: u32) -> Result<impl Iterator<Item = Vec<i16>>, Box<dyn Error>> { // 获取自动编译的DLL路径 let dll_path = env::var("AUDIO_PATCH_DLL_PATH")?; // 注入DLL到目标进程 inject_dll(target_pid, &dll_path)?; // 建立IPC连接并返回音频流 Ok(receive_audio()) }
5. 用户使用示例
用户只需在自身项目的Cargo.toml添加依赖:
[dependencies] my-audio-capture-lib = "0.1.0"
然后在代码中调用API:
use my_audio_capture_lib::capture_audio_from_pid; fn main() { let target_pid = 1234; // 替换为目标进程PID match capture_audio_from_pid(target_pid) { Ok(mut audio_stream) => { println!("开始捕获PID {}的音频", target_pid); for audio_data in audio_stream { // 处理音频数据(如保存、播放) println!("收到{}个音频样本", audio_data.len()); } } Err(e) => eprintln!("捕获失败:{}", e), } }
注意事项
- 平台限制:仅支持Windows平台,需确保用户安装对应Rust目标(如
rustup target add x86_64-pc-windows-msvc)。 - 权限要求:注入DLL需要管理员权限,用户需以管理员身份运行程序。
- IPC选择:推荐使用Windows命名管道或
interprocesscrate简化跨进程通信开发。
内容的提问来源于stack exchange,提问作者Nobbe
相关产品推荐
相关产品推荐

