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

COM Interface Declaration Support #1540

Merged
merged 13 commits into from
Feb 16, 2022
Merged

COM Interface Declaration Support #1540

merged 13 commits into from
Feb 16, 2022

Conversation

rylev
Copy link
Contributor

@rylev rylev commented Feb 15, 2022

This PR adds the very beginnings of COM interface declaration support.

#[interface("8CEEB155-2849-4ce5-9448-91FF70E1E4D9")]
unsafe trait IUIAnimationVariable: IUnknown {
    fn GetValue(&self, value: *mut f64) -> HRESULT;
}

There is still plenty of additional support to add, and many interfaces will not work yet, but I believe it's a good start, and I wanted to get feedback sooner rather than later.

Fixes #1486

@kennykerr
Copy link
Collaborator

That's a great start. That's essentially what I had in mind with #1486. A few comments.

Why require an unsafe trait? It's not actually making a Rust trait, so it seems a bit superfluous.

We need to support two key scenarios that should be easy to test:

  1. Re-declare an existing Windows interface and call it against an existing Windows implementation.

  2. Use the implement macro to implement the newly minted interface and test (call) it through the definition provided by the windows crate. Something simple like CreateUri and IUri from the com_uri sample should suffice to smoke out basic issues to start.

Something like this should validate the basic support:

#![allow(non_snake_case)]

use windows::{
    core::*,
    Win32::Foundation::*,
    Win32::System::Com::*,
};

#[interface("a39ee748-6a27-4817-a6f2-13914bef5890")]
unsafe trait ICustomUri : IUnknown {
    fn GetPropertyBSTR(&self);
    fn GetPropertyLength(&self);
    fn GetPropertyDWORD(&self);
    fn HasProperty(&self);
    fn GetAbsoluteUri(&self);
    fn GetAuthority(&self);
    fn GetDisplayUri(&self);
    fn GetDomain(&self, value: *mut BSTR) -> HRESULT;
    // etc
}

#[implement(ICustomUri)]
struct CustomUri();

impl ICustomUri_Impl for CustomUri {
    fn GetPropertyBSTR(&self) { todo!() }
    fn GetPropertyLength(&self) { todo!() }
    fn GetPropertyDWORD(&self) { todo!() }
    fn HasProperty(&self) { todo!() }
    fn GetAbsoluteUri(&self) { todo!() }
    fn GetAuthority(&self) { todo!() }
    fn GetDisplayUri(&self) { todo!() }
    fn GetDomain(&self, value: *mut BSTR) -> HRESULT {
        *value = "kennykerr.ca".into();
        S_OK
    }
}

fn main() -> windows::core::Result<()> {
    unsafe {
        // Use the OS implementation through the OS interface
        let a: IUri = CreateUri("http://kennykerr.ca", Default::default(), 0)?;
        let domain = a.GetDomain()?;
        assert_eq!(domain, "kennykerr.ca");

        // Call the OS implementation through the custom interface
        let b: ICustomUri = a.cast()?;
        let mut domain = BSTR::new();
        b.GetDomain(&mut domain).ok()?;
        assert_eq!(domain, "kennykerr.ca");

        // Use the custom implementation through the OS interface
        let c: IUri = CustomUri().into();
        let domain = c.GetDomain()?;
        assert_eq!(domain, "kennykerr.ca");

        // Call the custom implementation through the custom interface
        let d: ICustomUri = c.cast()?;
        let mut domain = BSTR::new();
        d.GetDomain(&mut domain).ok()?;
        assert_eq!(domain, "kennykerr.ca");

        Ok(())
    }
}

@rylev
Copy link
Contributor Author

rylev commented Feb 16, 2022

@kennykerr thanks for the example code! I got it working (with a few minor changes that I believe were just typos in the provided example).

As for why I require unsafe before the trait: this was a source of debate in com-rs. All the methods are als required to be marked unsafe which definitely makes sense as calling a COM method is inherently unsafe (if the interface is declared wrong you might end up with incorrect pointer dereferences and such). Since the declaration of the interface needs to be correct in order for things to work properly (and not potentially end up dereferencing junk memory for example), I think it makes sense to require the user to annotate the trait with unsafe as a sort of acknowledgement of this fact.

I talk a bit more about this in the com-rs docs.

Speaking of which, I think I should add docs on how interface and implement work.

@fu5ha
Copy link

fu5ha commented Feb 19, 2022

🥳🥳 Awesome. Hoping to use this to write an implementation of the d3d9 interface in Rust :) Great to see this stuff from the com crate moved over into this one.

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.

Add interface macro to define COM interfaces locally
3 participants