Closed saschanaz closed 3 years ago
The #[com_interface]
and #[com_class]
implementations were changed to avoid the need for #[com_impl]
so it's not needed, that comment is slightly outdated.
There are few problems in supporting winapi
directly.
#[com_interface]/#[com_class]
macros implement Intercom traits for the types, crates using Intercom cannot use those macros to implement said traits for winapi types due to Rust trait impl rules. While it would be possible to have the trait impls as part of Intercom, this brings us to the second issue.winapi
types are structs instead of traits. They are not specified to be easily implementable for local types, their primary use case seems to be calling other COM components that implement the interfaces.impl IFoo for Bar { ... }
item, this only works if the interface is being implemented locally (ie. if there is an impl
item for the interface to begin with).HRESULT
+ [retval] Foo
parameters and Result<Foo, ComError>
, but this only occurs when the traits are defined with these in mind. Although Intercom should support HRESULT
/*mut
/etc. parameters so even a "raw" interface definition should be usable by Intercom, even if the user loses some of the convenience.If you want to call existing COM methods (ie, you receive ISequentialStream
as a parameter), you can just accept the type as a winapi-rs
pointer and then use that. However if you need to implement ISequentialStream
, I'm not entirely sure how winapi-rs
types would help, given they are structs instead of traits.
I guess one benefit would be to somehow derive the trait definition from the winapi-rs struct/vtbl, but at that point I feel like the goal should be to derive those definitions from the sources winapi-rs uses (original IDL files or such).
And the above might sound a bit aimless, which is probably because it is! I'd still love "winapi-rs compatibility" - but other than being able to reuse structs (without having to newtype them), I don't think I know what that compatibility would mean. :x
If you want to call existing COM methods (ie, you receive
ISequentialStream
as a parameter), you can just accept the type as awinapi-rs
pointer and then use that.
Hmm, so the following code throws build errors, where it receives IStream
as a parameter:
use winapi::um::objidlbase::IStream;
#[com_interface(com_iid = "b824b49d-22ac-4161-ac8a-9916e8fa3f7f")]
trait IInitializeWithStream {
fn initialize(&self, stream: &ComItf<IStream>, mode: DWORD) -> ComResult<()>;
}
error[E0277]: the trait bound `IStream: ForeignType` is not satisfied
--> src\lib.rs:197:26
|
197 | fn initialize(&self, stream: &ComItf<IStream>, mode: DWORD) -> ComResult<()>;
| ^^^^^^ the trait `ForeignType` is not implemented for `IStream`
|
::: C:\Users\Kagami\.cargo\git\checkouts\intercom-a35634f63190d595\4633ab2\intercom\src\type_system.rs:108:1
|
108 | pub unsafe trait ExternType<TS: TypeSystem>
| ------------------------------------------- required by this bound in `ExternType`
|
= note: required because of the requirements on the impl of `ExternType<RawTypeSystem>` for `&intercom::ComItf<IStream>`
And of course the "basic Rust trait rules" prevents me to derive that. Based on the mentioned problems I guess there is no good way to do this yet.
By using the winapi pointer, you'll need to deal with the "raw" IStream
struct. You should be able to newtype that similar to how the other winapi types are newtyped for various Intercom traits:
#[derive(intercom::ExternType, intercom::ForeignType, intercom::ExternOutput, intercom::ExternInput)]
#[repr(transparent)]
struct ComIStream(IStream)
The winapi IStream
should already be FFI compatible and the newtype wrapping allows you to implement the traits necessary for Intercom to handle it - though ExternOutput
is not strictly necessarily if the type never appears as a return value.
In any case, that should allow you to define the method as
fn initialize (&self, stream: ComIStream, ...) -> ComResult<()> {
let istream = stream.0;
unsafe {
let hr = istream.foo(..);
}
}
Intercom does implement From<intercom::error::raw::HRESULT> for ComResult<()>
and intercom::raw::HRESULT
can be constructed with ::new(i32)
, which winapi::...::HRESULT
should be compatible with (being a type alias for i32
, so along the lines of:
use intercom::error::raw::HRESULT as IntercomHR;
let winapi_hr = istream.foo(..);
let intercom_hr = IntercomHR::new(winapi_hr);
ComResult::from(intercom_hr)?;
The last line might need type parameters along the lines of ComResult::<()>::from(...)
, etc.
If you need the ISequentialStream
, you'll need to go through query_interface
and remember to handle reference counting manually since there's no ComRc
/ComItf
wrappers to handle that.
Thanks, I made it work with the Com* wrapper!
For anyone who read this later: It should be ComLPSTREAM
instead of ComIStream
because you'll receive a pointer. query_interface
is not needed because an IStream is automatically ISequentialStream (because it's the ancestor interface).
For example, the thumbnail provider example redefines IStream without using the corresponding winapi definition. Implementing an actual provider requires better definition of IStream with ISequentialStream but redefining everything sounds not ideal.
https://github.com/Rantanen/intercom/issues/21#issuecomment-350478604 mentions
[com_impl]
but it's not available anymore. Is there still a way to do it?