我正在编写一个程序来收集性能计数器。该程序是用
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中
String
的分析和理解,请让我知道我哪里错了。
当我导航到
C:\Users\T9SAU2\.cargo\registry\src\github.com-1ecc6299db9ec823\windows-0.48.0\src\core\strings\literals.rs:12:6
时,相关的代码和注释是-
/// A literal UTF-16 wide string with a trailing null terminator.
#[macro_export]
macro_rules! w {
($s:literal) => {{
const INPUT: &[u8] = $s.as_bytes();
const OUTPUT_LEN: usize = $crate::core::utf16_len(INPUT) + 1;
输入应该是一个 UTF-16 空终止字符串。
PdhAddCounterW()
的函数签名是:
pub unsafe fn PdhAddCounterW<P0>(
hquery: isize,
szfullcounterpath: P0,
dwuserdata: usize,
phcounter: *mut isize
) -> u32
where
P0: IntoParam<PCWSTR>,
参数
szfullcounterpath
的类型是 IntoParam\<PCWSTR\>
。 PCWSTR
定义为:
pub struct PCWSTR(pub *const u16);
指向 16 位 Unicode 字符的以 null 结尾的常量字符串的指针。
A
String
在 Rust 中不是空终止的并且是 UTF-8。基本定义是:
pub struct String {
vec: Vec<u8>,
}
基本上,一个无符号的 8 位向量。这行不通。
参数
szfullcounterpath
应该是指向 u16
的空终止向量的指针。
str
是另一种类型,是有效的字符串切片。这种类型有一个有趣的功能encode_utf16
。另一个 StackOverflow 帖子 证实了这一点。
但是,当我使用下面的时候,我仍然得到同样的错误:
let mut v: Vec<u16> = query_path.encode_utf16().collect();
v.push(0);
我参考了博客Rust #8: Strings。从这里,我了解了
OsString
和CString
。
OsString
上的 Rust 文档指出:
在 Windows 上,字符串通常是非零 16 位值的任意序列,在有效时被解释为 UTF-16。
...
...在 Windows 上,正如刚刚讨论的那样,字符串是基于 16 位值的,字符串实际上也存储为 8 位值序列,编码为 UTF-8 的不太严格的变体。这对于理解处理容量和长度值时很有用。
Windows 上的
OsString
是UTF-16,但没有提及任何有关空终止的信息。此外,它存储为 u8
的序列,因此我不确定是否可以在不将其操作为 u16
并添加空终止符的情况下使用它。
CString
是一种表示拥有的、C 兼容的、以 null 结尾的字符串的类型,中间没有 nul
字节。它是一个 Box 类型,因此是一个智能指针,但是一个 u8
的数组。智能指针可以转换为原始指针,但仍然需要将其更改为u16
。如果我必须使用它,我需要遍历框类型并使用移位从 u8
转换为 u16
.
我相信最简单的选择是字符串切片,但我收到同样的错误。
我错过了什么?
w!
宏只能应用于字符串文字。因此,您的代码应为:
// ...
let mut counter = 0;
PdhAddCounterW(
query,
w!("\\Processor(0)\\% Processor Time"),
0,
&mut counter,
);
如果您确实需要在运行时构造一个 UTF-16 编码的字符串,因此不能使用
w!
宏,有几个选项。由于 Rust 不提供使用 UTF-16 编码的字符串类型,因此它们都遵循几乎相同的模式:
虽然您可以手动执行此操作,例如
let utf16 = text.encode_utf16().chain(once(0)).collect::<Vec<_>>();
使用
windows
箱子提供的工具要方便得多。所有字符串处理都以 HSTRING
类型为中心,它可以从几乎任何 Rust 字符串类型构造,也可以传递给期望 IntoParam<PCWSTR>
. 的函数
如果
query_path
是在运行时构造的,你可以把你的代码改成read
let mut query = 0;
PdhOpenQueryW(None, 0, &mut query);
let query_path = String::from("\\Processor(0)\\% Processor Time");
let mut counter = 0;
PdhAddCounterW(
query,
&HSTRING::from(&query_path),
0,
&mut counter,
);
impl From
的所有HSTRING
从UTF-8转换为UTF-16,并附加一个零终止符。