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

Feature request: COM _Impl types are not mutable #2304

Closed
imgurbot12 opened this issue Jan 24, 2023 · 9 comments
Closed

Feature request: COM _Impl types are not mutable #2304

imgurbot12 opened this issue Jan 24, 2023 · 9 comments
Labels
question Further information is requested

Comments

@imgurbot12
Copy link

imgurbot12 commented Jan 24, 2023

Motivation

Hello, I'm hoping to make use of windows-rs to implement a Windows Credential Provider in Rust.

There are a lot of steps involved when calling the associated interfaces that require the interface to mutate to keep track of information supplied to them in earlier calls, like the CREDENTIAL_PROVIDER_USAGE_SCENARIO.

Until these interfaces have the option to remain mutable it seems nearly impossible to properly design and build an implementation for these interfaces in Rust.

Drawbacks

Obviously not all functions need to be mutable but I'd imagine anything with a Set prefix in the function should follow this standard.

Rationale and alternatives

I'm a relatively new Rust developer but I'm not aware of any alternatives without somehow undoing every single rust security and compiler feature there is to somehow mutate an otherwise immutable object.

Additional context

No response

@imgurbot12 imgurbot12 added the enhancement New feature or request label Jan 24, 2023
@kennykerr kennykerr added question Further information is requested and removed enhancement New feature or request labels Jan 24, 2023
@imgurbot12
Copy link
Author

Hello @kennykerr, I assume because the label was changed from "enhancement" to "question" that I'm likely missing some key-info about implementations and how to go about them.

Is there some documentation regarding how to implement the COM Interfaces appropriately that I missed? Any help is appreciated. Thank you

@kennykerr
Copy link
Collaborator

Yeah, sorry I plan to write about it here as it comes up quite often: https://kennykerr.ca/rust-getting-started/

That's the next topic on my list. 😊

Briefly, Rust aliasing rules surrounding &self and &mut self preclude the latter from ever being sound when combined with COM's reference counting model where there may be multiple outstanding references. Here's an example of how to handle this in Rust:

https://github.com/microsoft/windows-rs/blob/master/crates/tests/implement/tests/vector.rs

Basically, you can use something like RwLock to coordinate access to mutable state. Having spent a number of decades writing COM code in C++, I've actually come around to this model and find it far more intuitive as you cannot so easily write incorrect multi-threaded COM in Rust as you can in C++.

@imgurbot12
Copy link
Author

Ah ok. If that works then that's great. Thanks!
While I have your attention I'm not quite sure about how to expose those COM interfaces after they've been written. Are there any extra steps to expose them and register them with windows besides simply compiling the project as a DLL?

I noticed you had written this example that included a lot of overhead in-between code that included IDL definitions. Is that necessary for predefined Implementation types?

@kennykerr
Copy link
Collaborator

The scaffolding in the linked example is intended for WinRT components. What your component may need depends very much on the API you're implementing and is more a question for those more familiar with this particular authentication API. For that you're better off checking the docs or going over to https://stackoverflow.com/ as I'm not familiar.

@imgurbot12
Copy link
Author

Will do. Thanks for the info :)

@riverar
Copy link
Collaborator

riverar commented Jan 24, 2023

Perhaps a snippet that might help you as well.

+use std::cell::RefCell;

use windows::{
    core::implement,
    Win32::UI::Shell::{ICredentialProvider, ICredentialProvider_Impl},
};

fn main() -> windows::core::Result<()> {
    #[implement(ICredentialProvider)]
    struct Provider {
+        mutable_state: RefCell<u32>,
    }

    impl Provider {
+        fn new() -> Self {
+           Self {
+               mutable_state: RefCell::new(0),
+           }
+       }
    }

    impl ICredentialProvider_Impl for Provider {
        fn Advise(
            &self,
            pcpe: &core::option::Option<windows::Win32::UI::Shell::ICredentialProviderEvents>,
            upadvisecontext: usize,
        ) -> windows::core::Result<()> {
+           *self.mutable_state.borrow_mut() = 42;
            todo!();
        }

        fn GetCredentialAt(
            &self,
            dwindex: u32,
        ) -> windows::core::Result<windows::Win32::UI::Shell::ICredentialProviderCredential>
        {
            todo!();
        }

        fn GetCredentialCount(
            &self,
            pdwcount: *mut u32,
            pdwdefault: *mut u32,
            pbautologonwithdefault: *mut windows::Win32::Foundation::BOOL,
        ) -> windows::core::Result<()> {
            todo!();
        }

        fn GetFieldDescriptorAt(
            &self,
            dwindex: u32,
        ) -> windows::core::Result<
            *mut windows::Win32::UI::Shell::CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR,
        > {
            todo!();
        }

        fn GetFieldDescriptorCount(&self) -> windows::core::Result<u32> {
            todo!();
        }

        fn SetSerialization(
            &self,
            pcpcs: *const windows::Win32::UI::Shell::CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION,
        ) -> windows::core::Result<()> {
            todo!();
        }

        fn SetUsageScenario(
            &self,
            cpus: windows::Win32::UI::Shell::CREDENTIAL_PROVIDER_USAGE_SCENARIO,
            dwflags: u32,
        ) -> windows::core::Result<()> {
            todo!();
        }

        fn UnAdvise(&self) -> windows::core::Result<()> {
            todo!();
        }
    }

    Ok(())
}

@imgurbot12
Copy link
Author

@riverar that helps thank you!

Now I just need to figure out how exposing the object works in windows. I've done basic FFI before but I've never done DLLs or worked with windows like this and COM programming seems so odd.

@tim-weis
Copy link
Contributor

tim-weis commented Feb 2, 2023

Quick follow-up question here: Would it be sufficient to set the panic strategy to "abort", so as to prevent potential panics from crossing the ABI?

@kennykerr
Copy link
Collaborator

I believe that's correct. Related: #1061

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

No branches or pull requests

4 participants