Skip to content

Commit

Permalink
Adds unsafe helpers to cast from C++ or raw IUnknown pointers to …
Browse files Browse the repository at this point in the history
…Rust `IUnknown` values (#2010)
  • Loading branch information
kennykerr committed Sep 8, 2022
1 parent 2db98c7 commit 61bc313
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 1 deletion.
30 changes: 29 additions & 1 deletion crates/libs/windows/src/core/interface.rs
Expand Up @@ -33,13 +33,41 @@ pub unsafe trait Interface: Sized {
unsafe { self.query(&T::IID, &mut result as *mut _ as _).and_some(result) }
}

/// Turn this interface into a raw pointer
/// Returns the raw COM interface pointer. The resulting pointer continues to be owned by the `Interface` implementation.
#[inline(always)]
fn as_raw(&self) -> *mut core::ffi::c_void {
// SAFETY: implementors of this trait must guarantee that the implementing type has a pointer in-memory representation
unsafe { core::mem::transmute_copy(self) }
}

/// Returns the raw COM interface pointer and releases ownership. It the caller's responsibility to release the COM interface pointer.
fn into_raw(self) -> *mut core::ffi::c_void {
// SAFETY: implementors of this trait must guarantee that the implementing type has a pointer in-memory representation
let raw = self.as_raw();
std::mem::forget(self);
raw
}

/// Creates an `Interface` by taking ownership of the `raw` COM interface pointer.
///
/// # Safety
///
/// The `raw` pointer must be owned by the caller and represent a valid COM interface pointer. In other words,
/// it must point to a vtable beginning with the `IUnknown` function pointers and match the vtable of `Interface`.
unsafe fn from_raw(raw: *mut core::ffi::c_void) -> Self {
std::mem::transmute_copy(&raw)
}

/// Creates an `Interface` that is valid so long as the `raw` COM interface pointer is valid.
///
/// # Safety
///
/// The `raw` pointer must be a valid COM interface pointer. In other words, it must point to a vtable
/// beginning with the `IUnknown` function pointers and match the vtable of `Interface`.
unsafe fn from_raw_borrowed<'a>(raw: &'a *mut core::ffi::c_void) -> &'a Self {
std::mem::transmute_copy(&raw)
}

/// Attempts to create a [`Weak`] reference to this object.
fn downgrade(&self) -> Result<Weak<Self>> {
self.cast::<IWeakReferenceSource>().and_then(|source| Weak::downgrade(&source))
Expand Down
2 changes: 2 additions & 0 deletions crates/tests/core/Cargo.toml
Expand Up @@ -7,6 +7,8 @@ edition = "2018"
[dependencies.windows]
path = "../../libs/windows"
features = [
"implement",
"interface",
"Win32_Foundation",
"Win32_System_WinRT",
]
Expand Down
79 changes: 79 additions & 0 deletions crates/tests/core/tests/unknown.rs
@@ -0,0 +1,79 @@
#![allow(non_snake_case)]

use windows::core::*;

#[interface("f7ea748b-8121-41c1-aaee-406ba6f148a9")]
unsafe trait ITest: IUnknown {
unsafe fn Test(&self) -> u32;
}

#[implement(ITest)]
struct Test {
drop: *mut u32,
}

impl ITest_Impl for Test {
unsafe fn Test(&self) -> u32 {
*self.drop
}
}

impl Drop for Test {
fn drop(&mut self) {
unsafe {
*self.drop += 1;
}
}
}

#[test]
fn test_unknown() {
unsafe {
let mut dropped = 0;
let test: ITest = Test { drop: &mut dropped }.into();

{
let raw_borrowed: *mut std::ffi::c_void = test.as_raw();
let unknown_borrowed: &IUnknown = IUnknown::from_raw_borrowed(&raw_borrowed);
assert_eq!(unknown_borrowed.as_raw(), test.as_raw());
let test_query: ITest = unknown_borrowed.cast().unwrap();
assert_eq!(test_query.Test(), 0);
}
{
let raw_owned: *mut std::ffi::c_void = test.clone().into_raw();
let unknown_owned: IUnknown = IUnknown::from_raw(raw_owned);
assert_eq!(unknown_owned.as_raw(), test.as_raw());
let test_query: ITest = unknown_owned.cast().unwrap();
assert_eq!(test_query.Test(), 0);
}

assert_eq!(test.Test(), 0);
drop(test);
assert_eq!(dropped, 1);
}
}

#[test]
fn test_pointer_conversion_functions() {
unsafe {
let mut dropped = 0;
let test: ITest = Test { drop: &mut dropped }.into();

{
let raw_borrowed: *mut std::ffi::c_void = test.as_raw();
let test_borrowed: &ITest = ITest::from_raw_borrowed(&raw_borrowed);
assert_eq!(test_borrowed.as_raw(), test.as_raw());
assert_eq!(test_borrowed.Test(), 0);
}
{
let raw_owned: *mut std::ffi::c_void = test.clone().into_raw();
let unknown_owned: ITest = ITest::from_raw(raw_owned);
assert_eq!(unknown_owned.as_raw(), test.as_raw());
assert_eq!(unknown_owned.Test(), 0);
}

assert_eq!(test.Test(), 0);
drop(test);
assert_eq!(dropped, 1);
}
}

0 comments on commit 61bc313

Please sign in to comment.