clroxide/primitives/
iclrruntimeinfo.rs

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}