use std::fmt::{Debug, Formatter};
pub use windows::Win32::System::Performance::{
PDH_FMT_DOUBLE, PDH_FMT_LARGE, PDH_FMT_LONG, PDH_HCOUNTER, PDH_HQUERY,
};
use windows::{
core::HSTRING,
Win32::{
Foundation::HANDLE,
System::Performance::{
PdhAddCounterW, PdhAddEnglishCounterW, PdhCloseQuery, PdhCollectQueryData,
PdhCollectQueryDataEx, PdhGetCounterInfoW, PdhGetFormattedCounterValue, PdhOpenQueryW,
PdhRemoveCounter, PDH_COUNTER_INFO_W, PDH_FMT, PDH_FMT_COUNTERVALUE,
},
},
};
pub const PDH_FMT_NOSCALE: PDH_FMT = PDH_FMT(0x00001000);
pub const PDH_FMT_1000: PDH_FMT = PDH_FMT(0x00002000);
pub const PDH_FMT_NODATA: PDH_FMT = PDH_FMT(0x00004000);
pub const PDH_FMT_NOCAP100: PDH_FMT = PDH_FMT(0x00008000);
pub trait PdhFmtExt {
fn add(&self, value: PDH_FMT) -> PDH_FMT;
}
impl PdhFmtExt for PDH_FMT {
fn add(&self, value: PDH_FMT) -> PDH_FMT {
PDH_FMT(self.0 | value.0)
}
}
pub fn pdh_open_query(data_source: Option<String>, user_data: usize) -> PDH_HQUERY {
unsafe {
let mut handle = std::mem::zeroed();
match data_source {
None => PdhOpenQueryW(None, user_data, &mut handle),
Some(d) => PdhOpenQueryW(&HSTRING::from(d), user_data, &mut handle),
};
handle
}
}
pub fn pdh_close_query(h_query: PDH_HQUERY) {
unsafe { PdhCloseQuery(h_query) };
}
pub fn pdh_get_counter_info(
h_counter: PDH_HCOUNTER,
retrieve_explain_text: bool,
) -> Vec<PDH_COUNTER_INFO_W> {
unsafe {
let mut size = std::mem::zeroed();
PdhGetCounterInfoW(h_counter, retrieve_explain_text, &mut size, None);
let mut v = vec![];
for _ in 0..size {
v.push(PDH_COUNTER_INFO_W::default());
}
PdhGetCounterInfoW(
h_counter,
retrieve_explain_text,
&mut size,
Some(v.as_mut_ptr()),
);
v
}
}
pub fn pdh_add_counter(
h_query: PDH_HQUERY,
full_counter_path: String,
user_data: usize,
) -> PDH_HCOUNTER {
unsafe {
let mut handle = std::mem::zeroed();
PdhAddCounterW(
h_query,
&HSTRING::from(full_counter_path),
user_data,
&mut handle,
);
handle
}
}
pub fn pdh_add_english_counter(
h_query: PDH_HQUERY,
full_counter_path: String,
user_data: usize,
) -> PDH_HCOUNTER {
unsafe {
let mut handle = std::mem::zeroed();
PdhAddEnglishCounterW(
h_query,
&HSTRING::from(full_counter_path),
user_data,
&mut handle,
);
handle
}
}
pub fn pdh_collect_query_data_ex(
h_query: PDH_HQUERY,
interval_time: u32,
h_new_data_event: HANDLE,
) {
unsafe {
PdhCollectQueryDataEx(h_query, interval_time, h_new_data_event);
}
}
pub fn pdh_collect_query_data(h_query: PDH_HQUERY) {
unsafe {
PdhCollectQueryData(h_query);
}
}
pub fn pdh_get_formatted_counter_value(
h_counter: PDH_HCOUNTER,
r#format: PDH_FMT,
) -> (u32, PDH_FMT_COUNTERVALUE) {
unsafe {
let mut r#type = std::mem::zeroed();
let mut value = std::mem::zeroed();
PdhGetFormattedCounterValue(h_counter, r#format, Some(&mut r#type), &mut value);
(r#type, value)
}
}
pub fn pdh_remove_counter(h_counter: PDH_HCOUNTER) {
unsafe {
PdhRemoveCounter(h_counter);
}
}
pub struct PdhCounter(PDH_HCOUNTER);
impl Debug for PdhCounter {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let (r, v) = self.get_value();
write!(f, "PdhCounter({}, {})", r, v)
}
}
impl Drop for PdhCounter {
fn drop(&mut self) {
pdh_remove_counter(self.0)
}
}
unsafe impl Sync for PdhCounter {}
unsafe impl Send for PdhCounter {}
#[derive(Debug)]
pub struct PdhQuery(PDH_HQUERY);
impl PdhQuery {
pub fn new() -> Self {
Self(pdh_open_query(None, 0))
}
pub fn add_counter(&self, full_counter_path: String) -> PdhCounter {
PdhCounter(pdh_add_counter(self.0, full_counter_path, 0))
}
pub fn add_english_counter(&self, full_counter_path: String) -> PdhCounter {
PdhCounter(pdh_add_english_counter(self.0, full_counter_path, 0))
}
pub fn collect_data(&self) -> &Self {
pdh_collect_query_data(self.0);
self
}
}
impl Drop for PdhQuery {
fn drop(&mut self) {
pdh_close_query(self.0)
}
}
unsafe impl Sync for PdhQuery {}
unsafe impl Send for PdhQuery {}
pub trait PdhCounterExt {
fn get_value(&self) -> (u32, f64);
}
impl PdhCounterExt for PdhCounter {
fn get_value(&self) -> (u32, f64) {
let (t, v) = pdh_get_formatted_counter_value(self.0, PDH_FMT_DOUBLE);
unsafe { (t, v.Anonymous.doubleValue) }
}
}
#[cfg(test)]
mod test_pdh {
use crate::pdh::PdhQuery;
#[test]
fn main() {
let pdh = PdhQuery::new();
let counter = pdh.add_counter(format!(
r"\Processor Information({})\% Processor Time",
"_Total"
));
for _ in 0..10 {
pdh.collect_data();
dbg!(&counter);
std::thread::sleep(std::time::Duration::from_millis(1000));
}
dbg!(pdh);
}
}