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

AbiTransferable on complex structures in winapi crate #197

Open
peacememories opened this issue Jan 17, 2021 · 5 comments
Open

AbiTransferable on complex structures in winapi crate #197

peacememories opened this issue Jan 17, 2021 · 5 comments

Comments

@peacememories
Copy link

I'm trying to build something that interfaces with DirectShow at the moment, and when describing the needed interfaces, I need a lot of types like LPCWSTR. winapi comes in handy by defining these types so I don't have to construct them myself. I'm running into problems with some of the types though, e.g. winapi::shared::ntdef::LARGE_INTEGER. This type is not just a simple alias of a core type, therefore com requires it to be AbiTransferable, which is not implemented on LARGE_INTEGER.

What is the best method to get those types to work with com, ideally without rewriting them?
Thanks in advance for any feedback you can provide :)

@danielegarciav
Copy link

This is an issue I came across as well. I ended up making a really quick and dirty AbiWrapper struct. I'm not even sure it's correct, but it works for my use case (FFI with windows::Windows::Win32::System::Variant::VARIANT and windows::Windows::Win32::UI::Accessibility::UIA_PROPERTY_ID).

#[repr(transparent)]
#[derive(Copy, Clone, Debug)]
struct AbiWrapper<T: Clone>(T);
unsafe impl<T: Clone> com::AbiTransferable for AbiWrapper<T> {
  type Abi = T;
  fn get_abi(&self) -> Self::Abi {
    self.0.clone()
  }
  fn set_abi(&mut self) -> *mut Self::Abi {
    &mut self.0
  }
}

I'm sure someone else can polish this into something more correct and polished.

For completeness, here's the IUIAutomationPropertyChangedEventHandler COM interface and class definitions I wrote (had to rewrite the interface using Microsoft's com since the windows crate doesn't support producing COM classes yet):

com::interfaces! {
  #[uuid("40CD37D4-C756-4B0C-8C6F-BDDFEEB13B50")]
  unsafe interface IChangeEventHandler: IUnknown {
    fn HandlePropertyChangedEvent(
      &self,
      sender: *mut std::ffi::c_void,
      propertyid: AbiWrapper<UIA_PROPERTY_ID>,
      newvalue: AbiWrapper<VARIANT>,
    ) -> ::windows::core::HRESULT;
  }
}

com::class! {
  pub class EventHandler: IChangeEventHandler {}

  impl IChangeEventHandler for EventHandler {
    fn HandlePropertyChangedEvent(&self, _sender: *mut std::ffi::c_void, _propertyid: AbiWrapper<UIA_PROPERTY_ID>, newvalue: AbiWrapper<VARIANT>) -> windows::core::HRESULT {
      let new_scroll_value = unsafe { newvalue.0.Anonymous.Anonymous.Anonymous.dblVal };
      tracing::debug!("scroll event: {}", new_scroll_value);
      windows::core::Result::Ok(()).into()
    }
  }
}

@MarijnS95
Copy link
Contributor

since the windows crate doesn't support producing COM classes yet

are you sure? Defining your own interface in the same way the generator does (if you cannot produce a winmd which is extremely trivial based on a C++ header) should be easy, and their #[implement] attribute should then allow you to produce class instances?

@kennykerr
Copy link
Collaborator

This has been supported for years. 😀

@danielegarciav
Copy link

@MarijnS95 @kennykerr sorry y'all, I just found about windows_implement few hours after that comment 😅. Absolutely love how simple it is to define a class for an existing interface, rust_analyzer wrote the impl block for me and everything:

#[windows_implement::implement(IUIAutomationPropertyChangedEventHandler)]
struct ScrollEventHandler {
  /* fields */
}

impl IUIAutomationPropertyChangedEventHandler_Impl for ScrollEventHandler {
  fn HandlePropertyChangedEvent(
    &self,
    sender: Option<&IUIAutomationElement>,
    propertyid: UIA_PROPERTY_ID,
    newvalue: &VARIANT,
  ) -> windows::core::Result<()> {
    /* impl */
  }
}

I found out about windows_implement from surfing through github issues, AFAIK it's not documented yet.

One thing I don't understand is, I was under the impression that COM instances must be heap allocated and pinned in memory. The implement macro adds From impls to convert from our custom struct into either our implemented interfaces or into a COM IUnknown, and those impls box the instance, but they don't pin it.

The safety docs for the cast method say that the object must be heap allocated and pinned using the provided methods, but the provided methods don't pin. Is that expected? The older com crate does use Pin<Box<T>> (link).

@MarijnS95
Copy link
Contributor

AFAIK it's not documented yet.

Exactly, ran into this while writing the above reply and filed an issue for it: microsoft/windows-rs#2694. windows-implement isn't reachable from the windows/windows-core docs but it barely matters as the crate/docs have no doc-comments themselves anyway.


but they don't pin it

Perhaps there's nothing happening to the contents of these boxes that would make the pointer move around? Pin<> is only an extra safeguard, not a thing that "makes everything work" (IMO).

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

4 participants