clroxide 1.1.1

A library that allows you to host the CLR and execute dotnet binaries.
Documentation
use crate::primitives::{
    empty_variant_array, get_array_length, itype::_Type, IUnknown, IUnknownVtbl, Interface, GUID,
    HRESULT,
};
use std::{
    ffi::{c_long, c_void},
    ops::Deref,
};
use windows::{
    core::BSTR,
    Win32::System::{
        Com::{SAFEARRAY, VARIANT, VT_UNKNOWN},
        Ole::SafeArrayCreateVector,
    },
};

#[repr(C)]
pub struct _MethodInfo {
    pub vtable: *const _MethodInfoVtbl,
}

#[repr(C)]
pub struct _MethodInfoVtbl {
    pub parent: IUnknownVtbl,
    pub GetTypeInfoCount: *const c_void,
    pub GetTypeInfo: *const c_void,
    pub GetIDsOfNames: *const c_void,
    pub Invoke: *const c_void,
    pub ToString: unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut u16) -> HRESULT,
    pub Equals: *const c_void,
    pub GetHashCode: unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut c_long) -> HRESULT,
    pub GetType: unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut _Type) -> HRESULT,
    pub get_MemberType: *const c_void,
    pub get_name: unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut u16) -> HRESULT,
    pub get_DeclaringType: *const c_void,
    pub get_ReflectedType: *const c_void,
    pub GetCustomAttributes: *const c_void,
    pub GetCustomAttributes_2: *const c_void,
    pub IsDefined: *const c_void,
    pub GetParameters:
        unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut SAFEARRAY) -> HRESULT,
    pub GetMethodImplementationFlags: *const c_void,
    pub get_MethodHandle: *const c_void,
    pub get_Attributes: *const c_void,
    pub get_CallingConvention: *const c_void,
    pub Invoke_2: *const c_void,
    pub get_IsPublic: *const c_void,
    pub get_IsPrivate: *const c_void,
    pub get_IsFamily: *const c_void,
    pub get_IsAssembly: *const c_void,
    pub get_IsFamilyAndAssembly: *const c_void,
    pub get_IsFamilyOrAssembly: *const c_void,
    pub get_IsStatic: *const c_void,
    pub get_IsFinal: *const c_void,
    pub get_IsVirtual: *const c_void,
    pub get_IsHideBySig: *const c_void,
    pub get_IsAbstract: *const c_void,
    pub get_IsSpecialName: *const c_void,
    pub get_IsConstructor: *const c_void,
    pub Invoke_3: unsafe extern "system" fn(
        this: *mut c_void,
        obj: VARIANT,
        parameters: *mut SAFEARRAY,
        pRetVal: *mut VARIANT,
    ) -> HRESULT,
    pub get_returnType: *const c_void,
    pub get_ReturnTypeCustomAttributes: *const c_void,
    pub GetBaseDefinition:
        unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut _MethodInfo) -> HRESULT,
}

impl _MethodInfo {
    pub fn invoke(
        &self,
        args: *mut SAFEARRAY,
        instance: Option<VARIANT>,
    ) -> Result<VARIANT, String> {
        let args_len = get_array_length(args);
        let parameter_count = (*self).get_parameter_count()?;

        if args_len != parameter_count {
            return Err(format!(
                "Arguments do not match method signature: {} given, {} expected",
                args_len, parameter_count
            ));
        }

        let mut return_value: VARIANT = unsafe { std::mem::zeroed() };

        let object: VARIANT = match instance {
            None => unsafe { std::mem::zeroed() },
            Some(i) => i,
        };

        let hr = unsafe { (*self).Invoke_3(object, args, &mut return_value) };

        if hr.is_err() {
            return Err(format!("Could not invoke method: {:?}", hr));
        }

        Ok(return_value)
    }

    pub fn invoke_without_args(&self, instance: Option<VARIANT>) -> Result<VARIANT, String> {
        let method_args = empty_variant_array();

        (*self).invoke(method_args, instance)
    }

    pub fn get_parameter_count(&self) -> Result<i32, String> {
        let mut safe_array_ptr: *mut SAFEARRAY =
            unsafe { SafeArrayCreateVector(VT_UNKNOWN, 0, 255) };

        let hr = unsafe { (*self).GetParameters(&mut safe_array_ptr) };

        if hr.is_err() {
            return Err(format!("Could not get parameter count: {:?}", hr));
        }

        Ok(get_array_length(safe_array_ptr))
    }

    pub fn to_string(&self) -> Result<String, String> {
        let mut buffer = BSTR::new();

        let hr = unsafe { (*self).ToString(&mut buffer as *mut _ as *mut *mut u16) };

        if hr.is_err() {
            return Err(format!("Failed while running `ToString`: {:?}", hr));
        }

        Ok(buffer.to_string())
    }

    #[inline]
    pub unsafe fn ToString(&self, pRetVal: *mut *mut u16) -> HRESULT {
        ((*self.vtable).ToString)(self as *const _ as *mut _, pRetVal)
    }

    #[inline]
    pub unsafe fn GetHashCode(&self, pRetVal: *mut c_long) -> HRESULT {
        ((*self.vtable).GetHashCode)(self as *const _ as *mut _, pRetVal)
    }

    #[inline]
    pub unsafe fn GetType(&self, pRetVal: *mut *mut _Type) -> HRESULT {
        ((*self.vtable).GetType)(self as *const _ as *mut _, pRetVal)
    }

    #[inline]
    pub unsafe fn get_name(&self, pRetVal: *mut *mut u16) -> HRESULT {
        ((*self.vtable).get_name)(self as *const _ as *mut _, pRetVal)
    }

    #[inline]
    pub unsafe fn GetParameters(&self, pRetVal: *mut *mut SAFEARRAY) -> HRESULT {
        ((*self.vtable).GetParameters)(self as *const _ as *mut _, pRetVal)
    }

    #[inline]
    pub unsafe fn Invoke_3(
        &self,
        obj: VARIANT,
        parameters: *mut SAFEARRAY,
        pRetVal: *mut VARIANT,
    ) -> HRESULT {
        ((*self.vtable).Invoke_3)(self as *const _ as *mut _, obj, parameters, pRetVal)
    }

    #[inline]
    pub unsafe fn GetBaseDefinition(&self, pRetVal: *mut *mut _MethodInfo) -> HRESULT {
        ((*self.vtable).GetBaseDefinition)(self as *const _ as *mut _, pRetVal)
    }
}

impl Interface for _MethodInfo {
    const IID: GUID = GUID::from_values(
        0xffcc1b5d,
        0xecb8,
        0x38dd,
        [0x9b, 0x01, 0x3d, 0xc8, 0xab, 0xc2, 0xaa, 0x5f],
    );

    fn vtable(&self) -> *const c_void {
        self.vtable as *const _ as *const c_void
    }
}

impl Deref for _MethodInfo {
    type Target = IUnknown;

    #[inline]
    fn deref(&self) -> &IUnknown {
        unsafe { &*(self as *const _MethodInfo as *const IUnknown) }
    }
}