update string writer logic, add a few methods, capture stderr as well

This commit is contained in:
Kadir Yamamoto
2023-03-25 11:51:10 +09:00
parent c5d4b9edf9
commit 59987cb4d3
6 changed files with 232 additions and 60 deletions
+38 -45
View File
@@ -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> {
+68 -9
View File
@@ -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)
}
+3
View File
@@ -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,
+22
View File
@@ -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
View File
@@ -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 {
+2 -1
View File
@@ -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::*;