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

Adds unsafe helpers to cast from C++ or raw IUnknown pointers to Rust IUnknown values #2010

Merged
merged 4 commits into from Sep 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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,
kennykerr marked this conversation as resolved.
Show resolved Hide resolved
}

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);
}
}