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 binding generator #5

Open
klutzy opened this issue Jul 24, 2014 · 2 comments
Open

COM binding generator #5

klutzy opened this issue Jul 24, 2014 · 2 comments

Comments

@klutzy
Copy link
Owner

klutzy commented Jul 24, 2014

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

@emberian
Copy link

emberian commented Nov 3, 2014

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.

@alexchandel
Copy link

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.

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

No branches or pull requests

3 participants