win-idispatch 0.3.0

This is a rust crate that aims to provide a more ergonomic way of working with idispatch in winapi based projects.
Documentation
use log::trace;
use std::ptr;
use winapi::{
    shared::{guiddef::GUID, minwindef::LPVOID, winerror::NOERROR, wtypesbase::CLSCTX},
    um::{combaseapi::CoCreateInstance, oaidl::LPDISPATCH, unknwnbase::LPUNKNOWN},
};

use super::guids::{IID_IDISPATCH, IID_IUNKNOWN};
use super::{dispatch::DispatchInterface, Error};

/// UnknownInterface is a structure that maintains access to an IUnkonwn
/// pointer. The pointer will be cleaned up correctly when the reference
/// is dropped.
pub struct UnknownInterface {
    unknown: LPUNKNOWN,
    released: bool,
}

impl UnknownInterface {
    /// Create a new Unknown interface based on the provided class GUID and
    /// the class context.
    pub fn new(class_id: GUID, class_context: CLSCTX) -> Result<UnknownInterface, Error> {
        let mut unknown: LPVOID = ptr::null_mut();
        let result = unsafe {
            CoCreateInstance(
                &class_id,
                ptr::null_mut(),
                class_context,
                &IID_IUNKNOWN,
                &mut unknown,
            )
        };
        if result != NOERROR {
            Err(result.into())
        } else {
            Ok(UnknownInterface {
                unknown: unknown as LPUNKNOWN,
                released: false,
            })
        }
    }
    /// Gets a DisptachInterface to work with the COM server registered with
    /// the class GUID provided to the unknown interface.
    pub fn get_dispatch_interface(&self) -> Result<DispatchInterface, Error> {
        let mut dispatch: LPVOID = ptr::null_mut();
        let result = unsafe {
            if let Some(r) = self.unknown.as_ref() {
                r.QueryInterface(&IID_IDISPATCH, &mut dispatch)
            } else {
                return Err(Error::NullUnknownPointer);
            }
        };
        if result != NOERROR {
            Err(result.into())
        } else {
            Ok(DispatchInterface::new(dispatch as LPDISPATCH))
        }
    }

    fn release(&mut self) {
        if self.released {
            return;
        }
        unsafe {
            if let Some(r) = self.unknown.as_ref() {
                r.Release();
                self.released = true;
                self.unknown = ptr::null_mut();
            }
        }
    }
}

impl Drop for UnknownInterface {
    fn drop(&mut self) {
        trace!("dropping unknown interface");
        self.release();
    }
}