Skip to content

Commit

Permalink
Add marshaling support for COM implementations (#3531)
Browse files Browse the repository at this point in the history
  • Loading branch information
kennykerr authored Mar 7, 2025
1 parent c66decb commit 4523c9d
Show file tree
Hide file tree
Showing 51 changed files with 970 additions and 97 deletions.
10 changes: 6 additions & 4 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,8 @@ jobs:
run: cargo test -p test_linux --target ${{ matrix.target }} ${{ matrix.etc }}
- name: Test test_literals
run: cargo test -p test_literals --target ${{ matrix.target }} ${{ matrix.etc }}
- name: Test test_marshal
run: cargo test -p test_marshal --target ${{ matrix.target }} ${{ matrix.etc }}
- name: Test test_match
run: cargo test -p test_match --target ${{ matrix.target }} ${{ matrix.etc }}
- name: Test test_metadata
Expand All @@ -257,10 +259,10 @@ jobs:
run: cargo test -p test_numerics --target ${{ matrix.target }} ${{ matrix.etc }}
- name: Test test_overloads
run: cargo test -p test_overloads --target ${{ matrix.target }} ${{ matrix.etc }}
- name: Test test_overloads_client
run: cargo test -p test_overloads_client --target ${{ matrix.target }} ${{ matrix.etc }}
- name: Clean
run: cargo clean
- name: Test test_overloads_client
run: cargo test -p test_overloads_client --target ${{ matrix.target }} ${{ matrix.etc }}
- name: Test test_query_signature
run: cargo test -p test_query_signature --target ${{ matrix.target }} ${{ matrix.etc }}
- name: Test test_readme
Expand Down Expand Up @@ -359,10 +361,10 @@ jobs:
run: cargo test -p windows-numerics --target ${{ matrix.target }} ${{ matrix.etc }}
- name: Test windows-registry
run: cargo test -p windows-registry --target ${{ matrix.target }} ${{ matrix.etc }}
- name: Test windows-result
run: cargo test -p windows-result --target ${{ matrix.target }} ${{ matrix.etc }}
- name: Clean
run: cargo clean
- name: Test windows-result
run: cargo test -p windows-result --target ${{ matrix.target }} ${{ matrix.etc }}
- name: Test windows-strings
run: cargo test -p windows-strings --target ${{ matrix.target }} ${{ matrix.etc }}
- name: Test windows-sys
Expand Down
3 changes: 3 additions & 0 deletions crates/libs/bindgen/src/types/delegate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,9 @@ impl Delegate {
*iid == <windows_core::IUnknown as windows_core::Interface>::IID ||
*iid == <windows_core::imp::IAgileObject as windows_core::Interface>::IID {
&mut (*this).vtable as *mut _ as _
} else if *iid == <windows_core::imp::IMarshal as windows_core::Interface>::IID {
(*this).count.add_ref();
return windows_core::imp::marshaler(core::mem::transmute(&mut (*this).vtable as *mut _ as *mut core::ffi::c_void), interface);
} else {
core::ptr::null_mut()
};
Expand Down
1 change: 1 addition & 0 deletions crates/libs/core/src/imp/com_bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,4 +260,5 @@ impl windows_core::RuntimeName for IWeakReferenceSource {}
pub const JSCRIPT_E_CANTEXECUTE: windows_core::HRESULT = windows_core::HRESULT(0x89020001_u32 as _);
pub const REGDB_E_CLASSNOTREG: windows_core::HRESULT = windows_core::HRESULT(0x80040154_u32 as _);
pub const RPC_E_DISCONNECTED: windows_core::HRESULT = windows_core::HRESULT(0x80010108_u32 as _);
pub const S_OK: windows_core::HRESULT = windows_core::HRESULT(0x0_u32 as _);
pub const TYPE_E_TYPEMISMATCH: windows_core::HRESULT = windows_core::HRESULT(0x80028CA0_u32 as _);
4 changes: 2 additions & 2 deletions crates/libs/core/src/imp/factory_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ impl<C: crate::RuntimeName, I: Interface> FactoryCache<C, I> {
}

// Otherwise, we load the factory the usual way.
let factory = factory::<C, I>()?;
let factory = load_factory::<C, I>()?;

// If the factory is agile, we can safely cache it.
if factory.cast::<IAgileObject>().is_ok() {
Expand Down Expand Up @@ -70,7 +70,7 @@ unsafe impl<C, I> Sync for FactoryCache<C, I> {}

/// Attempts to load the factory object for the given WinRT class.
/// This can be used to access COM interfaces implemented on a Windows Runtime class factory.
pub fn factory<C: crate::RuntimeName, I: Interface>() -> crate::Result<I> {
pub fn load_factory<C: crate::RuntimeName, I: Interface>() -> crate::Result<I> {
let mut factory: Option<I> = None;
let name = crate::HSTRING::from(C::NAME);

Expand Down
268 changes: 268 additions & 0 deletions crates/libs/core/src/imp/marshaler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,268 @@
use super::*;
use crate::{IUnknown, IUnknown_Vtbl, Interface, GUID, HRESULT};
use core::ffi::c_void;
use core::mem::{transmute, transmute_copy};
use core::ptr::null_mut;

windows_link::link!("ole32.dll" "system" fn CoCreateFreeThreadedMarshaler(punkouter: *mut c_void, ppunkmarshal: *mut *mut c_void) -> HRESULT);

pub unsafe fn marshaler(outer: IUnknown, result: *mut *mut c_void) -> HRESULT {
unsafe {
let mut marshaler_raw = null_mut();
_ = CoCreateFreeThreadedMarshaler(null_mut(), &mut marshaler_raw);
assert!(!marshaler_raw.is_null(), "allocation failed");
let marshaler: IUnknown = transmute(marshaler_raw);

_ = (marshaler.vtable().QueryInterface)(
transmute_copy(&marshaler),
&IMarshal::IID,
&mut marshaler_raw,
);

debug_assert!(!marshaler_raw.is_null());
let marshaler: IMarshal = transmute(marshaler_raw);

let marshaler = Marshaler {
vtable: &Marshaler::VTABLE,
outer,
marshaler,
count: RefCount::new(1),
};

debug_assert!(!result.is_null());
*result = transmute::<Box<_>, *mut c_void>(Box::new(marshaler));
S_OK
}
}

#[repr(C)]
struct Marshaler {
vtable: *const IMarshal_Vtbl,
outer: IUnknown,
marshaler: IMarshal,
count: RefCount,
}

impl Marshaler {
const VTABLE: IMarshal_Vtbl = IMarshal_Vtbl {
base__: IUnknown_Vtbl {
QueryInterface: Self::QueryInterface,
AddRef: Self::AddRef,
Release: Self::Release,
},
GetUnmarshalClass: Self::GetUnmarshalClass,
GetMarshalSizeMax: Self::GetMarshalSizeMax,
MarshalInterface: Self::MarshalInterface,
UnmarshalInterface: Self::UnmarshalInterface,
ReleaseMarshalData: Self::ReleaseMarshalData,
DisconnectObject: Self::DisconnectObject,
};

unsafe extern "system" fn QueryInterface(
this: *mut c_void,
iid: *const GUID,
interface: *mut *mut c_void,
) -> HRESULT {
unsafe {
let this = this as *mut *mut c_void as *mut Self;

if iid.is_null() || interface.is_null() {
return E_POINTER;
}

if *iid == IMarshal::IID {
*interface = &mut (*this).vtable as *mut _ as _;
(*this).count.add_ref();
return S_OK;
}

((*this).outer.vtable().QueryInterface)(transmute_copy(&(*this).outer), iid, interface)
}
}

unsafe extern "system" fn AddRef(this: *mut c_void) -> u32 {
unsafe {
let this = this as *mut *mut c_void as *mut Self;
(*this).count.add_ref()
}
}

unsafe extern "system" fn Release(this: *mut c_void) -> u32 {
unsafe {
let this = this as *mut *mut c_void as *mut Self;
let remaining = (*this).count.release();

if remaining == 0 {
let _ = Box::from_raw(this);
}

remaining
}
}

unsafe extern "system" fn GetUnmarshalClass(
this: *mut c_void,
riid: *const GUID,
pv: *const c_void,
dwdestcontext: u32,
pvdestcontext: *const c_void,
mshlflags: u32,
pcid: *mut GUID,
) -> HRESULT {
unsafe {
let this = this as *mut *mut c_void as *mut Self;

((*this).marshaler.vtable().GetUnmarshalClass)(
transmute_copy(&(*this).marshaler),
riid,
pv,
dwdestcontext,
pvdestcontext,
mshlflags,
pcid,
)
}
}

unsafe extern "system" fn GetMarshalSizeMax(
this: *mut c_void,
riid: *const GUID,
pv: *const c_void,
dwdestcontext: u32,
pvdestcontext: *const c_void,
mshlflags: u32,
psize: *mut u32,
) -> HRESULT {
unsafe {
let this = this as *mut *mut c_void as *mut Self;

((*this).marshaler.vtable().GetMarshalSizeMax)(
transmute_copy(&(*this).marshaler),
riid,
pv,
dwdestcontext,
pvdestcontext,
mshlflags,
psize,
)
}
}

unsafe extern "system" fn MarshalInterface(
this: *mut c_void,
pstm: *mut c_void,
riid: *const GUID,
pv: *const c_void,
dwdestcontext: u32,
pvdestcontext: *const c_void,
mshlflags: u32,
) -> HRESULT {
unsafe {
let this = this as *mut *mut c_void as *mut Self;

((*this).marshaler.vtable().MarshalInterface)(
transmute_copy(&(*this).marshaler),
pstm,
riid,
pv,
dwdestcontext,
pvdestcontext,
mshlflags,
)
}
}

unsafe extern "system" fn UnmarshalInterface(
this: *mut c_void,
pstm: *mut c_void,
riid: *const GUID,
ppv: *mut *mut c_void,
) -> HRESULT {
unsafe {
let this = this as *mut *mut c_void as *mut Self;

((*this).marshaler.vtable().UnmarshalInterface)(
transmute_copy(&(*this).marshaler),
pstm,
riid,
ppv,
)
}
}

unsafe extern "system" fn ReleaseMarshalData(this: *mut c_void, pstm: *mut c_void) -> HRESULT {
unsafe {
let this = this as *mut *mut c_void as *mut Self;

((*this).marshaler.vtable().ReleaseMarshalData)(
transmute_copy(&(*this).marshaler),
pstm,
)
}
}

unsafe extern "system" fn DisconnectObject(this: *mut c_void, dwreserved: u32) -> HRESULT {
unsafe {
let this = this as *mut *mut c_void as *mut Self;

((*this).marshaler.vtable().DisconnectObject)(
transmute_copy(&(*this).marshaler),
dwreserved,
)
}
}
}

#[repr(transparent)]
#[derive(Clone)]
pub struct IMarshal(IUnknown);

unsafe impl Interface for IMarshal {
type Vtable = IMarshal_Vtbl;
const IID: GUID = GUID::from_u128(0x00000003_0000_0000_c000_000000000046);
}

#[repr(C)]
pub struct IMarshal_Vtbl {
base__: IUnknown_Vtbl,

GetUnmarshalClass: unsafe extern "system" fn(
*mut c_void,
*const GUID,
*const c_void,
u32,
*const c_void,
u32,
*mut GUID,
) -> HRESULT,

GetMarshalSizeMax: unsafe extern "system" fn(
*mut c_void,
*const GUID,
*const c_void,
u32,
*const c_void,
u32,
*mut u32,
) -> HRESULT,

MarshalInterface: unsafe extern "system" fn(
*mut c_void,
*mut c_void,
*const GUID,
*const c_void,
u32,
*const c_void,
u32,
) -> HRESULT,

UnmarshalInterface: unsafe extern "system" fn(
*mut c_void,
*mut c_void,
*const GUID,
*mut *mut c_void,
) -> HRESULT,

ReleaseMarshalData: unsafe extern "system" fn(*mut c_void, *mut c_void) -> HRESULT,
DisconnectObject: unsafe extern "system" fn(*mut c_void, u32) -> HRESULT,
}
Loading

0 comments on commit 4523c9d

Please sign in to comment.