rust-windows
rust-windows copied to clipboard
COM binding generator
COM is implemented as flat vtable structs. For example,
interface A {
HRESULT DoSomethingA([in] UINT value);
}
interface B: A {
HRESULT DoSomethingB([in] UINT value);
}
becomes (in C)
struct AVtbl {
// A methods
HRESULT (STDMETHODCALLTYPE *DoSomethingA)(A* This, UINT value);
};
struct A { AVtbl* vtable; };
struct BVtbl {
// A methods (inherited)
HRESULT (STDMETHODCALLTYPE *DoSomethingA)(B* This, UINT value);
// B methods
HRESULT (STDMETHODCALLTYPE *DoSomethingB)(B* This, UINT value);
};
struct B { BVtbl * vtable; };
That is, "inheritance" on COM interfaces are flatten down in C/C++ world (struct B
does not inherit struct A
but instead struct B
copies all methods from struct A
), and since COM uses simple C++ vtable ABI, it's straightforward to use COM in C (AVtbl
/BVtbl
).
So it's possible to generate "good" COM bindings. e.g.
struct BVtbl {
DoSomethingA: extern "stdcall" fn(This: *mut B, value: UINT) -> HRESULT;
DoSomethingB: extern "stdcall" fn(This: *mut B, value: UINT) -> HRESULT;
};
struct B { vtable: *const BVtbl; };
impl B {
fn DoSomethingA(&mut self, value: UINT) -> HRESULT {
unsafe { (self.vtable.DoSomethingA)(self, value) }
}
fn DoSomethingB(&mut self, value: UINT) -> HRESULT {
unsafe { (self.vtable.DoSomethingB)(self, value) }
}
}
then some_b.DoSomethingA(value);
is possible and it looks good. See proof-of-concept demo.
mingw-w64 maintains .idl
files (e.g. shobjidl.idl) as well as corresponding C headers (e.g. shobjidl.h). This means we may use rust-bindgen to generate COM binding from C header, although the "raw binding" may not be convenient.
It's also possible to create COM bindings by hands. Some months ago I've created a some COM bindings by hands, but it's definitely not feasible.
It's also possible to write a MIDL-to-rust binding generator. I think it's the best way, but currently I don't know how hard it is.. :P
I spent quite some time looking at a MIDL-to-Rust generator. It's basically impossible, at least with the IDL files that Wine distributes, because they include significant portions of C headers in them. I think it's a better idea to just generate from C headers. Note though that due to https://github.com/crabtw/rust-bindgen/issues/94 rust-bindgen can produce bad code. I've worked around this by defining _C89_NAMELESSUNION1
etc to nameless1
etc.
A MIDL-to-Rust compiler would be very useful. Creating bindings for things like https://github.com/mascarenhas/luacomgen/blob/master/opc/opcda.idl is exceedingly tedious, even though they're quite regular. Such a binding generator could rely on retep998/winapi-rs to resolve typedefs.
And crabtw/rust-bindgen#94 may be resolved by crabtw/rust-bindgen#135.