0

I'm trying to subscribe to Windows events using EvtSubscribe from the winapi crate, but I'm getting ERROR_INVALID_PARAMETER.

I can not find an example in Rust, but did find a C++ example.

My code that produces ERROR_INVALID_PARAMETER:

fn main() {
    unsafe {
        let mut callback: winapi::um::winevt::EVT_SUBSCRIBE_CALLBACK = None;
        let mut session = std::ptr::null_mut();
        let mut signal_event = std::ptr::null_mut();

        let mut bookmark = std::ptr::null_mut();
        let mut context = std::ptr::null_mut();
        let channel_path = "Security";
        let channel_path: winnt::LPWSTR = to_wchar(channel_path);
        let query = "Event/System[EventID=4624]";
        let query: winnt::LPWSTR = to_wchar(query);

        let event_handle = winevt::EvtSubscribe(
            session,
            signal_event,
            channel_path,
            query,
            bookmark,
            context,
            callback,
            winevt::EvtSubscribeStartAtOldestRecord,
        );

        //println!("{:?}", &event_handle);
        println!("{:?}", &winapi::um::errhandlingapi::GetLastError());
    } //unsafe end
}

fn to_vec(str: &str) -> Vec<u16> {
    return OsStr::new(str)
        .encode_wide()
        .chain(Some(0).into_iter())
        .collect();
}

fn to_wchar(str: &str) -> *mut u16 {
    return to_vec(str).as_mut_ptr();
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Tomislav Nekic
  • 215
  • 2
  • 10
  • Your `&` in `println!` are unneeded ([Does println! borrow or own the variable?](https://stackoverflow.com/q/30450399/155423)) and the explicit `return`s in your helpers are non-idiomatic, and `.chain(Some(0))` is sufficient. – Shepmaster Aug 26 '19 at 18:36

1 Answers1

2

The documentation for EvtSubscribe states:

SignalEvent

[...] This parameter must be NULL if the Callback parameter is not NULL.

Callback

[...] This parameter must be NULL if the SignalEvent parameter is not NULL.

The unstated implication here is that exactly one of these parameters must be provided. Passing both is explicitly disallowed, but passing neither would not make sense, as otherwise there would be no way for your code to receive the event.

Passing one of these values should cause the code to start working.

Editorially, this is a good example of where a Rust enum would have been a better way to model the API. This would clearly show that the two options are mutually exclusive and one is required:

enum Subscriber {
    EventObject(HANDLE),
    Callback(EVT_SUBSCRIBE_CALLBACK),
}

Incidentally, your implementation of to_wchar is incorrect and likely leads to memory unsafety. to_vec allocates memory, you take a pointer to it, then that memory is deallocated, creating a dangling pointer. The bad pointer is read by the C code inside of the unsafe block — part of the reason unsafe is needed.

You either need to use mem::forget, as shown in How to expose a Rust `Vec<T>` to FFI? (and then you need to prevent leaking the memory somehow), or you need to take a reference to the data instead of taking the raw pointer.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366