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

Add bindings for IShellExtInit and IContextMenu #145

Open
gentoo90 opened this issue Jul 16, 2015 · 5 comments
Open

Add bindings for IShellExtInit and IContextMenu #145

gentoo90 opened this issue Jul 16, 2015 · 5 comments
Labels
missing api A request for a missing API

Comments

@gentoo90
Copy link
Contributor

Also, can you give me some tips on implementing COM interface in Rust?
I was going to try creating a shell extension in Rust, but got stuck on this.

@retep998
Copy link
Owner

I tried to implement a COM interface using a convenient trait with an associated const for the vtbl, but ran into an ICE.

@retep998
Copy link
Owner

Okay, I think I actually got something working.

#[repr(C)]
struct ComRepr<Type, Vtbl>(*const Vtbl, Type);
trait Com where Self: Sized {
    type Interface;
    type Vtbl: 'static;
    fn vtbl() -> &'static Self::Vtbl;
    fn into_interface(self) -> *mut Self::Interface {
        let com = Box::new(ComRepr(Self::vtbl(), self));
        Box::into_raw(com) as *mut Self::Interface
    }
    unsafe fn from_interface<'a>(thing: *mut Self::Interface) -> &'a mut Self {
        &mut (*(thing as *mut ComRepr<Self, Self::Vtbl>)).1
    }
    unsafe fn destroy(thing: *mut Self::Interface) {
        Box::from_raw(thing as *mut ComRepr<Self, Self::Vtbl>);
    }
}
struct MyType {
    counter: u32,
}
impl Drop for MyType {
    fn drop(&mut self) {
        println!("Dropped!");
    }
}
const MyTypeVtbl: &'static IUnknownVtbl = &IUnknownVtbl {
    QueryInterface: { unsafe extern "system" fn QueryInterface(This: *mut IUnknown, riid: REFIID, ppvObject: *mut *mut c_void) -> HRESULT  { unimplemented!() } QueryInterface },
    AddRef: { unsafe extern "system" fn AddRef(This: *mut IUnknown) -> ULONG {
        println!("AddRef!");
        let this = MyType::from_interface(This);
        this.counter += 1;
        this.counter
    } AddRef },
    Release: { unsafe extern "system" fn Release(This: *mut IUnknown) -> ULONG {
        println!("Release!");
        let count = {
            let this = MyType::from_interface(This);
            this.counter -= 1;
            this.counter
        };
        if count == 0 { MyType::destroy(This) }
        count
    } Release },
};
impl Com for MyType {
    type Interface = IUnknown;
    type Vtbl = IUnknownVtbl;
    fn vtbl() -> &'static IUnknownVtbl { MyTypeVtbl }
}
fn main() {
    let com = MyType { counter: 1 }.into_interface();
    unsafe { (*com).AddRef() };
    unsafe { (*com).Release() };
    unsafe { (*com).Release() };
}

@strega-nil
Copy link

If parametrized statics were allowed, the following solution would be fairly nice, imho:

#![allow(non_camel_case_types, non_snake_case)]

enum c_void { }
type REFIID = u32;
type ULONG = u32;
type HRESULT = u32;

#[repr(C)]
struct ComRepr<Type, Vtbl> {
    vt: *const Vtbl,
    ty: Type,
}

#[repr(C)]
struct IUnknownVtbl {
    QueryInterface: unsafe extern "system" fn(This: *mut IUnknown,
        riid: REFIID, ppvObject: *mut *mut c_void) -> HRESULT,
    AddRef: unsafe extern "system" fn(This: *mut IUnknown) -> ULONG,
    Release: unsafe extern "system" fn(This: *mut IUnknown) -> ULONG,
}
type IUnknown = ComRepr<(), IUnknownVtbl>;
trait IUnknownTrait: Sized {
    unsafe fn QueryInterface(&mut self, riid: REFIID,
            ppvObject: *mut *mut c_void) -> HRESULT;
    unsafe fn AddRef(&mut self) -> ULONG;
    unsafe fn Release(&mut self) -> ULONG;
    fn into_interface(self) -> *mut IUnknown {
        // /me is disappointment, Rust
        unsafe extern "system" fn QueryInterface<T: IUnknownTrait>(
                This: *mut IUnknown, riid: REFIID, 
                ppvObject: *mut *mut c_void) -> HRESULT {
            T::from_interface(&mut This).QueryInterface(riid, ppvObject)
        }
        unsafe extern "system" fn AddRef<T: IUnknownTrait>(
                This: *mut IUnknown) -> ULONG {
            T::from_interface(&mut This).AddRef()
        }
        unsafe extern "system" fn Release<T: IUnknownTrait>(
                This: *mut IUnknown) -> ULONG {
            T::from_interface(&mut This).Release()
        }
        static VT: IUnknownVtbl = IUnknownVtbl {
            QueryInterface: QueryInterface::<Self>,
            AddRef: AddRef::<Self>,
            Release: Release::<Self>,
        };
        let known = Box::into_raw(Box::new(ComRepr {
            vt: &VT,
            ty: self
        }));
        known as *mut IUnknown
    }
    unsafe fn from_interface(this: &mut *mut IUnknown) -> &mut Self {
        &mut *(&mut (**this).ty as *mut () as *mut Self)
    }
    unsafe fn destroy(this: *mut IUnknown) {
        Box::from_raw(this as *mut ComRepr<Self, IUnknownVtbl>);
    }
}
impl IUnknownTrait for *mut IUnknown {
    unsafe fn QueryInterface(&mut self,
            riid: REFIID, ppvObject: *mut *mut c_void) -> HRESULT {
        ((*(**self).vt).QueryInterface)(*self, riid, ppvObject)
    }
    unsafe fn AddRef(&mut self) -> ULONG {
        ((*(**self).vt).AddRef)(*self)
    }
    unsafe fn Release(&mut self) -> ULONG {
        ((*(**self).vt).Release)(*self)
    }
}

// --- USER CODE ---
struct MyType {
    counter: u32,
}
impl Drop for MyType {
    fn drop(&mut self) {
        println!("Dropped!");
    }
}

impl IUnknownTrait for MyType {
    unsafe fn QueryInterface(&mut self,
            _riid: REFIID, _ppvObject: *mut *mut c_void) -> HRESULT {
        unimplemented!()
    }
    unsafe fn AddRef(&mut self) -> ULONG {
        println!("AddRef!");
        self.counter += 1;
        self.counter
    }
    unsafe fn Release(&mut self) -> ULONG {
        println!("Release!");
        self.counter -= 1;
        let count = self.counter;
        if count == 0 {
            std::ptr::drop_in_place(self)
        }
        count
    }
}

fn main() {
    let mut com = MyType { counter: 1 }.into_interface();
    com.AddRef();
    com.Release();
    com.Release();
}

@mash-graz
Copy link

this are the only examples i found, how to implement COM in rust. i'm still very confused, puzzling how to wrap new components in an exemplary way.

are this examples here still state of the art, or do we have some more recent macros in winapi-rs or winrt-rust, to handle this kind of task in a more efficient, feature complete and rust adequate way?

@gentoo90
Copy link
Contributor Author

gentoo90 commented Aug 7, 2016

Would be even better to have a decorator which turns a struct into a COM-object and a trait into a COM-interface

@retep998 retep998 added the missing api A request for a missing API label Aug 28, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
missing api A request for a missing API
Projects
None yet
Development

No branches or pull requests

4 participants