You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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(())
}

额外注意事项

  1. w!宏的正确用法:如果你的路径是静态不变的,直接用w!("\\Processor(0)\\% Processor Time")即可,不需要额外转换。
  2. 字符串终止符:所有Windows API的宽字符串参数都要求以u16::0结尾,缺失会导致未定义行为。
  3. CString的误区:CString是用来处理UTF-8的C风格字符串(带\0终止符),和Windows的UTF-16宽字符串完全不兼容,这里完全不需要用到它。

内容的提问来源于stack exchange,提问作者Tushar Saurabh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 11:19:58