win_wrap/
pdh.rs

1/*
2 * Copyright (c) 2024. The RigelA open source project team and
3 * its contributors reserve all rights.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software distributed under the
10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 * See the License for the specific language governing permissions and limitations under the License.
12 */
13
14use std::fmt::{Debug, Formatter};
15pub use windows::Win32::System::Performance::{
16    PDH_FMT_DOUBLE, PDH_FMT_LARGE, PDH_FMT_LONG, PDH_HCOUNTER, PDH_HQUERY,
17};
18use windows::{
19    core::HSTRING,
20    Win32::{
21        Foundation::HANDLE,
22        System::Performance::{
23            PdhAddCounterW, PdhAddEnglishCounterW, PdhCloseQuery, PdhCollectQueryData,
24            PdhCollectQueryDataEx, PdhGetCounterInfoW, PdhGetFormattedCounterValue, PdhOpenQueryW,
25            PdhRemoveCounter, PDH_COUNTER_INFO_W, PDH_FMT, PDH_FMT_COUNTERVALUE,
26        },
27    },
28};
29
30//noinspection SpellCheckingInspection
31pub const PDH_FMT_NOSCALE: PDH_FMT = PDH_FMT(0x00001000);
32pub const PDH_FMT_1000: PDH_FMT = PDH_FMT(0x00002000);
33pub const PDH_FMT_NODATA: PDH_FMT = PDH_FMT(0x00004000);
34//noinspection SpellCheckingInspection
35pub const PDH_FMT_NOCAP100: PDH_FMT = PDH_FMT(0x00008000);
36
37pub trait PdhFmtExt {
38    fn add(&self, value: PDH_FMT) -> PDH_FMT;
39}
40
41impl PdhFmtExt for PDH_FMT {
42    fn add(&self, value: PDH_FMT) -> PDH_FMT {
43        PDH_FMT(self.0 | value.0)
44    }
45}
46
47/**
48创建用于管理性能数据收集的新查询。
49若要使用数据源的句柄,请使用 pdh_open_query_h 函数。
50`data_source` 以 Null 结尾的字符串,指定要从中检索性能数据的日志文件的名称。 如果 为 NULL,则从实时数据源收集性能数据。
51`user_data` 要与此查询关联的用户定义的值。 若要稍后检索用户数据,请调用 pdh_get_counter_info 并访问 PDH_COUNTER_INFO 的 dwQueryUserData 成员。
52*/
53pub fn pdh_open_query(data_source: Option<String>, user_data: usize) -> PDH_HQUERY {
54    unsafe {
55        let mut handle = std::mem::zeroed();
56        match data_source {
57            None => PdhOpenQueryW(None, user_data, &mut handle),
58            Some(d) => PdhOpenQueryW(&HSTRING::from(d), user_data, &mut handle),
59        };
60        handle
61    }
62}
63
64/**
65关闭指定查询中包含的所有计数器,关闭与查询相关的所有句柄,并释放与查询关联的所有内存。
66`h_query` 要关闭的查询的句柄。 此句柄由 pdh_open_query 函数返回。
67*/
68pub fn pdh_close_query(h_query: PDH_HQUERY) {
69    unsafe { PdhCloseQuery(h_query) };
70}
71
72/**
73检索有关计数器的信息,例如数据大小、计数器类型、路径和用户提供的数据值。
74`h_counter` 要从中检索信息的计数器的句柄。 pdh_add_counter 函数返回此句柄。
75`retrieve_explain_text` 确定是否检索说明文本。 如果将此参数设置为 TRUE,则会检索计数器的说明文本。 如果将此参数设置为 FALSE,则返回的缓冲区中的字段为 NULL。
76*/
77pub fn pdh_get_counter_info(
78    h_counter: PDH_HCOUNTER,
79    retrieve_explain_text: bool,
80) -> Vec<PDH_COUNTER_INFO_W> {
81    unsafe {
82        let mut size = std::mem::zeroed();
83        PdhGetCounterInfoW(h_counter, retrieve_explain_text, &mut size, None);
84        let mut v = vec![];
85        for _ in 0..size {
86            v.push(PDH_COUNTER_INFO_W::default());
87        }
88        PdhGetCounterInfoW(
89            h_counter,
90            retrieve_explain_text,
91            &mut size,
92            Some(v.as_mut_ptr()),
93        );
94        v
95    }
96}
97
98/**
99将指定的计数器添加到查询。
100`h_query` 要向其添加计数器的查询的句柄。 此句柄由 pdh_open_query 函数返回。
101`full_counter_path` 包含计数器路径的以 Null 结尾的字符串。 有关计数器路径格式的详细信息,请参阅指定计数器路径。 计数器路径的最大长度为PDH_MAX_COUNTER_PATH。
102`user_data` 用户定义的值。 此值将成为计数器信息的一部分。 若要稍后检索此值,请调用 pdh_get_counter_info 函数并访问 PDH_COUNTER_INFO 结构的 dwUserData 成员。
103*/
104pub fn pdh_add_counter(
105    h_query: PDH_HQUERY,
106    full_counter_path: String,
107    user_data: usize,
108) -> PDH_HCOUNTER {
109    unsafe {
110        let mut handle = std::mem::zeroed();
111        PdhAddCounterW(
112            h_query,
113            &HSTRING::from(full_counter_path),
114            user_data,
115            &mut handle,
116        );
117        handle
118    }
119}
120
121/**
122将指定的非特定语言计数器添加到查询。
123`h_query` 要向其添加计数器的查询的句柄。 此句柄由 pdh_open_query 函数返回。
124`full_counter_path` 包含计数器路径的以 Null 结尾的字符串。 有关计数器路径格式的详细信息,请参阅指定计数器路径。 计数器路径的最大长度为PDH_MAX_COUNTER_PATH。
125`user_data` 用户定义的值。 此值将成为计数器信息的一部分。 若要稍后检索此值,请调用 pdh_get_counter_info 函数并访问 PDH_COUNTER_INFO 结构的 dwUserData 成员。
126*/
127pub fn pdh_add_english_counter(
128    h_query: PDH_HQUERY,
129    full_counter_path: String,
130    user_data: usize,
131) -> PDH_HCOUNTER {
132    unsafe {
133        let mut handle = std::mem::zeroed();
134        PdhAddEnglishCounterW(
135            h_query,
136            &HSTRING::from(full_counter_path),
137            user_data,
138            &mut handle,
139        );
140        handle
141    }
142}
143
144/**
145使用单独的线程收集指定查询中所有计数器的当前原始数据值。 然后,函数向应用程序定义的事件发出信号,并在返回之前等待指定的时间间隔。
146`h_query` 查询的句柄。 查询标识要收集的计数器。 pdh_open_query 函数返回此句柄。
147`interval_time` 等待的时间间隔(以秒为单位)。
148`h_new_data_event` 希望 PDH 在时间间隔过期后发出信号的事件的句柄。 若要创建事件对象,请调用 create_event 函数。
149*/
150pub fn pdh_collect_query_data_ex(
151    h_query: PDH_HQUERY,
152    interval_time: u32,
153    h_new_data_event: HANDLE,
154) {
155    unsafe {
156        PdhCollectQueryDataEx(h_query, interval_time, h_new_data_event);
157    }
158}
159
160/**
161收集指定查询中所有计数器的当前原始数据值,并更新每个计数器的状态代码。
162*/
163pub fn pdh_collect_query_data(h_query: PDH_HQUERY) {
164    unsafe {
165        PdhCollectQueryData(h_query);
166    }
167}
168
169/**
170计算指定计数器的可显示值。
171`h_counter` 要计算其可显示值的计数器的句柄。 PdhAddCounter 函数返回此句柄。
172`format` 确定格式化值的数据类型。 指定以下值之一。
173- PDH_FMT_DOUBLE 以双精度浮点实数的形式返回数据。
174- PDH_FMT_LARGE 以 64 位整数的形式返回数据。
175- PDH_FMT_LONG 以长整数的形式返回数据。
176*/
177pub fn pdh_get_formatted_counter_value(
178    h_counter: PDH_HCOUNTER,
179    r#format: PDH_FMT,
180) -> (u32, PDH_FMT_COUNTERVALUE) {
181    unsafe {
182        let mut r#type = std::mem::zeroed();
183        let mut value = std::mem::zeroed();
184        PdhGetFormattedCounterValue(h_counter, r#format, Some(&mut r#type), &mut value);
185        (r#type, value)
186    }
187}
188
189/**
190从查询中删除计数器。
191`h_counter` 要从其查询中删除的计数器的句柄。 pdh_add_counter 函数返回此句柄。
192*/
193pub fn pdh_remove_counter(h_counter: PDH_HCOUNTER) {
194    unsafe {
195        PdhRemoveCounter(h_counter);
196    }
197}
198
199pub struct PdhCounter(PDH_HCOUNTER);
200
201impl Debug for PdhCounter {
202    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
203        let (r, v) = self.get_value();
204        write!(f, "PdhCounter({}, {})", r, v)
205    }
206}
207
208impl Drop for PdhCounter {
209    fn drop(&mut self) {
210        pdh_remove_counter(self.0)
211    }
212}
213
214unsafe impl Sync for PdhCounter {}
215unsafe impl Send for PdhCounter {}
216
217#[derive(Debug)]
218pub struct PdhQuery(PDH_HQUERY);
219
220impl PdhQuery {
221    /**
222    创建一个性能数据查询器。
223    */
224    pub fn new() -> Self {
225        Self(pdh_open_query(None, 0))
226    }
227
228    /**
229    将指定的计数器添加到查询。
230    `full_counter_path` 包含计数器路径的字符串。 有关计数器路径格式的详细信息,请参阅指定计数器路径。
231    */
232    pub fn add_counter(&self, full_counter_path: String) -> PdhCounter {
233        PdhCounter(pdh_add_counter(self.0, full_counter_path, 0))
234    }
235
236    /**
237    将指定的非特定语言计数器添加到查询。
238    `full_counter_path` 包含计数器路径的字符串。 有关计数器路径格式的详细信息,请参阅指定计数器路径。
239    */
240    pub fn add_english_counter(&self, full_counter_path: String) -> PdhCounter {
241        PdhCounter(pdh_add_english_counter(self.0, full_counter_path, 0))
242    }
243
244    /// 收集指定查询中所有计数器的当前原始数据值,并更新每个计数器的状态代码。
245    pub fn collect_data(&self) -> &Self {
246        pdh_collect_query_data(self.0);
247        self
248    }
249}
250
251impl Drop for PdhQuery {
252    fn drop(&mut self) {
253        pdh_close_query(self.0)
254    }
255}
256
257unsafe impl Sync for PdhQuery {}
258unsafe impl Send for PdhQuery {}
259
260pub trait PdhCounterExt {
261    fn get_value(&self) -> (u32, f64);
262}
263
264impl PdhCounterExt for PdhCounter {
265    /**
266    查询指定计数器的可显示值。
267    */
268    fn get_value(&self) -> (u32, f64) {
269        let (t, v) = pdh_get_formatted_counter_value(self.0, PDH_FMT_DOUBLE);
270        unsafe { (t, v.Anonymous.doubleValue) }
271    }
272}
273
274#[cfg(test)]
275mod test_pdh {
276    use crate::pdh::PdhQuery;
277
278    #[test]
279    fn main() {
280        let pdh = PdhQuery::new();
281        let counter = pdh.add_counter(format!(
282            r"\Processor Information({})\% Processor Time",
283            "_Total"
284        ));
285        for _ in 0..10 {
286            pdh.collect_data();
287            dbg!(&counter);
288            std::thread::sleep(std::time::Duration::from_millis(1000));
289        }
290        dbg!(pdh);
291    }
292}