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

Add support for non-COM interfaces #2066

Merged
merged 4 commits into from Sep 26, 2022
Merged

Add support for non-COM interfaces #2066

merged 4 commits into from Sep 26, 2022

Conversation

kennykerr
Copy link
Collaborator

Well, this was a lot more complicated than I'd hoped! I'm not a big fan of interfaces that sort of pretend to be COM interfaces but don't actually inherit from IUnknown. The thing is that IUnknown addresses a number of fundamental problems with lifetime and discovery that are just left in question without it. The solution here isn't perfect but it's a start and builds on a number of PRs that have already been completed. Here's what's new:

  1. You can use the interface macro to declare a (new) interface that derives from an existing interface defined by the windows crate. The implement macro can implement COM interfaces regardless of how they were declared.

  2. You cannot implement non-COM interfaces with the implement macro. Since IUnknown is not available to reason about lifetime, the implement macro isn't much use, but you can simply use the new method on the interface to "wrap" an implementation of the interface's trait (or traits) with a scoped implementation.

struct Reflection;

impl ID3D12FunctionParameterReflection_Impl for Reflection {
    fn GetDesc(&self) -> Result<D3D12_PARAMETER_DESC> {
        todo!();
    }
}

let reflection = ID3D12FunctionParameterReflection::new(&Reflection);
let desc = reflection.GetDesc()?;
  1. You can use the interface macro to declare a (new) interface that does not derive from IUnknown. This too can be implemented in the same way:
#[interface]
unsafe trait IValuable {
    unsafe fn Value(&self) -> i32;
}

struct Valuable(i32);

impl IValuable_Impl for Valuable {
    unsafe fn Value(&self) -> i32 {
        self.0
    }
}

unsafe fn call_value(test: &IValuable) -> i32 {
    test.Value()
}

#[test]
fn main {
    unsafe {
        // Since the interface is not rooted in `IUnknown`, there's no COM-style lifetime and the 
        // resulting implementation merely exists for the lifetime of the referenced implementation. 
        let test = Valuable(456);
        let interface = IValuable::new(&test);
        assert_eq!(call_value(&interface), 456);
        assert_eq!(interface.Value(), 456);
    }
}
  1. Non-COM interfaces no longer hold a private IUnknown pointer so you don't have to worry about them doing bad things when dropped.

Fixes: #453

@kennykerr kennykerr merged commit d7343bc into master Sep 26, 2022
@kennykerr kennykerr deleted the non-com-interfaces branch September 26, 2022 20:59
@wesleywiser
Copy link

Thanks @kennykerr! Really appreciate your help on this 🥇

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

Successfully merging this pull request may close these issues.

Support COM-like interfaces that don't inherit from IUnknown
2 participants