Rust调用PdhAddCounterW遇宏错误:字符串处理问题排查
Rust + windows crate 性能计数器开发报错解决
问题代码
let mut query = 0; PdhOpenQueryW(None, 0, &mut query); let query_path = CString::new("\\Processor(0)\\% Processor Time"); let mut counter = 0; PdhAddCounterW( query, w!(query_path.as_ptr()), 0, &mut counter, );
编译器报错
error: no rules expected the token `query_path` --> src\main.rs:12:16 | 12 | w!(query_path.as_ptr()), | ^^^^^^^^^^ no rules expected this token in macro call | note: while trying to match meta-variable `$s:literal` --> C:\Users\T9SAU2\.cargo\registry\src\github.com-1ecc6299db9ec823\windows-0.48.0\src\core\strings\literals.rs:12:6
问题背景
我正在开发一个用Rust和windows crate收集性能计数器的程序,梳理了Rust字符串类型的相关知识,但仍未找到问题:
- 查看
w!宏定义,它只接受字符串字面量($s:literal),用来生成带终止符的UTF-16宽字符串。 PdhAddCounterW的szfullcounterpath参数类型是IntoParam<PCWSTR>,PCWSTR是指向带终止符的16位Unicode字符串的指针。- 我知道Rust的
String是UTF-8编码且无终止符,底层是Vec<u8>,无法直接使用;用str的encode_utf16方法转成u16序列并添加终止符后还是报相同错误;也研究过OsString和CString,但不确定怎么正确转换成符合要求的参数。
核心问题
你把运行时字符串变量传给了只接受编译期字符串字面量的w!宏。w!宏的设计目标是处理硬编码的静态字符串,完全不支持动态生成的字符串或表达式,这就是报错的根本原因。
解决方案
方法1:使用windows crate的WideCString(推荐)
windows crate提供了WideCString类型,专门用来处理带终止符的UTF-16宽字符串,能直接适配PCWSTR参数:
use windows::core::WideCString; use windows::Win32::System::PerformanceData::{PdhAddCounterW, PdhOpenQueryW}; fn main() -> windows::core::Result<()> { let mut query = 0; unsafe { PdhOpenQueryW(None, 0, &mut query) }?; // 将UTF-8字符串转换为带终止符的UTF-16宽字符串 let query_path = WideCString::from_str("\\Processor(0)\\% Processor Time")?; let mut counter = 0; unsafe { PdhAddCounterW( query, query_path.as_pcwstr(), 0, &mut counter, ) }?; // 后续逻辑:收集计数器数据、清理资源等 Ok(()) }
方法2:手动构建UTF-16序列
如果需要手动控制字符串转换过程,可以自行生成带终止符的UTF-16向量,再包装成PCWSTR:
use windows::core::PCWSTR; use windows::Win32::System::PerformanceData::{PdhAddCounterW, PdhOpenQueryW}; fn main() -> windows::core::Result<()> { let mut query = 0; unsafe { PdhOpenQueryW(None, 0, &mut query) }?; let path_str = "\\Processor(0)\\% Processor Time"; // 转换为UTF-16序列并添加终止符 let mut utf16: Vec<u16> = path_str.encode_utf16().collect(); utf16.push(0); // Windows宽字符串必须以u16::0结尾 let mut counter = 0; unsafe { PdhAddCounterW( query, PCWSTR(utf16.as_ptr()), 0, &mut counter, ) }?; // 后续逻辑:收集计数器数据、清理资源等 Ok(()) }
额外注意事项
w!宏的正确用法:如果你的路径是静态不变的,直接用w!("\\Processor(0)\\% Processor Time")即可,不需要额外转换。- 字符串终止符:所有Windows API的宽字符串参数都要求以
u16::0结尾,缺失会导致未定义行为。 CString的误区:CString是用来处理UTF-8的C风格字符串(带\0终止符),和Windows的UTF-16宽字符串完全不兼容,这里完全不需要用到它。
内容的提问来源于stack exchange,提问作者Tushar Saurabh
相关产品推荐
相关产品推荐

