-
-
Notifications
You must be signed in to change notification settings - Fork 393
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
Comments
I tried to implement a COM interface using a convenient trait with an associated const for the vtbl, but ran into an ICE. |
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() };
} |
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();
} |
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? |
Would be even better to have a decorator which turns a |
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.
The text was updated successfully, but these errors were encountered: