mirror of
https://github.com/yamakadi/clroxide
synced 2026-06-08 18:28:14 +00:00
update string writer logic, add a few methods, capture stderr as well
This commit is contained in:
+38
-45
@@ -1,7 +1,4 @@
|
||||
use crate::primitives::{
|
||||
ICLRMetaHost, ICLRRuntimeInfo, ICorRuntimeHost, IUnknown, Interface, _AppDomain, _MethodInfo,
|
||||
_StringWriter, wrap_method_arguments, GUID, HRESULT,
|
||||
};
|
||||
use crate::primitives::{ICLRMetaHost, ICLRRuntimeInfo, ICorRuntimeHost, IUnknown, Interface, _AppDomain, _MethodInfo, wrap_method_arguments, GUID, HRESULT, empty_variant_array};
|
||||
use std::{ffi::c_void, ptr};
|
||||
use windows::Win32::System::Com::VARIANT;
|
||||
#[cfg(feature = "default-loader")]
|
||||
@@ -25,8 +22,12 @@ pub struct ClrContext {
|
||||
|
||||
pub struct OutputContext {
|
||||
pub set_out: *mut _MethodInfo,
|
||||
pub set_err: *mut _MethodInfo,
|
||||
pub to_string: *mut _MethodInfo,
|
||||
pub original_stdout: VARIANT,
|
||||
pub original_stderr: VARIANT,
|
||||
pub redirected_stdout: VARIANT,
|
||||
pub redirected_stderr: VARIANT,
|
||||
}
|
||||
|
||||
impl Clr {
|
||||
@@ -124,27 +125,41 @@ impl Clr {
|
||||
pub fn redirect_output(&mut self) -> Result<(), String> {
|
||||
let context = self.get_context()?;
|
||||
|
||||
let assembly = unsafe { (*(&context).app_domain).load_library("mscorlib")? };
|
||||
// Get mscorlib assembly
|
||||
let mscorlib = unsafe { (*(&context).app_domain).load_library("mscorlib")? };
|
||||
|
||||
let console = unsafe { (*assembly).get_type("System.Console")? };
|
||||
// Sort out console related types/functions
|
||||
let console = unsafe { (*mscorlib).get_type("System.Console")? };
|
||||
|
||||
let get_out = unsafe { (*console).get_method("get_Out")? };
|
||||
|
||||
let set_out = unsafe { (*console).get_method("SetOut")? };
|
||||
let get_err = unsafe { (*console).get_method("get_Error")? };
|
||||
let set_err = unsafe { (*console).get_method("SetError")? };
|
||||
|
||||
let old_console = unsafe { (*get_out).invoke_without_args(None)? };
|
||||
let old_out = unsafe { (*get_out).invoke_without_args(None)? };
|
||||
let old_err = unsafe { (*get_err).invoke_without_args(None)? };
|
||||
|
||||
// Sort out string writer related types/functions
|
||||
let string_writer = unsafe { (*mscorlib).get_type("System.IO.StringWriter")? };
|
||||
let to_string = unsafe { (*string_writer).get_method("ToString")? };
|
||||
|
||||
let string_writer_instance =
|
||||
unsafe { (*assembly).create_instance("System.IO.StringWriter")? };
|
||||
unsafe { (*mscorlib).create_instance("System.IO.StringWriter")? };
|
||||
|
||||
let method_args = wrap_method_arguments(vec![string_writer_instance.clone()])?;
|
||||
|
||||
// Replace stdout and stderr with the same StringWriter instance
|
||||
unsafe { (*set_out).invoke(method_args, None)? };
|
||||
unsafe { (*set_err).invoke(method_args, None)? };
|
||||
|
||||
self.output_context = Some(OutputContext {
|
||||
set_out,
|
||||
original_stdout: old_console,
|
||||
redirected_stdout: string_writer_instance,
|
||||
set_err,
|
||||
to_string,
|
||||
original_stdout: old_out,
|
||||
original_stderr: old_err,
|
||||
redirected_stdout: string_writer_instance.clone(),
|
||||
redirected_stderr: string_writer_instance.clone(),
|
||||
});
|
||||
|
||||
Ok(())
|
||||
@@ -157,9 +172,15 @@ impl Clr {
|
||||
|
||||
let context = self.output_context.as_ref().unwrap();
|
||||
|
||||
let method_args = wrap_method_arguments(vec![context.original_stdout.clone()])?;
|
||||
unsafe { (*(&context).set_out).invoke(
|
||||
wrap_method_arguments(vec![context.original_stdout.clone()])?,
|
||||
None
|
||||
)? };
|
||||
|
||||
unsafe { (*(&context).set_out).invoke(method_args, None)? };
|
||||
unsafe { (*(&context).set_err).invoke(
|
||||
wrap_method_arguments(vec![context.original_stderr.clone()])?,
|
||||
None
|
||||
)? };
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -170,41 +191,13 @@ impl Clr {
|
||||
}
|
||||
|
||||
let context = self.output_context.as_ref().unwrap();
|
||||
let instance = context.redirected_stdout.clone();
|
||||
|
||||
let dispatchable: &*mut IUnknown = unsafe {
|
||||
std::mem::transmute(
|
||||
context
|
||||
.redirected_stdout
|
||||
.Anonymous
|
||||
.Anonymous
|
||||
.Anonymous
|
||||
.pdispVal
|
||||
.as_ref()
|
||||
.unwrap(),
|
||||
)
|
||||
};
|
||||
let mut string_writer_ptr: *mut _StringWriter = ptr::null_mut();
|
||||
|
||||
if dispatchable.is_null() {
|
||||
return Err("Could not retrieve StringWriter".into());
|
||||
}
|
||||
|
||||
let hr = unsafe {
|
||||
(**dispatchable).QueryInterface(
|
||||
&_StringWriter::IID,
|
||||
&mut string_writer_ptr as *mut *mut _ as *mut *mut c_void,
|
||||
)
|
||||
let result = unsafe {
|
||||
(*(&context).to_string).invoke(empty_variant_array(), Some(instance.clone()))?
|
||||
};
|
||||
|
||||
if hr.is_err() {
|
||||
return Err(format!("Error while retrieving StringWriter: 0x{:x}", hr.0));
|
||||
}
|
||||
|
||||
if string_writer_ptr.is_null() {
|
||||
return Err("Could not retrieve StringWriter".into());
|
||||
}
|
||||
|
||||
Ok(unsafe { (*string_writer_ptr).to_string()? })
|
||||
Ok(unsafe { result.Anonymous.Anonymous.Anonymous.bstrVal.to_string() })
|
||||
}
|
||||
|
||||
pub fn get_context(&mut self) -> Result<&ClrContext, String> {
|
||||
|
||||
@@ -1,14 +1,21 @@
|
||||
use std::{ffi::c_void, mem::ManuallyDrop, ptr};
|
||||
use std::{
|
||||
ffi::c_void,
|
||||
mem::{size_of, ManuallyDrop},
|
||||
ptr,
|
||||
};
|
||||
use windows::{
|
||||
core::BSTR,
|
||||
Win32::System::{
|
||||
Com::{
|
||||
SAFEARRAY, SAFEARRAYBOUND, VARENUM, VARIANT, VARIANT_0, VARIANT_0_0, VARIANT_0_0_0,
|
||||
VT_ARRAY, VT_BSTR, VT_EMPTY, VT_UI1, VT_UNKNOWN, VT_VARIANT,
|
||||
},
|
||||
Ole::{
|
||||
SafeArrayAccessData, SafeArrayCreate, SafeArrayCreateVector, SafeArrayGetLBound,
|
||||
SafeArrayGetUBound, SafeArrayPutElement, SafeArrayUnaccessData,
|
||||
Win32::{
|
||||
Foundation::VARIANT_BOOL,
|
||||
System::{
|
||||
Com::{
|
||||
SAFEARRAY, SAFEARRAYBOUND, VARENUM, VARIANT, VARIANT_0, VARIANT_0_0, VARIANT_0_0_0,
|
||||
VT_ARRAY, VT_BOOL, VT_BSTR, VT_EMPTY, VT_I8, VT_UI1, VT_UNKNOWN, VT_VARIANT,
|
||||
},
|
||||
Ole::{
|
||||
SafeArrayAccessData, SafeArrayCreate, SafeArrayCreateVector, SafeArrayGetElement,
|
||||
SafeArrayGetLBound, SafeArrayGetUBound, SafeArrayPutElement, SafeArrayUnaccessData,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -83,6 +90,36 @@ pub fn wrap_unknown_ptr_in_variant(unknown_ptr: *mut c_void) -> VARIANT {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wrap_bool_in_variant(value: bool) -> VARIANT {
|
||||
VARIANT {
|
||||
Anonymous: VARIANT_0 {
|
||||
Anonymous: ManuallyDrop::new(VARIANT_0_0 {
|
||||
vt: VT_BOOL,
|
||||
wReserved1: 0,
|
||||
wReserved2: 0,
|
||||
wReserved3: 0,
|
||||
Anonymous: VARIANT_0_0_0 {
|
||||
boolVal: VARIANT_BOOL::from(value),
|
||||
},
|
||||
}),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wrap_i64_in_variant(value: i64) -> VARIANT {
|
||||
VARIANT {
|
||||
Anonymous: VARIANT_0 {
|
||||
Anonymous: ManuallyDrop::new(VARIANT_0_0 {
|
||||
vt: VT_I8,
|
||||
wReserved1: 0,
|
||||
wReserved2: 0,
|
||||
wReserved3: 0,
|
||||
Anonymous: VARIANT_0_0_0 { llVal: value },
|
||||
}),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wrap_string_in_variant(string: &str) -> VARIANT {
|
||||
let inner = BSTR::from(string);
|
||||
|
||||
@@ -161,3 +198,25 @@ pub fn wrap_method_arguments(arguments: Vec<VARIANT>) -> Result<*mut SAFEARRAY,
|
||||
|
||||
Ok(variant_array_ptr)
|
||||
}
|
||||
|
||||
pub fn unpack_byte_array(safe_array_ptr: *mut SAFEARRAY) -> Result<Vec<u8>, String> {
|
||||
let ubound = unsafe { SafeArrayGetUBound(safe_array_ptr, 1) }.unwrap_or(0);
|
||||
let mut results: Vec<u8> = vec![];
|
||||
|
||||
for i in 0..ubound {
|
||||
let indices: [i32; 1] = [i as _];
|
||||
let mut variant: u8 = 0;
|
||||
let pv = &mut variant as *mut _ as *mut c_void;
|
||||
|
||||
match unsafe { SafeArrayGetElement(safe_array_ptr, indices.as_ptr(), pv) } {
|
||||
Ok(_) => {},
|
||||
Err(e) => return Err(format!("Could not access safe array: {:?}", e.code())),
|
||||
}
|
||||
|
||||
if !pv.is_null() {
|
||||
results.push(variant);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
@@ -13,6 +13,9 @@ pub struct _AppDomain {
|
||||
pub vtable: *const _AppDomainVtbl,
|
||||
}
|
||||
|
||||
unsafe impl Sync for _AppDomain {}
|
||||
unsafe impl Send for _AppDomain {}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct _AppDomainVtbl {
|
||||
pub parent: IUnknownVtbl,
|
||||
|
||||
@@ -152,6 +152,23 @@ impl _PropertyInfo {
|
||||
Ok(return_value)
|
||||
}
|
||||
|
||||
pub fn set_value(&self, value: VARIANT, instance: Option<VARIANT>) -> Result<(), String> {
|
||||
let object: VARIANT = match instance {
|
||||
None => unsafe { std::mem::zeroed() },
|
||||
Some(i) => i,
|
||||
};
|
||||
|
||||
let index = empty_array();
|
||||
|
||||
let hr = unsafe { (*self).SetValue(object, value, index) };
|
||||
|
||||
if hr.is_err() {
|
||||
return Err(format!("Could not invoke method: {:?}", hr));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn ToString(&self, pRetVal: *mut *mut u16) -> HRESULT {
|
||||
((*self.vtable).ToString)(self as *const _ as *mut _, pRetVal)
|
||||
@@ -166,6 +183,11 @@ impl _PropertyInfo {
|
||||
) -> HRESULT {
|
||||
((*self.vtable).GetValue)(self as *const _ as *mut _, obj, index, pRetVal)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn SetValue(&self, obj: VARIANT, val: VARIANT, index: *mut SAFEARRAY) -> HRESULT {
|
||||
((*self.vtable).SetValue)(self as *const _ as *mut _, obj, val, index)
|
||||
}
|
||||
}
|
||||
|
||||
impl Interface for _PropertyInfo {
|
||||
|
||||
+99
-5
@@ -1,6 +1,6 @@
|
||||
use crate::primitives::{
|
||||
IUnknown, IUnknownVtbl, Interface, _Assembly, _MethodInfo, _PropertyInfo, empty_variant_array,
|
||||
GUID, HRESULT,
|
||||
IUnknown, IUnknownVtbl, Interface, _Assembly, _ConstructorInfo, _MethodInfo, _PropertyInfo,
|
||||
empty_variant_array, wrap_method_arguments, GUID, HRESULT,
|
||||
};
|
||||
use std::{
|
||||
ffi::{c_long, c_void},
|
||||
@@ -55,7 +55,11 @@ pub struct _TypeVtbl {
|
||||
pub GetArrayRank: *const c_void,
|
||||
pub get_BaseType:
|
||||
unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut _Type) -> HRESULT,
|
||||
pub GetConstructors: *const c_void,
|
||||
pub GetConstructors: unsafe extern "system" fn(
|
||||
this: *mut c_void,
|
||||
bindingAttr: BindingFlags,
|
||||
pRetVal: *mut *mut SAFEARRAY,
|
||||
) -> HRESULT,
|
||||
pub GetInterface: *const c_void,
|
||||
pub GetInterfaces: *const c_void,
|
||||
pub FindInterfaces: *const c_void,
|
||||
@@ -115,8 +119,13 @@ pub struct _TypeVtbl {
|
||||
) -> HRESULT,
|
||||
pub GetConstructor: *const c_void,
|
||||
pub GetConstructor_2: *const c_void,
|
||||
pub GetConstructor_3: *const c_void,
|
||||
pub GetConstructors_2: *const c_void,
|
||||
pub GetConstructor_3: unsafe extern "system" fn(
|
||||
this: *mut c_void,
|
||||
types: *mut SAFEARRAY,
|
||||
pRetVal: *mut *mut _ConstructorInfo,
|
||||
) -> HRESULT,
|
||||
pub GetConstructors_2:
|
||||
unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut SAFEARRAY) -> HRESULT,
|
||||
pub get_TypeInitializer: *const c_void,
|
||||
pub GetMethod_3: *const c_void,
|
||||
pub GetMethod_4: unsafe extern "system" fn(
|
||||
@@ -211,6 +220,77 @@ impl _Type {
|
||||
Ok(buffer.to_string())
|
||||
}
|
||||
|
||||
pub fn get_constructor(
|
||||
&self,
|
||||
parameter_types: Vec<VARIANT>,
|
||||
) -> Result<*mut _ConstructorInfo, String> {
|
||||
let mut constructor_ptr: *mut _ConstructorInfo = ptr::null_mut();
|
||||
let mut type_array = wrap_method_arguments(parameter_types)?;
|
||||
let hr = unsafe { (*self).GetConstructor_3(type_array, &mut constructor_ptr) };
|
||||
|
||||
if hr.is_err() {
|
||||
return Err(format!("Error while retrieving constructor: 0x{:x}", hr.0));
|
||||
}
|
||||
|
||||
if constructor_ptr.is_null() {
|
||||
return Err("Could not retrieve constructor".into());
|
||||
}
|
||||
|
||||
Ok(constructor_ptr)
|
||||
}
|
||||
|
||||
pub fn get_constructor_with_signature(
|
||||
&self,
|
||||
signature: &str,
|
||||
) -> Result<*mut _ConstructorInfo, String> {
|
||||
let constructors = self.get_constructors()?;
|
||||
|
||||
for constructor in constructors {
|
||||
let constructor_name = unsafe { (*constructor).to_string()? };
|
||||
|
||||
if &constructor_name == signature {
|
||||
return Ok(constructor);
|
||||
}
|
||||
}
|
||||
|
||||
Err(format!(
|
||||
"Could not find a constructor with the given signature: {}",
|
||||
signature
|
||||
))
|
||||
}
|
||||
|
||||
pub fn get_constructors(&self) -> Result<Vec<*mut _ConstructorInfo>, String> {
|
||||
let mut results: Vec<*mut _ConstructorInfo> = vec![];
|
||||
|
||||
let mut safe_array_ptr: *mut SAFEARRAY =
|
||||
unsafe { SafeArrayCreateVector(VT_UNKNOWN, 0, 255) };
|
||||
|
||||
let hr = unsafe { (*self).GetConstructors_2(&mut safe_array_ptr) };
|
||||
|
||||
if hr.is_err() {
|
||||
return Err(format!("Error while retrieving constructors: 0x{:x}", hr.0));
|
||||
}
|
||||
|
||||
let ubound = unsafe { SafeArrayGetUBound(safe_array_ptr, 1) }.unwrap_or(0);
|
||||
|
||||
for i in 0..ubound {
|
||||
let indices: [i32; 1] = [i as _];
|
||||
let mut variant: *mut _ConstructorInfo = ptr::null_mut();
|
||||
let pv = &mut variant as *mut _ as *mut c_void;
|
||||
|
||||
match unsafe { SafeArrayGetElement(safe_array_ptr, indices.as_ptr(), pv) } {
|
||||
Ok(_) => {},
|
||||
Err(e) => return Err(format!("Could not access safe array: {:?}", e.code())),
|
||||
}
|
||||
|
||||
if !pv.is_null() {
|
||||
results.push(variant)
|
||||
}
|
||||
}
|
||||
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
pub fn get_method(&self, name: &str) -> Result<*mut _MethodInfo, String> {
|
||||
let dw = BSTR::from(name);
|
||||
|
||||
@@ -556,6 +636,20 @@ impl _Type {
|
||||
pub unsafe fn GetProperties_2(&self, pRetVal: *mut *mut SAFEARRAY) -> HRESULT {
|
||||
((*self.vtable).GetProperties_2)(self as *const _ as *mut _, pRetVal)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn GetConstructor_3(
|
||||
&self,
|
||||
types: *mut SAFEARRAY,
|
||||
pRetVal: *mut *mut _ConstructorInfo,
|
||||
) -> HRESULT {
|
||||
((*self.vtable).GetConstructor_3)(self as *const _ as *mut _, types, pRetVal)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn GetConstructors_2(&self, pRetVal: *mut *mut SAFEARRAY) -> HRESULT {
|
||||
((*self.vtable).GetConstructors_2)(self as *const _ as *mut _, pRetVal)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for _Type {
|
||||
|
||||
@@ -4,11 +4,11 @@ mod iappdomain;
|
||||
mod iassembly;
|
||||
mod iclrmetahost;
|
||||
mod iclrruntimeinfo;
|
||||
mod iconstructorinfo;
|
||||
mod icorruntimehost;
|
||||
mod ienumunknown;
|
||||
mod imethodinfo;
|
||||
mod ipropertyinfo;
|
||||
mod istringwriter;
|
||||
mod itype;
|
||||
mod iunknown;
|
||||
mod types;
|
||||
@@ -18,6 +18,7 @@ pub use iappdomain::*;
|
||||
pub use iassembly::*;
|
||||
pub use iclrmetahost::*;
|
||||
pub use iclrruntimeinfo::*;
|
||||
pub use iconstructorinfo::*;
|
||||
pub use icorruntimehost::*;
|
||||
pub use ienumunknown::*;
|
||||
pub use imethodinfo::*;
|
||||
|
||||
Reference in New Issue
Block a user