#![cfg(windows)]
use std::ffi::OsStr;
use std::os::windows::ffi::OsStrExt;
use windows::core::PCWSTR;
use windows::Win32::System::EventLog::{
EvtClose, EvtNext, EvtQuery, EvtQueryChannelPath, EvtQueryForwardDirection,
EvtQueryReverseDirection, EvtQueryTolerateQueryErrors, EvtRender, EvtRenderEventXml,
EVT_HANDLE,
};
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()
}
pub struct WinEventIter {
handle: EVT_HANDLE,
channel: String,
batch: Vec<isize>,
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.0,
QueryDirection::Reverse => EvtQueryReverseDirection.0,
};
let flags = EvtQueryChannelPath.0 | dir_flag | EvtQueryTolerateQueryErrors.0;
let handle = unsafe {
EvtQuery(None, PCWSTR(path.as_ptr()), PCWSTR(query.as_ptr()), flags)
.map_err(|e| Error::ChannelAccess(channel.to_string(), format!("EvtQuery: {e}")))?
};
Ok(Self {
handle,
channel: channel.to_string(),
batch: vec![0isize; 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 rc = unsafe {
EvtNext(
self.handle,
&mut self.batch[..],
5_000,
0,
&mut returned as *mut u32,
)
};
match rc {
Ok(()) => {
self.filled = returned;
if returned == 0 {
self.exhausted = true;
}
Ok(())
}
Err(e) => {
let code = e.code().0 as u32 & 0xFFFF;
if code == 259 {
self.exhausted = true;
Ok(())
} else {
Err(Error::Win32 {
code: e.code().0 as u32,
context: "EvtNext",
})
}
}
}
}
fn render_and_close(&self, raw: isize) -> Result<Event> {
let evt_handle = EVT_HANDLE(raw);
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 != 0 {
unsafe {
let _ = EvtClose(EVT_HANDLE(raw));
}
}
}
if !self.handle.is_invalid() {
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(
None,
event,
EvtRenderEventXml.0,
0,
None,
&mut used as *mut u32,
&mut props as *mut u32,
)
};
match probe {
Ok(()) => {
return Ok(String::new());
}
Err(e) => {
let code = e.code().0 as u32 & 0xFFFF;
if code != 122 {
return Err(Error::Win32 {
code: e.code().0 as u32,
context: "EvtRender(size probe)",
});
}
}
}
let u16_len = (used as usize + 1) / 2;
let mut buf: Vec<u16> = vec![0u16; u16_len];
let cap_bytes = (buf.len() * 2) as u32;
unsafe {
EvtRender(
None,
event,
EvtRenderEventXml.0,
cap_bytes,
Some(buf.as_mut_ptr() as *mut _),
&mut used as *mut u32,
&mut props as *mut u32,
)
.map_err(|e| Error::Win32 {
code: e.code().0 as u32,
context: "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)
}