1use crate::primitives::{
2 Class, ICorRuntimeHost, IUnknown, IUnknownVtbl, Interface, BOOL, GUID, HANDLE, HRESULT,
3};
4use std::{ffi::c_void, fmt::Display, ops::Deref, ptr};
5use windows::core::{BSTR, PWSTR};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
8pub enum RuntimeVersion {
9 V2,
10 V3,
11 V4,
12 UNKNOWN,
13}
14
15impl RuntimeVersion {
16 pub fn to_str(&self) -> &str {
17 match self {
18 RuntimeVersion::V2 => "v2.0.50727",
19 RuntimeVersion::V3 => "v3.0",
20 RuntimeVersion::V4 => "v4.0.30319",
21 RuntimeVersion::UNKNOWN => "UNKNOWN",
22 }
23 }
24
25 pub fn to_bstr(&self) -> BSTR {
26 BSTR::from(self.to_str())
27 }
28}
29
30impl From<String> for RuntimeVersion {
31 fn from(version: String) -> Self {
32 match version.as_str() {
33 "v2.0.50727" => RuntimeVersion::V2,
34 "v3.0" => RuntimeVersion::V3,
35 "v4.0.30319" => RuntimeVersion::V4,
36 _ => RuntimeVersion::UNKNOWN,
37 }
38 }
39}
40
41impl Display for RuntimeVersion {
42 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43 write!(f, "{}", self.to_str())
44 }
45}
46
47#[repr(C)]
48pub struct ICLRRuntimeInfo {
49 pub vtable: *const ICLRRuntimeInfoVtbl,
50}
51
52#[repr(C)]
53pub struct ICLRRuntimeInfoVtbl {
54 pub parent: IUnknownVtbl,
55 pub GetVersionString: unsafe extern "system" fn(
56 this: *mut ICLRRuntimeInfo,
57 pwzBuffer: *mut u16,
58 pcchBuffer: *mut u32,
59 ) -> HRESULT,
60 pub GetRuntimeDirectory: unsafe extern "system" fn(
61 this: *mut ICLRRuntimeInfo,
62 pwzBuffer: *mut u16,
63 pcchBuffer: *mut u32,
64 ) -> HRESULT,
65 pub IsLoaded: unsafe extern "system" fn(
66 this: *mut ICLRRuntimeInfo,
67 hndProcess: HANDLE,
68 pbLoaded: *mut BOOL,
69 ) -> HRESULT,
70 pub LoadErrorString: unsafe extern "system" fn(
71 this: *mut ICLRRuntimeInfo,
72 iResourceID: u32,
73 pwzBuffer: *mut u16,
74 pcchBuffer: *mut u32,
75 iLocaleID: u32,
76 ) -> HRESULT,
77 pub LoadLibrary: unsafe extern "system" fn(
78 this: *mut ICLRRuntimeInfo,
79 pwzDllName: *const u16,
80 ppProc: *mut *mut c_void,
81 ) -> HRESULT,
82 pub GetProcAddress: unsafe extern "system" fn(
83 this: *mut ICLRRuntimeInfo,
84 pszProcName: *const i8,
85 ppProc: *mut *mut c_void,
86 ) -> HRESULT,
87 pub GetInterface: unsafe extern "system" fn(
88 this: *mut ICLRRuntimeInfo,
89 rclsid: *const GUID,
90 riid: *const GUID,
91 ppUnk: *mut *mut c_void,
92 ) -> HRESULT,
93 pub IsLoadable:
94 unsafe extern "system" fn(this: *mut ICLRRuntimeInfo, pbLoadable: *mut BOOL) -> HRESULT,
95 pub SetDefaultStartupFlags: unsafe extern "system" fn(
96 this: *mut ICLRRuntimeInfo,
97 dwStartupFlags: u32,
98 pwzHostConfigFile: *const u16,
99 ) -> HRESULT,
100 pub GetDefaultStartupFlags: unsafe extern "system" fn(
101 this: *mut ICLRRuntimeInfo,
102 pdwStartupFlags: *mut u32,
103 pwzHostConfigFile: *mut u16,
104 pcchHostConfigFile: *mut u32,
105 ) -> HRESULT,
106 pub BindAsLegacyV2Runtime: unsafe extern "system" fn(this: *mut ICLRRuntimeInfo) -> HRESULT,
107 pub IsStarted: unsafe extern "system" fn(
108 this: *mut ICLRRuntimeInfo,
109 pbStarted: *mut BOOL,
110 pdwStartupFlags: *mut u32,
111 ) -> HRESULT,
112}
113
114impl ICLRRuntimeInfo {
115 pub fn get_runtime_host(&self) -> Result<*mut ICorRuntimeHost, String> {
116 let mut ppv: *mut ICorRuntimeHost = ptr::null_mut();
117
118 let hr = unsafe {
119 (*self).GetInterface(
120 &ICorRuntimeHost::CLSID,
121 &ICorRuntimeHost::IID,
122 &mut ppv as *mut *mut _ as *mut *mut c_void,
123 )
124 };
125
126 if hr.is_err() {
127 return Err(format!("Could not retrieve ICorRuntimeHost: {:?}", hr));
128 }
129
130 if ppv.is_null() {
131 return Err("Could not retrieve ICorRuntimeHost".into());
132 }
133
134 return Ok(ppv);
135 }
136
137 pub fn get_version(&self) -> Result<RuntimeVersion, String> {
138 let dummy = ptr::null_mut();
139 let mut length = 0;
140
141 let _ = unsafe { (*self).GetVersionString(dummy, &mut length) };
142
143 let mut buffer: Vec<u16> = Vec::with_capacity(length as usize);
144 let version = PWSTR(buffer.as_mut_ptr());
145
146 let hr = unsafe { (*self).GetVersionString(version.as_ptr(), &mut length) };
147
148 if hr.is_err() {
149 return Err(format!("Failed while running `GetVersionString`: {:?}", hr));
150 }
151
152 Ok(RuntimeVersion::from(unsafe {
153 version.to_string().unwrap_or_default()
154 }))
155 }
156
157 pub fn can_be_loaded(&self) -> Result<bool, String> {
158 let mut loadable = BOOL(0);
159
160 let hr = unsafe { (*self).IsLoadable(&mut loadable) };
161
162 if hr.is_err() {
163 return Err(format!("Failed while running `IsLoadable`: {:?}", hr));
164 }
165
166 Ok(loadable.0 > 0)
167 }
168
169 pub fn has_started(&self) -> Result<bool, String> {
170 let mut startup_flags = 0;
171 let mut started = BOOL(0);
172
173 let hr = unsafe { (*self).IsStarted(&mut started, &mut startup_flags) };
174
175 if hr.is_err() {
176 return Err(format!("Failed while running `IsStarted`: {:?}", hr));
177 }
178
179 Ok(started.0 > 0)
180 }
181
182 #[inline]
183 pub unsafe fn GetVersionString(&self, pwzBuffer: *mut u16, pcchBuffer: *mut u32) -> HRESULT {
184 ((*self.vtable).GetVersionString)(self as *const _ as *mut _, pwzBuffer, pcchBuffer)
185 }
186
187 #[inline]
188 pub unsafe fn GetRuntimeDirectory(&self, pwzBuffer: *mut u16, pcchBuffer: *mut u32) -> HRESULT {
189 ((*self.vtable).GetRuntimeDirectory)(self as *const _ as *mut _, pwzBuffer, pcchBuffer)
190 }
191
192 #[inline]
193 pub unsafe fn IsLoaded(&self, hndProcess: HANDLE, pbLoaded: *mut BOOL) -> HRESULT {
194 ((*self.vtable).IsLoaded)(self as *const _ as *mut _, hndProcess, pbLoaded)
195 }
196
197 #[inline]
198 pub unsafe fn LoadErrorString(
199 &self,
200 iResourceID: u32,
201 pwzBuffer: *mut u16,
202 pcchBuffer: *mut u32,
203 iLocaleID: u32,
204 ) -> HRESULT {
205 ((*self.vtable).LoadErrorString)(
206 self as *const _ as *mut _,
207 iResourceID,
208 pwzBuffer,
209 pcchBuffer,
210 iLocaleID,
211 )
212 }
213
214 #[inline]
215 pub unsafe fn LoadLibrary(&self, pwzDllName: *const u16, ppProc: *mut *mut c_void) -> HRESULT {
216 ((*self.vtable).LoadLibrary)(self as *const _ as *mut _, pwzDllName, ppProc)
217 }
218
219 #[inline]
220 pub unsafe fn GetProcAddress(
221 &self,
222 pszProcName: *const i8,
223 ppProc: *mut *mut c_void,
224 ) -> HRESULT {
225 ((*self.vtable).GetProcAddress)(self as *const _ as *mut _, pszProcName, ppProc)
226 }
227
228 #[inline]
229 pub unsafe fn GetInterface(
230 &self,
231 rclsid: *const GUID,
232 riid: *const GUID,
233 ppUnk: *mut *mut c_void,
234 ) -> HRESULT {
235 ((*self.vtable).GetInterface)(self as *const _ as *mut _, rclsid, riid, ppUnk)
236 }
237
238 #[inline]
239 pub unsafe fn IsLoadable(&self, pbLoadable: *mut BOOL) -> HRESULT {
240 ((*self.vtable).IsLoadable)(self as *const _ as *mut _, pbLoadable)
241 }
242
243 #[inline]
244 pub unsafe fn SetDefaultStartupFlags(
245 &self,
246 dwStartupFlags: u32,
247 pwzHostConfigFile: *const u16,
248 ) -> HRESULT {
249 ((*self.vtable).SetDefaultStartupFlags)(
250 self as *const _ as *mut _,
251 dwStartupFlags,
252 pwzHostConfigFile,
253 )
254 }
255
256 #[inline]
257 pub unsafe fn GetDefaultStartupFlags(
258 &self,
259 pdwStartupFlags: *mut u32,
260 pwzHostConfigFile: *mut u16,
261 pcchHostConfigFile: *mut u32,
262 ) -> HRESULT {
263 ((*self.vtable).GetDefaultStartupFlags)(
264 self as *const _ as *mut _,
265 pdwStartupFlags,
266 pwzHostConfigFile,
267 pcchHostConfigFile,
268 )
269 }
270
271 #[inline]
272 pub unsafe fn BindAsLegacyV2Runtime(&self) -> HRESULT {
273 ((*self.vtable).BindAsLegacyV2Runtime)(self as *const _ as *mut _)
274 }
275
276 #[inline]
277 pub unsafe fn IsStarted(&self, pbStarted: *mut BOOL, pdwStartupFlags: *mut u32) -> HRESULT {
278 ((*self.vtable).IsStarted)(self as *const _ as *mut _, pbStarted, pdwStartupFlags)
279 }
280}
281
282impl Interface for ICLRRuntimeInfo {
283 const IID: GUID = GUID::from_values(
284 0xBD39D1D2,
285 0xBA2F,
286 0x486a,
287 [0x89, 0xB0, 0xB4, 0xB0, 0xCB, 0x46, 0x68, 0x91],
288 );
289
290 fn vtable(&self) -> *const c_void {
291 self.vtable as *const _ as *const c_void
292 }
293}
294
295impl Deref for ICLRRuntimeInfo {
296 type Target = IUnknown;
297
298 #[inline]
299 fn deref(&self) -> &IUnknown {
300 unsafe { &*(self as *const ICLRRuntimeInfo as *const IUnknown) }
301 }
302}