mirror of
https://github.com/yamakadi/clroxide
synced 2026-06-08 18:28:14 +00:00
fix getting version strings -- my bad >< -- and allow specifying version, using V4 as default
This commit is contained in:
+1
-1
@@ -8,7 +8,7 @@ documentation = "https://docs.rs/clroxide"
|
||||
readme = "README.md"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/yamakadi/clroxide"
|
||||
version = "1.0.9"
|
||||
version = "1.1.0"
|
||||
exclude = ["/test"]
|
||||
|
||||
[lib]
|
||||
|
||||
@@ -5,7 +5,7 @@ use clroxide::{
|
||||
use windows::Win32::System::Com::VARIANT;
|
||||
|
||||
fn main() -> Result<(), String> {
|
||||
let mut clr = Clr::context_only()?;
|
||||
let mut clr = Clr::context_only(None)?;
|
||||
let mut context = clr.get_context()?;
|
||||
let app_domain = context.app_domain;
|
||||
let mscorlib = unsafe { (*app_domain).load_library("mscorlib")? };
|
||||
|
||||
@@ -18,7 +18,7 @@ fn main() -> Result<(), String> {
|
||||
}
|
||||
|
||||
pub unsafe fn run(contents: Vec<u8>, args: Vec<String>) -> Result<String, String> {
|
||||
let mut clr = Clr::context_only()?;
|
||||
let mut clr = Clr::context_only(None)?;
|
||||
|
||||
clr.redirect_output()?;
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ fn main() -> Result<(), String> {
|
||||
let (path, args) = prepare_args();
|
||||
let contents = fs::read(path).expect("Unable to read file");
|
||||
|
||||
let mut clr = Clr::context_only()?;
|
||||
let mut clr = Clr::context_only(None)?;
|
||||
let mut context = clr.get_context()?;
|
||||
let app_domain = context.app_domain;
|
||||
let mscorlib = unsafe { (*app_domain).load_library("mscorlib")? };
|
||||
|
||||
+54
-5
@@ -1,6 +1,6 @@
|
||||
use crate::primitives::{
|
||||
ICLRMetaHost, ICLRRuntimeInfo, ICorRuntimeHost, _AppDomain, _MethodInfo, empty_variant_array,
|
||||
wrap_method_arguments, GUID, HRESULT,
|
||||
wrap_method_arguments, RuntimeVersion, GUID, HRESULT,
|
||||
};
|
||||
use std::ffi::c_void;
|
||||
use windows::Win32::System::Com::VARIANT;
|
||||
@@ -13,6 +13,7 @@ pub struct Clr {
|
||||
create_interface: isize,
|
||||
context: Option<ClrContext>,
|
||||
output_context: Option<OutputContext>,
|
||||
version: RuntimeVersion,
|
||||
}
|
||||
|
||||
pub struct ClrContext {
|
||||
@@ -44,11 +45,30 @@ impl Clr {
|
||||
create_interface,
|
||||
context: None,
|
||||
output_context: None,
|
||||
version: RuntimeVersion::V4,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "default-loader")]
|
||||
pub fn context_only() -> Result<Clr, String> {
|
||||
pub fn new_with_runtime(
|
||||
contents: Vec<u8>,
|
||||
arguments: Vec<String>,
|
||||
version: RuntimeVersion,
|
||||
) -> Result<Clr, String> {
|
||||
let create_interface = load_function("mscoree.dll", "CreateInterface")?;
|
||||
|
||||
Ok(Clr {
|
||||
contents,
|
||||
arguments,
|
||||
create_interface,
|
||||
context: None,
|
||||
output_context: None,
|
||||
version,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "default-loader")]
|
||||
pub fn context_only(version: Option<RuntimeVersion>) -> Result<Clr, String> {
|
||||
let create_interface = load_function("mscoree.dll", "CreateInterface")?;
|
||||
|
||||
Ok(Clr {
|
||||
@@ -57,6 +77,7 @@ impl Clr {
|
||||
create_interface,
|
||||
context: None,
|
||||
output_context: None,
|
||||
version: version.unwrap_or(RuntimeVersion::V4),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -74,11 +95,34 @@ impl Clr {
|
||||
create_interface,
|
||||
context: None,
|
||||
output_context: None,
|
||||
version: RuntimeVersion::V4,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "default-loader"))]
|
||||
pub fn context_only(load_function: fn() -> Result<isize, String>) -> Result<Clr, String> {
|
||||
pub fn new_with_runtime(
|
||||
contents: Vec<u8>,
|
||||
arguments: Vec<String>,
|
||||
version: RuntimeVersion,
|
||||
load_function: fn() -> Result<isize, String>,
|
||||
) -> Result<Clr, String> {
|
||||
let create_interface = load_function()?;
|
||||
|
||||
Ok(Clr {
|
||||
contents,
|
||||
arguments,
|
||||
create_interface,
|
||||
context: None,
|
||||
output_context: None,
|
||||
version,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "default-loader"))]
|
||||
pub fn context_only(
|
||||
load_function: fn() -> Result<isize, String>,
|
||||
version: Option<RuntimeVersion>,
|
||||
) -> Result<Clr, String> {
|
||||
let create_interface = load_function()?;
|
||||
|
||||
Ok(Clr {
|
||||
@@ -87,6 +131,7 @@ impl Clr {
|
||||
create_interface,
|
||||
context: None,
|
||||
output_context: None,
|
||||
version: version.unwrap_or(RuntimeVersion::V4),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -213,10 +258,14 @@ impl Clr {
|
||||
}
|
||||
|
||||
let host = self.get_clr_host()?;
|
||||
let runtime_info = unsafe { (*host).get_first_available_runtime()? };
|
||||
let runtime_info = unsafe { (*host).get_first_available_runtime(Some(self.version))? };
|
||||
let runtime_host = unsafe { (*runtime_info).get_runtime_host()? };
|
||||
|
||||
unsafe { (*runtime_host).start()? };
|
||||
unsafe {
|
||||
if (*runtime_info).can_be_loaded()? && !(*runtime_info).has_started()? {
|
||||
(*runtime_host).start()?;
|
||||
}
|
||||
};
|
||||
|
||||
let app_domain = unsafe { (*runtime_host).get_default_domain()? };
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use crate::primitives::{
|
||||
Class, ICLRRuntimeInfo, IEnumUnknown, IUnknown, IUnknownVtbl, Interface, GUID, HRESULT,
|
||||
Class, ICLRRuntimeInfo, IEnumUnknown, IUnknown, IUnknownVtbl, Interface, RuntimeVersion, GUID,
|
||||
HRESULT,
|
||||
};
|
||||
use std::{ffi::c_void, ops::Deref, ptr};
|
||||
use std::{collections::HashMap, ffi::c_void, ops::Deref, ptr};
|
||||
|
||||
#[repr(C)]
|
||||
pub struct ICLRMetaHostVtbl {
|
||||
@@ -53,22 +54,32 @@ impl ICLRMetaHost {
|
||||
return Ok(ppv);
|
||||
}
|
||||
|
||||
pub fn get_first_available_runtime(&self) -> Result<*mut ICLRRuntimeInfo, String> {
|
||||
pub fn get_first_available_runtime(
|
||||
&self,
|
||||
prefer_version: Option<RuntimeVersion>,
|
||||
) -> Result<*mut ICLRRuntimeInfo, String> {
|
||||
let runtimes = self.get_installed_runtimes()?;
|
||||
|
||||
if runtimes.len() > 0 {
|
||||
Ok(runtimes[0])
|
||||
} else {
|
||||
Err("Could not find any runtimes".into())
|
||||
if let Some(prefer_version) = prefer_version {
|
||||
if let Some(runtime) = runtimes.get(&prefer_version) {
|
||||
return Ok(*runtime);
|
||||
}
|
||||
}
|
||||
|
||||
for runtime in runtimes.values() {
|
||||
return Ok(*runtime);
|
||||
}
|
||||
|
||||
Err("Could not find any runtimes".into())
|
||||
}
|
||||
|
||||
pub fn get_runtime(&self, version: *mut u16) -> Result<*mut ICLRRuntimeInfo, String> {
|
||||
pub fn get_runtime(&self, version: RuntimeVersion) -> Result<*mut ICLRRuntimeInfo, String> {
|
||||
let version_ptr = version.clone().to_bstr();
|
||||
let mut ppv: *mut ICLRRuntimeInfo = ptr::null_mut();
|
||||
|
||||
let hr = unsafe {
|
||||
(*self).GetRuntime(
|
||||
version,
|
||||
version_ptr.into_raw() as *mut _,
|
||||
&ICLRRuntimeInfo::IID,
|
||||
&mut ppv as *mut *mut _ as *mut *mut c_void,
|
||||
)
|
||||
@@ -76,17 +87,22 @@ impl ICLRMetaHost {
|
||||
|
||||
return match hr.is_ok() {
|
||||
true => Ok(ppv),
|
||||
false => Err(format!("{:?}", hr)),
|
||||
false => Err(format!(
|
||||
"Could not find a runtime for version `{}`: {:?}",
|
||||
version, hr
|
||||
)),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn get_installed_runtimes(&self) -> Result<Vec<*mut ICLRRuntimeInfo>, String> {
|
||||
pub fn get_installed_runtimes(
|
||||
&self,
|
||||
) -> Result<HashMap<RuntimeVersion, *mut ICLRRuntimeInfo>, String> {
|
||||
let mut ieu_ptr: *mut IEnumUnknown = ptr::null_mut();
|
||||
|
||||
let hr = unsafe { (*self).EnumerateInstalledRuntimes(&mut ieu_ptr) };
|
||||
|
||||
if hr.is_err() {
|
||||
return Err(format!("{:?}", hr));
|
||||
return Err(format!("Could not enumerate installed runtimes: {:?}", hr));
|
||||
}
|
||||
|
||||
if ieu_ptr.is_null() {
|
||||
@@ -94,6 +110,7 @@ impl ICLRMetaHost {
|
||||
}
|
||||
|
||||
let mut hmri: Vec<*mut ICLRRuntimeInfo> = vec![];
|
||||
let mut hmri: HashMap<RuntimeVersion, *mut ICLRRuntimeInfo> = HashMap::new();
|
||||
|
||||
loop {
|
||||
let mut iu_ptr: *mut IUnknown = ptr::null_mut();
|
||||
@@ -118,7 +135,9 @@ impl ICLRMetaHost {
|
||||
break;
|
||||
}
|
||||
|
||||
hmri.push(ri_ptr);
|
||||
let version = unsafe { (*ri_ptr).get_version()? };
|
||||
|
||||
hmri.insert(version, ri_ptr);
|
||||
}
|
||||
|
||||
Ok(hmri)
|
||||
|
||||
@@ -1,7 +1,48 @@
|
||||
use crate::primitives::{
|
||||
Class, ICorRuntimeHost, IUnknown, IUnknownVtbl, Interface, BOOL, GUID, HANDLE, HRESULT,
|
||||
};
|
||||
use std::{ffi::c_void, ops::Deref, ptr};
|
||||
use std::{ffi::c_void, fmt::Display, ops::Deref, ptr};
|
||||
use windows::core::{BSTR, PWSTR};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum RuntimeVersion {
|
||||
V2,
|
||||
V3,
|
||||
V4,
|
||||
UNKNOWN,
|
||||
}
|
||||
|
||||
impl RuntimeVersion {
|
||||
pub fn to_str(&self) -> &str {
|
||||
match self {
|
||||
RuntimeVersion::V2 => "v2.0.50727",
|
||||
RuntimeVersion::V3 => "v3.0",
|
||||
RuntimeVersion::V4 => "v4.0.30319",
|
||||
RuntimeVersion::UNKNOWN => "UNKNOWN",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_bstr(&self) -> BSTR {
|
||||
BSTR::from(self.to_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for RuntimeVersion {
|
||||
fn from(version: String) -> Self {
|
||||
match version.as_str() {
|
||||
"v2.0.50727" => RuntimeVersion::V2,
|
||||
"v3.0" => RuntimeVersion::V3,
|
||||
"v4.0.30319" => RuntimeVersion::V4,
|
||||
_ => RuntimeVersion::UNKNOWN,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for RuntimeVersion {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.to_str())
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct ICLRRuntimeInfo {
|
||||
@@ -93,8 +134,49 @@ impl ICLRRuntimeInfo {
|
||||
return Ok(ppv);
|
||||
}
|
||||
|
||||
pub fn has_started() -> Result<bool, String> {
|
||||
todo!()
|
||||
pub fn get_version(&self) -> Result<RuntimeVersion, String> {
|
||||
let mut dummy = ptr::null_mut();
|
||||
let mut length = 0;
|
||||
|
||||
let _ = unsafe { (*self).GetVersionString(dummy, &mut length) };
|
||||
|
||||
let mut buffer: Vec<u16> = Vec::with_capacity(length as usize);
|
||||
let mut version = PWSTR(buffer.as_mut_ptr());
|
||||
|
||||
let hr = unsafe { (*self).GetVersionString(version.as_ptr(), &mut length) };
|
||||
|
||||
if hr.is_err() {
|
||||
return Err(format!("Failed while running `GetVersionString`: {:?}", hr));
|
||||
}
|
||||
|
||||
Ok(RuntimeVersion::from(unsafe {
|
||||
version.to_string().unwrap_or_default()
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn can_be_loaded(&self) -> Result<bool, String> {
|
||||
let mut loadable = BOOL(0);
|
||||
|
||||
let hr = unsafe { (*self).IsLoadable(&mut loadable) };
|
||||
|
||||
if hr.is_err() {
|
||||
return Err(format!("Failed while running `IsLoadable`: {:?}", hr));
|
||||
}
|
||||
|
||||
Ok(loadable.0 > 0)
|
||||
}
|
||||
|
||||
pub fn has_started(&self) -> Result<bool, String> {
|
||||
let mut startup_flags = 0;
|
||||
let mut started = BOOL(0);
|
||||
|
||||
let hr = unsafe { (*self).IsStarted(&mut started, &mut startup_flags) };
|
||||
|
||||
if hr.is_err() {
|
||||
return Err(format!("Failed while running `IsStarted`: {:?}", hr));
|
||||
}
|
||||
|
||||
Ok(started.0 > 0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
||||
Reference in New Issue
Block a user