fix getting version strings -- my bad >< -- and allow specifying version, using V4 as default

This commit is contained in:
Kadir Yamamoto
2023-03-31 11:36:46 +09:00
parent 90717dc74a
commit 7faea31e83
7 changed files with 175 additions and 25 deletions
+1 -1
View File
@@ -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]
+1 -1
View File
@@ -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")? };
+1 -1
View File
@@ -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()?;
+1 -1
View File
@@ -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
View File
@@ -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()? };
+32 -13
View File
@@ -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)
+85 -3
View File
@@ -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]