Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to use ICLRMetaHost #1998

Closed
sk3w opened this issue Sep 1, 2022 · 3 comments
Closed

How to use ICLRMetaHost #1998

sk3w opened this issue Sep 1, 2022 · 3 comments
Labels
question Further information is requested

Comments

@sk3w
Copy link

sk3w commented Sep 1, 2022

Which crate is this about?

windows

Crate version

0.39.0

Summary

Hi, I am trying to declare and use the ICLRMetaHost interface from mscoree.dll with the windows::core::interface macro per the FAQ (https://github.com/microsoft/windows-rs/blob/master/docs/FAQ.md#how-do-i-declare-a-new-com-interface). As far as I can tell I have a valid metahost value, but when I attempt to call the interface methods, I am getting memory access violations (call rax goes to a very high very wrong address). I'm not sure if this is a bug with the vtable generation or (more likely) if I'm just doing something wrong. The included example code is calling the GetRuntime method but I have tried some of the other methods with similar results. Thanks!

Toolchain version/configuration

Default host: x86_64-pc-windows-msvc
rustup home:  C:\Users\John\.rustup

installed toolchains
--------------------

stable-x86_64-pc-windows-msvc
nightly-x86_64-pc-windows-msvc

active toolchain
----------------

stable-x86_64-pc-windows-msvc (default)
rustc 1.62.0 (a8314ef7d 2022-06-27)

Reproducible example

#![allow(non_snake_case, non_upper_case_globals)]
#![cfg(windows)]

use core::ffi::c_void;
use windows::{
    core::{interface, IUnknown, IUnknownVtbl, GUID, HRESULT, HSTRING, PCWSTR, PWSTR, Interface},
    Win32::Foundation::HANDLE,
};

const CLSID_CLRMetaHost: GUID = GUID::from_u128(0x9280188d_0e8e_4867_b30c_7fa83884e8de);
const IID_ICLRMetaHost: GUID = GUID::from_u128(0xd332db9e_b9b3_4125_8207_a14884f53216);

// for libloading
type CLRCreateInstanceFn =
    unsafe extern "system" fn(*const GUID, *const GUID, *mut *mut c_void) -> HRESULT;

#[interface("D332DB9E-B9B3-4125-8207-A14884F53216")]
unsafe trait ICLRMetaHost: IUnknown {
    unsafe fn GetRuntime(
        &self,
        pwzVersion: PCWSTR,
        riid: *const GUID,
        ppRuntime: *mut *mut c_void,
    ) -> HRESULT;
    unsafe fn GetVersionFromFile(
        &self,
        pwzFilePath: PCWSTR,
        pwzBuffer: PCWSTR,
        pcchBuffer: u32,
    ) -> HRESULT;
    unsafe fn EnumerateInstalledRuntimes(&self, ppEnumerator: *mut *mut c_void) -> HRESULT;
    unsafe fn EnumerateLoadedRuntimes(
        &self,
        hndProcess: HANDLE,
        ppEnumerator: *mut *mut c_void,
    ) -> HRESULT;
    unsafe fn RequestRuntimeLoadedNotification(&self, pCallbackFunction: *const c_void) -> HRESULT;
    unsafe fn QueryLegacyV2RuntimeBinding(
        &self,
        riid: *const GUID,
        ppUnk: *mut *mut c_void,
    ) -> HRESULT;
    unsafe fn ExitProcess(&self, iExitCode: i32) -> HRESULT;
}

#[interface("BD39D1D2-BA2F-486a-89B0-B4B0CB466891")]
unsafe trait ICLRRuntimeInfo: IUnknown {
    unsafe fn GetVersionString(&self, pwzBuffer: PWSTR, pcchBuffer: *mut u32) -> HRESULT;
}

fn main() {
    let mut p_metahost: *mut ICLRMetaHost = core::ptr::null_mut();
    unsafe {
        let lib = libloading::Library::new("mscoree.dll").unwrap();
        let CLRCreateInstance: libloading::Symbol<CLRCreateInstanceFn> =
            lib.get(b"CLRCreateInstance").unwrap();
        let hresult = CLRCreateInstance(
            &CLSID_CLRMetaHost,
            &IID_ICLRMetaHost,
            &mut p_metahost as *mut *mut _ as *mut *mut c_void,
        );
        println!("HRESULT: {:?} {:?}", &hresult, hresult.message());
        dbg!(p_metahost);
        let metahost = p_metahost.as_ref().unwrap();
        dbg!(metahost);

        let hstring = core::mem::ManuallyDrop::new(HSTRING::from("v4.0.30319"));
        let pwzVersion = PCWSTR(hstring.as_wide().as_ptr());
        dbg!(pwzVersion.to_string().unwrap());


        let mut p_runtime_info: *mut ICLRRuntimeInfo = core::ptr::null_mut();
        metahost
            .GetRuntime(
                pwzVersion,
                &IID_ICLRMetaHost,
                &mut p_runtime_info as *mut *mut _ as *mut *mut c_void,
            )
            .unwrap();
        dbg!(p_runtime_info);
    }
    println!("DONE");
}

Crate manifest

[dependencies]
libloading = "0.7.3"
windows = { version = "0.39.0", features = [
    "implement",
    "interface",
    "Win32_Foundation",
    "Win32_System_Com",
] }

Expected behavior

I expected to receive a pointer to a CLRRuntimeInfo object

Actual behavior

stdout

HRESULT: HRESULT(0x00000000) The operation completed successfully.
[src\bin\test-clr.rs:63] p_metahost = 0x000001483a885eb0
[src\bin\test-clr.rs:65] metahost = ICLRMetaHost(
    IUnknown(
        0x00007ffd75811a88,
    ),
)
[src\bin\test-clr.rs:69] pwzVersion.to_string().unwrap() = "v4.0.30319"
error: process didn't exit successfully: `target\debug\test-clr.exe` (exit code: 0xc0000005, STATUS_ACCESS_VIOLATION)

windbg

windbg_01

windbg_02

Additional comments

No response

@sk3w sk3w added the bug Something isn't working label Sep 1, 2022
@kennykerr kennykerr added question Further information is requested and removed bug Something isn't working labels Sep 2, 2022
@kennykerr
Copy link
Collaborator

Try this:

[dependencies.windows]
version = "0.39.0"
features = [
    "interface",
    "Win32_Foundation",
    "Win32_System_Com",
    "Win32_System_LibraryLoader"
]
#![allow(non_snake_case, non_upper_case_globals)]

use std::ffi::*;
use std::mem::*;
use windows::{core::*, Win32::System::LibraryLoader::*};

const CLSID_CLRMetaHost: GUID = GUID::from_u128(0x9280188d_0e8e_4867_b30c_7fa83884e8de);

type CLRCreateInstance = extern "system" fn(*const GUID, *const GUID, *mut *mut c_void) -> HRESULT;

#[interface("D332DB9E-B9B3-4125-8207-A14884F53216")]
unsafe trait ICLRMetaHost: IUnknown {
    unsafe fn GetRuntime(
        &self,
        pwzVersion: PCWSTR,
        riid: *const GUID,
        ppRuntime: *mut *mut c_void,
    ) -> HRESULT;
}

#[interface("BD39D1D2-BA2F-486a-89B0-B4B0CB466891")]
unsafe trait ICLRRuntimeInfo: IUnknown {
    unsafe fn GetVersionString(&self, pwzBuffer: PWSTR, pcchBuffer: *mut u32) -> HRESULT;
}

fn main() -> Result<()> {
    unsafe {
        let mscoree = LoadLibraryA(s!("mscoree"))?;

        let create: CLRCreateInstance =
            std::mem::transmute(GetProcAddress(mscoree, s!("CLRCreateInstance")));

        let mut host = MaybeUninit::zeroed();

        let host: ICLRMetaHost =
            create(&CLSID_CLRMetaHost, &ICLRMetaHost::IID, host.as_mut_ptr()).from_abi(host)?;

        let mut info = MaybeUninit::zeroed();

        let info: ICLRRuntimeInfo = host
            .GetRuntime(
                w!("v4.0.30319").into(),
                &ICLRRuntimeInfo::IID,
                info.as_mut_ptr(),
            )
            .from_abi(info)?;

        let mut version = [0; 20];
        let mut len = version.len() as u32;

        info.GetVersionString(PWSTR(version.as_mut_ptr()), &mut len)
            .ok()?;

        println!(
            "Version: {}",
            String::from_utf16(&version).expect("Invalid string")
        );

        println!("Rust is now controlling the CLR 😜");
        Ok(())
    }
}

@kennykerr kennykerr changed the title Bug: ICLRMetaHost com method access violations How to use ICLRMetaHost Sep 2, 2022
@sk3w
Copy link
Author

sk3w commented Sep 2, 2022

Wow, thanks! That example is very helpful (TIL about the Abi trait and those string macros). I'm still unpacking it a bit, but it looks like my raw pointer cast for metahost was bad? And I actually had a pointer to the inner IUnknown?

@kennykerr
Copy link
Collaborator

Yep, you need a pretty good understanding of both COM and how COM interface pointers are represented in Rust. We're working on making it as safe and simple as possible so if you find yourself doing a lot of contrived casting there's a good chance you're doing something wrong.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants