windows_erg/evt/mod.rs
1//! Windows Event Log (evt) - Query and read events from Windows Event Logs.
2//!
3//! This module provides ergonomic access to Windows Event Logs with support for:
4//! - Reading from event log channels (Security, System, Application, etc.)
5//! - Reading from .evtx log files
6//! - Flexible XPath-based or builder-based queries
7//! - Optional event message rendering with publisher metadata caching
8//! - Optional EventData extraction with field name interning
9//! - Custom parsing APIs for performance-critical scenarios
10//! - Detailed error reporting including corrupted event information
11//!
12//! # Choosing the Right API
13//!
14//! This module provides multiple ways to process events, each optimized for different use cases:
15//!
16//! ## Quick Start: Standard Event Processing
17//!
18//! Use the built-in [`Event`](types::Event) struct with builder options:
19//!
20//! ```no_run
21//! use windows_erg::evt::EventLog;
22//!
23//! # fn main() -> windows_erg::Result<()> {
24//! let log = EventLog::open("Security")?;
25//! let mut query = log.query_stream("*[System[EventID=4624]]")?
26//! .with_event_data() // Extract EventData key-value pairs (opt-in)
27//! .with_message(); // Format event messages via EvtFormatMessage (opt-in)
28//!
29//! let mut batch = Vec::new();
30//! while query.next_batch(&mut batch)? > 0 {
31//! for event in &batch {
32//! println!("Event {}: {}", event.id, event.formatted_message.as_deref().unwrap_or(""));
33//! if let Some(ref data) = event.data {
34//! for (key, value) in data {
35//! println!(" {}: {}", key, value); // Common fields use Cow::Borrowed (zero-copy)
36//! }
37//! }
38//! }
39//! }
40//! # Ok(())
41//! # }
42//! ```
43//!
44//! **When to use**: Most applications. Provides structured Event objects with opt-in features.
45//!
46//! ## Explicit Corruption Handling
47//!
48//! Use [`next_batch_with_results`](EventQuery::next_batch_with_results) when you need to handle corrupted events explicitly:
49//!
50//! ```no_run
51//! use windows_erg::evt::EventLog;
52//!
53//! # fn main() -> windows_erg::Result<()> {
54//! let log = EventLog::open("System")?;
55//! let mut query = log.query_stream("*")?;
56//! let mut batch = Vec::new();
57//!
58//! while query.next_batch_with_results(&mut batch)? > 0 {
59//! for result in &batch {
60//! match result {
61//! Ok(event) => println!("Event: {}", event.id),
62//! Err(corrupted) => {
63//! eprintln!("Corrupted event at record {}: {}",
64//! corrupted.record_id.unwrap_or(0),
65//! corrupted.reason
66//! );
67//! }
68//! }
69//! }
70//! }
71//! # Ok(())
72//! # }
73//! ```
74//!
75//! **When to use**: When parsing untrusted or potentially corrupted logs. Returns both Ok(Event) and Err(CorruptedEvent) in the same vector.
76//!
77//! ## Custom Types: Maximum Performance
78//!
79//! Use [`next_batch_raw_with_filter`](EventQuery::next_batch_raw_with_filter) when you need custom event types without allocating intermediate Event structs:
80//!
81//! ```no_run
82//! use windows_erg::evt::{EventLog, types::{extract_event_id, extract_provider}};
83//!
84//! # fn main() -> windows_erg::Result<()> {
85//! #[derive(Debug)]
86//! struct LightweightEvent {
87//! id: u32,
88//! provider: String,
89//! }
90//!
91//! let log = EventLog::open("Application")?;
92//! let mut query = log.query_stream("*")?;
93//! let mut events = Vec::new();
94//!
95//! query.next_batch_raw_with_filter(
96//! &mut events,
97//! |handle| {
98//! Ok(LightweightEvent {
99//! id: extract_event_id(handle)?,
100//! provider: extract_provider(handle)?,
101//! })
102//! },
103//! |event| event.id < 1000, // Filter AFTER conversion (on your custom type)
104//! )?;
105//! # Ok(())
106//! # }
107//! ```
108//!
109//! **When to use**: High-throughput scenarios where you only need a subset of event fields. Avoids allocating full Event structs.
110//!
111//! ## Custom Types: Serde Deserialization
112//!
113//! Use [`next_batch_deserialize`](EventQuery::next_batch_deserialize) (requires `serde` feature) for owned XML deserialization:
114//!
115//! ```no_run
116//! # fn main() -> windows_erg::Result<()> {
117//! # #[cfg(feature = "serde")]
118//! # {
119//! use windows_erg::evt::EventLog;
120//! use serde::Deserialize;
121//!
122//! #[derive(Deserialize)]
123//! struct CustomEvent {
124//! #[serde(rename = "System")]
125//! system: SystemData,
126//! }
127//!
128//! #[derive(Deserialize)]
129//! struct SystemData {
130//! #[serde(rename = "EventID")]
131//! event_id: u32,
132//! #[serde(rename = "Provider")]
133//! provider: ProviderData,
134//! }
135//!
136//! #[derive(Deserialize)]
137//! struct ProviderData {
138//! #[serde(rename = "@Name")]
139//! name: String,
140//! }
141//!
142//! let log = EventLog::open("Security")?;
143//! let mut query = log.query_stream("*")?;
144//! let mut events: Vec<CustomEvent> = Vec::new();
145//!
146//! query.next_batch_deserialize(&mut events)?;
147//! # }
148//! # Ok(())
149//! # }
150//! ```
151//!
152//! **When to use**: When you need flexible custom event types with straightforward, owned Rust data structures.
153//!
154//! # Performance Considerations
155//!
156//! ## Field Name Interning
157//!
158//! Common EventData field names (e.g., "TargetUserName", "ProcessId", "CommandLine") are automatically interned as `Cow::Borrowed(&'static str)` via a compile-time match statement. This reduces allocations by ~30% for typical Windows security logs.
159//!
160//! ## Publisher Metadata Caching
161//!
162//! Event message formatting via `with_message()` caches publisher metadata handles with RwLock for concurrent read access. First message format per provider is ~5ms, subsequent formats are <0.1ms.
163//!
164//! ## Buffer Reuse
165//!
166//! All `next_batch_*` methods clear and reuse the provided output vector. Preallocate with `Vec::with_capacity(batch_size)` to avoid repeated allocations.
167//!
168//! # Examples
169//!
170//! ## Basic query
171//! ```no_run
172//! use windows_erg::evt::{EventLog, query::QueryBuilder};
173//!
174//! # fn main() -> windows_erg::Result<()> {
175//! let log = EventLog::open("Security")?;
176//! let query = QueryBuilder::new().event_id(4688);
177//! let result = log.query(&query)?;
178//!
179//! for event in result.events {
180//! println!("Event ID: {}, Level: {}", event.id, event.level);
181//! }
182//! # Ok(())
183//! # }
184//! ```
185//!
186//! ## Batch processing with buffer reuse
187//! ```no_run
188//! use windows_erg::evt::{EventLog, EventQuery};
189//!
190//! # fn main() -> windows_erg::Result<()> {
191//! let log = EventLog::open("System")?;
192//! let mut query = log.query_stream("*")?; // All events
193//! let mut batch = Vec::with_capacity(64);
194//!
195//! while query.next_batch(&mut batch)? > 0 {
196//! for event in &batch {
197//! println!("Event: {}", event.provider);
198//! }
199//! }
200//! # Ok(())
201//! # }
202//! ```
203//!
204//! ## Query with filtering
205//! ```no_run
206//! use windows_erg::evt::{EventLog, types::EventLevel, query::QueryBuilder};
207//!
208//! # fn main() -> windows_erg::Result<()> {
209//! let log = EventLog::open("Application")?;
210//! let query = QueryBuilder::new()
211//! .level(EventLevel::Error)
212//! .provider("MyApp");
213//! let result = log.query(&query)?;
214//! # Ok(())
215//! # }
216//! ```
217
218pub mod query;
219pub mod render;
220pub mod types;
221
222use crate::error::{Error, EventLogError, EventLogQueryError, Result};
223use crate::utils::to_utf16_nul;
224use crate::wait::Wait;
225use query::QueryBuilder;
226use std::path::Path;
227use types::{ChannelFilter, Event, EventQueryResult, RenderFormat};
228use windows::Win32::Foundation::GetLastError;
229use windows::Win32::System::EventLog::*;
230use windows::core::PWSTR;
231
232/// Handle to an opened event log or log file.
233pub struct EventLog {
234 handle: EVT_HANDLE,
235 channel_or_path: String,
236 is_file: bool,
237}
238
239impl EventLog {
240 /// Open an event log channel by name.
241 ///
242 /// Examples: "Security", "System", "Application", "Windows PowerShell", etc.
243 pub fn open(channel_name: &str) -> Result<Self> {
244 let channel_wide: Vec<u16> = channel_name
245 .encode_utf16()
246 .chain(std::iter::once(0))
247 .collect();
248
249 let handle = unsafe {
250 EvtOpenLog(
251 EVT_HANDLE::default(), // Local computer
252 PWSTR(channel_wide.as_ptr() as *mut u16),
253 EvtOpenChannelPath.0,
254 )
255 }
256 .map_err(|_| {
257 Error::EventLog(EventLogError::QueryFailed(EventLogQueryError::new(
258 channel_name.to_string(),
259 "Channel not found or access denied",
260 )))
261 })?;
262
263 Ok(EventLog {
264 handle,
265 channel_or_path: channel_name.to_string(),
266 is_file: false,
267 })
268 }
269
270 /// Open an event log file (.evtx, .evt, or .etl format).
271 pub fn open_file(path: &Path) -> Result<Self> {
272 let path_str = path.to_str().ok_or_else(|| {
273 Error::EventLog(EventLogError::QueryFailed(EventLogQueryError::new(
274 "file_path",
275 "Invalid file path",
276 )))
277 })?;
278
279 let path_wide = to_utf16_nul(path_str);
280
281 let handle = unsafe {
282 EvtOpenLog(
283 EVT_HANDLE::default(), // Local computer
284 PWSTR(path_wide.as_ptr() as *mut u16),
285 EvtOpenFilePath.0,
286 )
287 }
288 .map_err(|_| {
289 Error::EventLog(EventLogError::QueryFailed(EventLogQueryError::new(
290 path_str.to_string(),
291 "Log file not found or cannot be read",
292 )))
293 })?;
294
295 Ok(EventLog {
296 handle,
297 channel_or_path: path_str.to_string(),
298 is_file: true,
299 })
300 }
301
302 /// List available event log channels.
303 pub fn list_channels() -> Result<Vec<String>> {
304 Self::list_channels_filtered(ChannelFilter::Operational)
305 }
306
307 /// List event log channels with a specific filter.
308 pub fn list_channels_filtered(filter: ChannelFilter) -> Result<Vec<String>> {
309 let mut channels = Vec::new();
310
311 let enum_handle =
312 unsafe { EvtOpenChannelEnum(EVT_HANDLE::default(), 0) }.map_err(|_| {
313 Error::EventLog(EventLogError::QueryFailed(EventLogQueryError::new(
314 "channels",
315 "Failed to enumerate channels",
316 )))
317 })?;
318
319 let mut buffer = [0u16; 1024];
320
321 loop {
322 let mut buffer_used = 0u32;
323 let result =
324 unsafe { EvtNextChannelPath(enum_handle, Some(&mut buffer[..]), &mut buffer_used) };
325
326 if result.is_err() {
327 break;
328 }
329
330 let channel_name = String::from_utf16_lossy(&buffer[..buffer_used as usize]);
331
332 // Apply filter
333 if should_include_channel(&channel_name, filter) {
334 channels.push(channel_name.to_string());
335 }
336 }
337
338 unsafe {
339 let _ = EvtClose(enum_handle);
340 }
341
342 Ok(channels)
343 }
344
345 /// Query events using a query builder.
346 ///
347 /// This returns all matching events at once. For large result sets,
348 /// prefer `query_stream()` with batch processing.
349 pub fn query(&self, builder: &QueryBuilder) -> Result<EventQueryResult> {
350 let mut result = EventQueryResult::default();
351 self.query_internal(builder, &mut result, None)?;
352 Ok(result)
353 }
354
355 /// Query events in a streaming fashion with batch processing.
356 ///
357 /// Returns an EventQuery handle for batch iteration with buffer reuse.
358 /// Use `query_stream()` for processing large logs efficiently.
359 pub fn query_stream(&self, xpath: &str) -> Result<EventQuery> {
360 let xpath_wide = to_utf16_nul(xpath);
361
362 let channel_wide: Vec<u16> = self
363 .channel_or_path
364 .encode_utf16()
365 .chain(std::iter::once(0))
366 .collect();
367
368 let flags = if self.is_file {
369 EvtQueryFilePath.0
370 } else {
371 EvtQueryChannelPath.0
372 };
373
374 let query_handle = unsafe {
375 EvtQuery(
376 EVT_HANDLE::default(), // Local computer
377 PWSTR(channel_wide.as_ptr() as *mut u16),
378 PWSTR(xpath_wide.as_ptr() as *mut u16),
379 flags,
380 )
381 }
382 .map_err(|_| {
383 Error::EventLog(EventLogError::QueryFailed(EventLogQueryError::new(
384 self.channel_or_path.clone(),
385 "Failed to create query handle",
386 )))
387 })?;
388
389 Ok(EventQuery {
390 handle: query_handle,
391 batch_buffer: vec![0isize; 64], // Changed to 0isize for EvtNext buffer
392 batch_timeout_ms: 1000,
393 render_format: RenderFormat::Values,
394 include_event_data: false,
395 parse_message: false,
396 #[cfg(feature = "serde")]
397 xml_buffer: String::with_capacity(16384),
398 variant_buffer: Vec::with_capacity(8192),
399 corrupted: Vec::new(),
400 total_processed: 0,
401 })
402 }
403
404 /// Internal query implementation.
405 fn query_internal(
406 &self,
407 builder: &QueryBuilder,
408 result: &mut EventQueryResult,
409 _max_events: Option<usize>,
410 ) -> Result<()> {
411 let mut query = self.query_stream(&builder.build_xpath())?;
412
413 // Fetch all batches
414 let mut batch = Vec::with_capacity(64);
415 while query.next_batch(&mut batch)? > 0 {
416 result.events.append(&mut batch);
417 }
418
419 result.corrupted = query.corrupted.clone();
420 result.total_processed = query.total_processed;
421
422 Ok(())
423 }
424}
425
426impl Drop for EventLog {
427 fn drop(&mut self) {
428 if !self.handle.is_invalid() {
429 unsafe {
430 let _ = EvtClose(self.handle);
431 }
432 }
433 }
434}
435
436/// Query result stream for batch iteration with reusable buffers.
437pub struct EventQuery {
438 handle: EVT_HANDLE,
439 batch_buffer: Vec<isize>, // Changed from Vec<EVT_HANDLE> to Vec<isize> for EvtNext
440 batch_timeout_ms: u32,
441 render_format: RenderFormat,
442 include_event_data: bool,
443 parse_message: bool,
444 #[cfg(feature = "serde")]
445 xml_buffer: String,
446 #[allow(dead_code)]
447 variant_buffer: Vec<u8>, // Reserved for future use
448 corrupted: Vec<crate::evt::types::CorruptedEvent>,
449 total_processed: usize,
450}
451
452impl EventQuery {
453 /// Set timeout used by `EvtNext` for each batch retrieval call.
454 pub fn set_batch_timeout(&mut self, timeout: std::time::Duration) {
455 self.batch_timeout_ms = timeout.as_millis().min(u32::MAX as u128) as u32;
456 }
457
458 /// Enable EventData extraction (opt-in).
459 ///
460 /// When enabled, events will have their `data` field populated with EventData key-value pairs.
461 /// Common field names (e.g., "TargetUserName", "ProcessId") are automatically interned
462 /// as `Cow::Borrowed(&'static str)` for zero-copy access.
463 pub fn with_event_data(mut self) -> Self {
464 self.include_event_data = true;
465 self
466 }
467
468 /// Enable message formatting via EvtFormatMessage (opt-in).
469 ///
470 /// When enabled, events will have their `formatted_message` field populated with the
471 /// human-readable event message. Publisher metadata is cached for performance.
472 pub fn with_message(mut self) -> Self {
473 self.parse_message = true;
474 self
475 }
476
477 /// Set the rendering format for events (default: Values).
478 pub fn set_render_format(&mut self, format: RenderFormat) {
479 self.render_format = format;
480 }
481
482 /// Fetch next batch of events into output buffer.
483 ///
484 /// Returns the count of events added to the buffer.
485 /// When no more events are available, returns 0.
486 pub fn next_batch(&mut self, out_events: &mut Vec<Event>) -> Result<usize> {
487 self.next_batch_with_filter(out_events, |_| true)
488 }
489
490 /// Fetch the next batch unless a cancel wait object is already signaled.
491 ///
492 /// Returns `0` when cancellation is requested.
493 pub fn next_batch_or_cancel(
494 &mut self,
495 out_events: &mut Vec<Event>,
496 cancel: &Wait,
497 ) -> Result<usize> {
498 if cancel.is_signaled()? {
499 out_events.clear();
500 return Ok(0);
501 }
502
503 self.next_batch(out_events)
504 }
505
506 /// Fetch next batch with filtering applied during enumeration.
507 ///
508 /// The filter function is called for each parsed event;
509 /// only events where the filter returns true are included.
510 pub fn next_batch_with_filter<F>(
511 &mut self,
512 out_events: &mut Vec<Event>,
513 filter: F,
514 ) -> Result<usize>
515 where
516 F: Fn(&Event) -> bool,
517 {
518 out_events.clear();
519
520 let mut returned = 0u32;
521 let result = unsafe {
522 EvtNext(
523 self.handle,
524 self.batch_buffer.as_mut_slice(),
525 self.batch_timeout_ms,
526 0,
527 &mut returned,
528 )
529 };
530
531 if result.is_err() {
532 // Check for normal end of stream
533 let error_code = unsafe { GetLastError() };
534 if error_code.0 == 259 {
535 // ERROR_NO_MORE_ITEMS
536 return Ok(0);
537 }
538 return Err(Error::EventLog(EventLogError::QueryFailed(
539 EventLogQueryError::with_code("", "EvtNext failed", error_code.0 as i32),
540 )));
541 }
542
543 // Process fetched events
544 for i in 0..returned as usize {
545 let handle_val = self.batch_buffer[i];
546 let handle = EVT_HANDLE(handle_val);
547
548 match render::render_event(
549 handle,
550 self.render_format,
551 self.include_event_data,
552 self.parse_message,
553 ) {
554 Ok(event) => {
555 if filter(&event) {
556 out_events.push(event);
557 }
558 self.total_processed += 1;
559 }
560 Err(corruption_info) => {
561 self.corrupted.push(corruption_info);
562 self.total_processed += 1;
563 }
564 }
565
566 // Close the handle
567 unsafe {
568 let _ = EvtClose(handle);
569 }
570 }
571
572 Ok(out_events.len())
573 }
574
575 /// Process events with a custom converter and filter for each raw event handle.
576 ///
577 /// This allows custom event parsing without allocating the intermediate Event struct.
578 /// The converter receives the raw EVT_HANDLE and returns a custom type T.
579 /// The filter is applied after successful conversion.
580 ///
581 /// # Example
582 /// ```no_run
583 /// use windows_erg::evt::{EventLog, types::{extract_event_id, extract_provider}};
584 ///
585 /// # fn main() -> windows_erg::Result<()> {
586 /// #[derive(Debug)]
587 /// struct LightweightEvent {
588 /// id: u32,
589 /// provider: String,
590 /// }
591 ///
592 /// let log = EventLog::open("Security")?;
593 /// let mut query = log.query_stream("*[System[EventID=4624]]")?;
594 /// let mut events = Vec::new();
595 ///
596 /// query.next_batch_raw_with_filter(
597 /// &mut events,
598 /// |handle| {
599 /// Ok(LightweightEvent {
600 /// id: extract_event_id(handle)?,
601 /// provider: extract_provider(handle)?,
602 /// })
603 /// },
604 /// |event| event.id == 4624,
605 /// )?;
606 /// # Ok(())
607 /// # }
608 /// ```
609 pub fn next_batch_raw_with_filter<T, F, P>(
610 &mut self,
611 out_events: &mut Vec<T>,
612 mut converter: F,
613 filter: P,
614 ) -> Result<usize>
615 where
616 F: FnMut(EVT_HANDLE) -> Result<T>,
617 P: Fn(&T) -> bool,
618 {
619 out_events.clear();
620
621 let mut returned = 0u32;
622 let result = unsafe {
623 EvtNext(
624 self.handle,
625 self.batch_buffer.as_mut_slice(),
626 self.batch_timeout_ms,
627 0,
628 &mut returned,
629 )
630 };
631
632 if result.is_err() {
633 let error_code = unsafe { GetLastError() };
634 if error_code.0 == 259 {
635 return Ok(0);
636 }
637 return Err(Error::EventLog(EventLogError::QueryFailed(
638 EventLogQueryError::with_code("", "EvtNext failed", error_code.0 as i32),
639 )));
640 }
641
642 for i in 0..returned as usize {
643 let handle_val = self.batch_buffer[i];
644 let handle = EVT_HANDLE(handle_val);
645
646 match converter(handle) {
647 Ok(event) => {
648 if filter(&event) {
649 out_events.push(event);
650 }
651 self.total_processed += 1;
652 }
653 Err(_) => {
654 // User's converter handles errors - skip this event
655 self.total_processed += 1;
656 }
657 }
658
659 unsafe {
660 let _ = EvtClose(handle);
661 }
662 }
663
664 Ok(out_events.len())
665 }
666
667 /// Fetch next batch with explicit corruption handling.
668 ///
669 /// Returns both successfully parsed events (Ok) and corrupted events (Err)
670 /// in a single vector, preserving event order.
671 ///
672 /// # Example
673 /// ```no_run
674 /// use windows_erg::evt::EventLog;
675 ///
676 /// # fn main() -> windows_erg::Result<()> {
677 /// let log = EventLog::open("System")?;
678 /// let mut query = log.query_stream("*")?;
679 /// let mut batch = Vec::new();
680 ///
681 /// while query.next_batch_with_results(&mut batch)? > 0 {
682 /// for result in &batch {
683 /// match result {
684 /// Ok(event) => println!("Event: {}", event.id),
685 /// Err(corrupted) => println!("Corrupted: {}", corrupted.reason),
686 /// }
687 /// }
688 /// }
689 /// # Ok(())
690 /// # }
691 /// ```
692 pub fn next_batch_with_results(
693 &mut self,
694 out_events: &mut Vec<std::result::Result<Event, types::CorruptedEvent>>,
695 ) -> Result<usize> {
696 out_events.clear();
697
698 let mut returned = 0u32;
699 let result = unsafe {
700 EvtNext(
701 self.handle,
702 self.batch_buffer.as_mut_slice(),
703 self.batch_timeout_ms,
704 0,
705 &mut returned,
706 )
707 };
708
709 if result.is_err() {
710 let error_code = unsafe { GetLastError() };
711 if error_code.0 == 259 {
712 return Ok(0);
713 }
714 return Err(Error::EventLog(EventLogError::QueryFailed(
715 EventLogQueryError::with_code("", "EvtNext failed", error_code.0 as i32),
716 )));
717 }
718
719 for i in 0..returned as usize {
720 let handle_val = self.batch_buffer[i];
721 let handle = EVT_HANDLE(handle_val);
722
723 match render::render_event(
724 handle,
725 self.render_format,
726 self.include_event_data,
727 self.parse_message,
728 ) {
729 Ok(event) => {
730 out_events.push(Ok(event));
731 self.total_processed += 1;
732 }
733 Err(corruption_info) => {
734 out_events.push(Err(corruption_info));
735 self.total_processed += 1;
736 }
737 }
738
739 unsafe {
740 let _ = EvtClose(handle);
741 }
742 }
743
744 Ok(out_events.len())
745 }
746
747 /// Deserialize events to custom types using serde with owned parsing.
748 ///
749 /// Events are rendered as XML and deserialized into owned Rust values.
750 /// This keeps the API simple and avoids lifetime coupling to internal
751 /// buffers.
752 ///
753 /// Requires the `serde` feature.
754 ///
755 /// # Example
756 /// ```no_run
757 /// use windows_erg::evt::EventLog;
758 /// use serde::Deserialize;
759 ///
760 /// # fn main() -> windows_erg::Result<()> {
761 /// #[derive(Deserialize)]
762 /// struct CustomEvent {
763 /// #[serde(rename = "System")]
764 /// system: SystemData,
765 /// }
766 ///
767 /// #[derive(Deserialize)]
768 /// struct SystemData {
769 /// #[serde(rename = "EventID")]
770 /// event_id: u32,
771 /// #[serde(rename = "Provider")]
772 /// provider: ProviderData,
773 /// }
774 ///
775 /// #[derive(Deserialize)]
776 /// struct ProviderData {
777 /// #[serde(rename = "@Name")]
778 /// name: String,
779 /// }
780 ///
781 /// let log = EventLog::open("Application")?;
782 /// let mut query = log.query_stream("*")?;
783 /// let mut events: Vec<CustomEvent> = Vec::new();
784 ///
785 /// query.next_batch_deserialize(&mut events)?;
786 /// # Ok(())
787 /// # }
788 /// ```
789 #[cfg(feature = "serde")]
790 pub fn next_batch_deserialize<T>(&mut self, out_events: &mut Vec<T>) -> Result<usize>
791 where
792 T: serde::de::DeserializeOwned,
793 {
794 out_events.clear();
795
796 let mut returned = 0u32;
797 let result = unsafe {
798 EvtNext(
799 self.handle,
800 self.batch_buffer.as_mut_slice(),
801 self.batch_timeout_ms,
802 0,
803 &mut returned,
804 )
805 };
806
807 if result.is_err() {
808 let error_code = unsafe { GetLastError() };
809 if error_code.0 == 259 {
810 return Ok(0);
811 }
812 return Err(Error::EventLog(EventLogError::QueryFailed(
813 EventLogQueryError::with_code("", "EvtNext failed", error_code.0 as i32),
814 )));
815 }
816
817 for i in 0..returned as usize {
818 let handle_val = self.batch_buffer[i];
819 let handle = EVT_HANDLE(handle_val);
820
821 // Render to XML
822 self.xml_buffer.clear();
823 let mut buffer = vec![0u8; 16384];
824 let mut buffer_used = 0u32;
825 let mut prop_count = 0u32;
826
827 let render_result = unsafe {
828 EvtRender(
829 EVT_HANDLE::default(),
830 handle,
831 EvtRenderEventXml.0,
832 buffer.len() as u32,
833 Some(buffer.as_mut_ptr() as *mut std::ffi::c_void),
834 &mut buffer_used,
835 &mut prop_count,
836 )
837 };
838
839 if render_result.is_ok() {
840 let xml_bytes = &buffer[..buffer_used as usize];
841 let xml_str = String::from_utf16_lossy(unsafe {
842 std::slice::from_raw_parts(
843 xml_bytes.as_ptr() as *const u16,
844 xml_bytes.len() / 2,
845 )
846 });
847 self.xml_buffer.clear();
848 self.xml_buffer.push_str(&xml_str);
849
850 // Deserialize from XML buffer
851 match quick_xml::de::from_str::<T>(&self.xml_buffer) {
852 Ok(event) => {
853 out_events.push(event);
854 self.total_processed += 1;
855 }
856 Err(_) => {
857 // Deserialization failed - skip this event
858 self.total_processed += 1;
859 }
860 }
861 }
862
863 unsafe {
864 let _ = EvtClose(handle);
865 }
866 }
867
868 Ok(out_events.len())
869 }
870}
871
872impl Drop for EventQuery {
873 fn drop(&mut self) {
874 if !self.handle.is_invalid() {
875 unsafe {
876 let _ = EvtClose(self.handle);
877 }
878 }
879 }
880}
881
882/// Determine if a channel should be included based on filter.
883fn should_include_channel(channel_name: &str, filter: ChannelFilter) -> bool {
884 match filter {
885 ChannelFilter::All => true,
886 ChannelFilter::Operational => {
887 // Operational channels: Application, System, Security, etc.
888 // Exclude Analytic and Debug
889 !channel_name.contains("Analytic") && !channel_name.contains("Debug")
890 }
891 ChannelFilter::AdminOrHigher => {
892 // Similar to Operational for now (can be refined)
893 !channel_name.contains("Analytic") && !channel_name.contains("Debug")
894 }
895 ChannelFilter::IncludeAnalytic => true,
896 }
897}