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

How to cast C++ IUnknown pointer to Rust IUnknown struct? #1975

Closed
seungha-yang opened this issue Aug 22, 2022 · 8 comments · Fixed by #2010
Closed

How to cast C++ IUnknown pointer to Rust IUnknown struct? #1975

seungha-yang opened this issue Aug 22, 2022 · 8 comments · Fixed by #2010

Comments

@seungha-yang
Copy link

I'm currently trying to cast C++ IUnknown pointer (*mut c_void) to rust IUnknown via std::mem::transmute_copy
but not successful so far.

Is it supposed to be working or is there any guide for the scenario?

@kennykerr kennykerr added the question Further information is requested label Aug 22, 2022
@kennykerr
Copy link
Collaborator

It's not particularly easy because it's not particularly safe. There's also the question of whether the C++ pointer is owned or dangling. Do you want the Rust IUnknown to assume ownership - and thus call Release when it is dropped - or only reference the C++ pointer that is potentially owned by someone else?

If we assume you're only borrowing someone else's pointer, then it would look something like this:

let raw: *mut std::ffi::c_void = ptr; // someone else will call `ptr->Release()`
let unknown: &windows::core::IUnknown = std::mem::transmute(&raw);

You can now use unknown for as long as raw is valid...

If we assume you're taking ownership of the pointer, then it would look something like this:

let raw: *mut std::ffi::c_void = ptr; // nobody else will touch `ptr` again
let unknown: windows::core::IUnknown = std::mem::transmute(raw);

You can now use unknown for as long as it is valid as it owns a reference and will call Release when it is dropped.

@seungha-yang
Copy link
Author

Thanks for the feedback. My case is borrowing C++ pointer and it was indeed ownership issue. I worked around it via core::mem::forget or something else so that rust IUnknown can be stay in the scope without IUnknown::Release.

I understood that this kind of stuff is hard (or almost impossible) to be safe but I'm wondering if there can be a higher level and a bit safer helper method. Particularly I'm working on rust binding for GStreamer D3D11 library and plugin

@sdroege
Copy link

sdroege commented Aug 23, 2022

Such helper API would certainly be useful, even if it has to be unsafe. It's not obvious which memory representations are equivalent and having to call transmute() with all it's pre-conditions is a lot less safe than having e.g.

unsafe fn from_raw_owned(ptr: *mut c_void) -> IUnknown;
unsafe fn from_raw_borrowed<'a>(ptr: &'a *mut c_void) -> &'a IUnknown;

Both could document the pre-conditions for calling these safely much clearer than transmute() could, i.e. the first only needs a valid pointer to an IUnknown that is owned by the code in question, while the second requires a valid pointer and that the pointer is valid at least for the lifetime 'a.

@kennykerr
Copy link
Collaborator

Thanks for the suggestion! I'm just not convinced this is any safer or even any clearer.

These would just wrap transmute calls. Yes, there's value in having the functions be somewhat self-documenting but even then the names can be misleading. Does from_raw_owned mean the raw pointer is owned or the resulting IUnknown is owned. There's the difference between ownership and transferring ownership. Either way you need a good understanding of how COM interface pointers are represented in both C++ and Rust to use it safely.

I'm interested in what @rylev has to say.

@sdroege
Copy link

sdroege commented Aug 25, 2022

Either way you need a good understanding of how COM interface pointers are represented in both C++ and Rust to use it safely.

You need that anyway if you want to deal with raw pointers :) The main advantage of those functions over just transmuting is that they make it clearer what is allowed to do and what not, and how memory layouts are matching. And more importantly, they're much more discoverable than knowing that you can transmute things under certain conditions.

For example you could create an &IUnknown from a *mut c_void via transmute if you're not careful, but such functions would avoid such mistakes.

Similarly, the documentation would make it clear that in the *mut c_void -> IUnknown case the object must be owned by the code and it would make the mistake much less likely that people do a transmute on an unowned/borrowed pointer and then accidentally Drop it.

In any case, these functions would still be unsafe so would require an unsafe block.

(Note that the &'a *mut c_void -> &'a IUnknown function doesn't even need a transmute but just a raw pointer cast and dereference)


FWIW, in the GObject (GTK/GStreamer/etc) Rust bindings we provide such functions and people generally seem to use them correctly while that's much less often the case with transmute.

@rylev
Copy link
Contributor

rylev commented Aug 26, 2022

I am absolutely in favor of these functions existing for many of the same reasons as @sdroege listed. I think any time we're requiring a transmute, we have a failure in the API design.

@kennykerr
Copy link
Collaborator

Sounds good - I'll try to get to it as I have time.

@kennykerr kennykerr added enhancement New feature or request unsafe and removed question Further information is requested enhancement New feature or request labels Aug 29, 2022
@kennykerr kennykerr changed the title How to cast C++ IUnknown pointer to rust IUnknown struct? How to cast C++ IUnknown pointer to Rust IUnknown struct? Sep 2, 2022
@kennykerr
Copy link
Collaborator

Sorry for the delay - here you go: #2010

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