#![cfg(windows)]
use std::ffi::OsStr;
use std::os::windows::ffi::OsStrExt;
use core::ffi::c_void;
use core::ptr;
use win32_min::eventlog::{
EvtClose, EvtNext, EvtQuery, EvtQueryChannelPath, EvtQueryForwardDirection,
EvtQueryReverseDirection, EvtQueryTolerateQueryErrors, EvtRender, EvtRenderEventXml,
EVT_HANDLE, NULL_EVT_HANDLE,
};
use win32_min::foundation::{GetLastError, PCWSTR};
use crate::error::{Error, Result};
use crate::event::{parse_event_xml, Event};
use crate::query::QueryDirection;
fn to_wide(s: &str) -> Vec<u16> {
OsStr::new(s)
.encode_wide()
.chain(std::iter::once(0))
.collect()
}
fn last_win32(context: &'static str) -> Error {
let code = unsafe { GetLastError() };
Error::Win32 { code, context }
}
pub struct WinEventIter {
handle: EVT_HANDLE,
channel: String,
batch: Vec<EVT_HANDLE>,
cursor: usize,
filled: u32,
exhausted: bool,
}
const BATCH: usize = 16;
impl WinEventIter {
pub fn open(channel: &str, xpath: &str, direction: QueryDirection) -> Result<Self> {
let path = to_wide(channel);
let query = to_wide(xpath);
let dir_flag = match direction {
QueryDirection::Forward => EvtQueryForwardDirection,
QueryDirection::Reverse => EvtQueryReverseDirection,
};
let flags = EvtQueryChannelPath | dir_flag | EvtQueryTolerateQueryErrors;
let handle = unsafe {
EvtQuery(
NULL_EVT_HANDLE,
PCWSTR(path.as_ptr()),
PCWSTR(query.as_ptr()),
flags,
)
};
if handle == NULL_EVT_HANDLE {
let code = unsafe { GetLastError() };
return Err(Error::ChannelAccess(
channel.to_string(),
format!("EvtQuery: Win32 error {code:#010x}"),
));
}
Ok(Self {
handle,
channel: channel.to_string(),
batch: vec![NULL_EVT_HANDLE; BATCH],
cursor: 0,
filled: 0,
exhausted: false,
})
}
fn refill(&mut self) -> Result<()> {
if self.exhausted {
return Ok(());
}
self.cursor = 0;
self.filled = 0;
let mut returned: u32 = 0;
let ok = unsafe {
EvtNext(
self.handle,
self.batch.len() as u32,
self.batch.as_mut_ptr(),
5_000,
0,
&mut returned,
)
};
if ok != 0 {
self.filled = returned;
if returned == 0 {
self.exhausted = true;
}
Ok(())
} else {
let code = unsafe { GetLastError() };
if code == 259 {
self.exhausted = true;
Ok(())
} else {
Err(Error::Win32 {
code,
context: "EvtNext",
})
}
}
}
fn render_and_close(&self, evt_handle: EVT_HANDLE) -> Result<Event> {
let xml = render_xml(evt_handle)?;
unsafe {
let _ = EvtClose(evt_handle);
}
parse_event_xml(&xml, &self.channel)
}
}
impl Drop for WinEventIter {
fn drop(&mut self) {
for &raw in &self.batch[self.cursor..self.filled as usize] {
if raw != NULL_EVT_HANDLE {
unsafe {
let _ = EvtClose(raw);
}
}
}
if self.handle != NULL_EVT_HANDLE {
unsafe {
let _ = EvtClose(self.handle);
}
}
}
}
impl Iterator for WinEventIter {
type Item = Result<Event>;
fn next(&mut self) -> Option<Self::Item> {
loop {
if (self.cursor as u32) < self.filled {
let raw = self.batch[self.cursor];
self.cursor += 1;
return Some(self.render_and_close(raw));
}
if self.exhausted {
return None;
}
if let Err(e) = self.refill() {
return Some(Err(e));
}
if self.filled == 0 {
return None;
}
}
}
}
pub fn render_xml(event: EVT_HANDLE) -> Result<String> {
let mut used: u32 = 0;
let mut props: u32 = 0;
let probe = unsafe {
EvtRender(
NULL_EVT_HANDLE,
event,
EvtRenderEventXml,
0,
ptr::null_mut(),
&mut used,
&mut props,
)
};
if probe != 0 {
return Ok(String::new());
}
let code = unsafe { GetLastError() };
if code != 122 {
return Err(Error::Win32 {
code,
context: "EvtRender(size probe)",
});
}
let u16_len = (used as usize).div_ceil(2);
let mut buf: Vec<u16> = vec![0u16; u16_len];
let cap_bytes = (buf.len() * 2) as u32;
let ok = unsafe {
EvtRender(
NULL_EVT_HANDLE,
event,
EvtRenderEventXml,
cap_bytes,
buf.as_mut_ptr() as *mut c_void,
&mut used,
&mut props,
)
};
if ok == 0 {
return Err(last_win32("EvtRender"));
}
let used_u16 = (used as usize) / 2;
let end = buf[..used_u16]
.iter()
.position(|&c| c == 0)
.unwrap_or(used_u16);
String::from_utf16(&buf[..end]).map_err(|_| Error::Utf16)
}