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

We need an FAQ #1647

Closed
kennykerr opened this issue Mar 31, 2022 · 18 comments
Closed

We need an FAQ #1647

kennykerr opened this issue Mar 31, 2022 · 18 comments

Comments

@kennykerr
Copy link
Collaborator

kennykerr commented Mar 31, 2022

Eventually I'd like to get our doc writers involved, but I'm not quite ready to unleash their talents. For the time being, I think it would be handy just to have an informal FAQ to deal with common questions. Naturally, anything in the FAQ is also a candidate for simplifying this project in some way to avoid needing an FAQ in the first place. Still, there will always be common issues that newcomers will run into. Here's a start, based on some common questions I've received:

  • How do I choose between the windows and windows-sys crates
  • Is the nightly compiler needed for some features
  • What if I can't find the API I need
  • Why are the macros from the Windows SDK missing
  • How to query for a specific COM interface
  • How are optional COM interfaces handled
  • How do I implement an existing COM interface
  • How do I declare a new COM interface

What's on your wish list for an FAQ?

@kennykerr kennykerr added the enhancement New feature or request label Mar 31, 2022
@rylev
Copy link
Contributor

rylev commented Mar 31, 2022

I think an FAQ is a good idea. I will note that we used to have an FAQ and we may wish to bring that back in some form.

I will also note that it is a common and well received practice to have markdown "book-style" tutorials for crates. The serde crate for instance has a book as their guide: https://serde.rs/. An FAQ could be a great place for the most frequent of frequently asked questions, but it would also be helpful to have a longer form guide for many of the topics in windows-rs. For example, I've had to help explain how COM works in windows-rs to some users who have really benefited from an explanation of the fundamentals of how COM concepts are translated into Rust. This would be a perfect example of something that would be at home in guide.

@kennykerr
Copy link
Collaborator Author

Yep, it's a fine line but I would like to avoid getting into guide/book/doc format until we're confident things are relatively settled and I can afford to commit resources to do that well. The old FAQ was just a little too early and so much has changed, but windows-rs is now much more mature so I think it is time to revisit. The FAQ I have in mind is really meant to be very short explainers that mostly just point to a working example in the samples folder to avoid the FAQ becoming stale with code snippets that cannot be checked by the build. So just a concise paragraph with a pointer to a sample.

@wesleywiser
Copy link

How are optional COM interfaces handled?

+1 from me for having some of these kinds of questions answered. I just ran into that two days ago and was bugging @rylev for answers 😄

In general, perhaps a table that maps common idioms from C++/COM to Rust/windows-rs/COM would be helpful? I can try to write down what I've learned over the last week if that would be helpful.

@riverar
Copy link
Collaborator

riverar commented Mar 31, 2022

I spent a bunch of time in Rust Discord communities and some recent ones I've helped folks with:

  • What the heck is this IntoParam<...>?
  • How do I handle strings (constants, dynamically allocated, utf-8/16/32, ansi, null terminated, not null terminated, A/W APIs)?
  • How do I know when I do/do not need alloc?
  • How do I pass a negative integer as my array length for this funky API?
  • Why are you missing API X?
  • What's the idiomatic Windows+Rust way to handle errors from Win32 APIs (HRESULTs, bool, etc.)?
  • How do I create a VARIANT? (What is this ManuallyDrop/union mess?)
  • How do I deploy files with my app?
  • Why is T a u32 while the function needs i32? (Metadata bug)
  • How do I work with IAsyncOperation (async in general)?

@wesleywiser
Copy link

Ok, since I got a thumbs up, this is the table I would loved to have had on Monday 🙂

C++ windows-rs Notes
_In_ IFooBar* ManuallyDrop<IFooBar> This is the "borrowed" COM object idiom in C++ where you are given a pointer to a COM object which is valid for the duration of your method call. The reason we use ManuallyDrop<> on the Rust side is because IFooBar is really akin to ComPtr<IFooBar> in C++ and so when the argument goes out of scope, Rust calls Drop::drop on the value which decrements the ref count, but there was no corresponding increment which isn't valid!
_In_opt_ IFooBar* ManuallyDrop<Option<IFooBar>> The same "borrowed" COM object idiom but the parameter is optional.
_In_ REFGUID *const windows::core::GUID Not that important but would have saved a lookup to make sure REFGUID was in fact a pointer to a GUID and not anything more exotic.
_COM_Outptr_ IFooBar** *mut IFooBar What you'd expect.
_COM_Outptr_opt_ IFooBar** *mut IFooBar Also *mut IFooBar since Rust pointers can be null.

Since my program seems to be working, I believe this is technically correct but perhaps not idiomatic.

@kennykerr
Copy link
Collaborator Author

Hey @wesleywiser that's a great example of something I plan to simplify so that it is just inherently less confusing. 😄

@GamePad64
Copy link
Contributor

Links to docs.microsoft.com for functions, structs, unions, enums and constants would be really useful

@kennykerr
Copy link
Collaborator Author

Links to docs.microsoft.com for functions, structs, unions, enums and constants would be really useful

That's a good suggestion and something I've been wanting to add to the generated docs, but not something that would be part of an FAQ. 😉 Feel free top to open a new issue for it.

@tim-weis
Copy link
Contributor

tim-weis commented Apr 1, 2022

Here's another one I predict will get asked:

@kennykerr
Copy link
Collaborator Author

The other question is what format the FAQ should take. FAQs tend to be long markdown docs and while that works well enough, it has a few challenges. It ends up as part of the "source code" for the repository, is more work to maintain and comment on as PRs are required, a TOC needs to be maintained, and so forth. GitHub wiki and discussions are other potential solutions but have their own drawbacks.

I was thinking of the FAQ being a set of GitHub issues with an "FAQ" label. The issue's title would form the “question” and the original issue body would provide the “answer”. Folks can then comment on the entry and suggest improvements and the author (or other maintainers) can polish and just generally keep it up to date. We could have an FAQ markdown doc that simply provides links to the various FAQ issues or simply an FAQ link on the repo's readme that points to a query for all the FAQ issues like:

https://github.com/microsoft/windows-rs/issues?q=label%3Afaq

Obviously, the repo maintainers (regular contributors) would exercise oversight over the process to ensure that the FAQ issues are relevant and high quality. We would need to decide which issues get the "FAQ" label, but anyone could put one forward as a candidate for the FAQ. The main difficulty is having people accidentally - or mischievously 😉 - add the FAQ label to other issues.

@Zingam
Copy link

Zingam commented Apr 2, 2022

  • How do I choose between the windows and windows-sys crates

This is a very (most) important question really, which needs a good explanation!

@ryancerium
Copy link
Contributor

Q: Why is the type of FrobnosticateEx()'s third parameter a u32 instead of a HFROB?
A: Because the Win32 metadata project needs your help! Please file an issue there and they will fix it, they're super helpful.

@kennykerr
Copy link
Collaborator Author

I've moved some of the outstanding questions (without answers) from the FAQ here to avoid confusion:

  • How are optional COM interfaces handled?
  • Calling Windows APIs
  • What is IntoParam<...>?
  • What's the idiomatic Windows and Rust way to handle errors from Win32 APIs (HRESULT, BOOL, etc.)?
  • How do I handle the various string types Windows uses?
  • What is the alloc feature for?
  • How do I create a VARIANT?
  • Why does MSDN say a function takes an argument of type X but the windows crate says it takes an argument of type Y?
  • How do I work with IAsyncOperation (async in general)?

We can move them back as we have the answers written up.

@FreddyDgh
Copy link

@kennykerr Maybe this is just because I've been following this project since the winrt-rs days, but I've always had these two fundamental questions:

  1. How do I create a custom WinRT component / how do I create a WinMD file
  2. How do I consume a custom WinRT component / how do I consume a WinMD file

However, since the focus of this repo has switched more toward pre-generated bindings for the Windows API, I have begun to wonder if those questions are not even primary concerns. Perhaps in addition to the FAQ, it might be nice if there was a roadmap for this repo that gave at least some information on what features windows-rs expects to have, the current status of those features, and so on. More and more, I'm finding it unclear as to what the long-term vision is for the repo and its crates.

@rgwood
Copy link

rgwood commented May 24, 2022

A question I've had multiple times: When do I need to manually free memory, and how should I do it? This is is a complex area, but if it's possible to provide some high-level guidance that would be useful.

Examples

When I started using COM it wasn't clear to me whether I needed to manually call IUnknown::Release(). Eventually I dug through the source and concluded that IUnknown has a Drop implementation that automatically calls Release(); this would be useful to document.

Later I started writing code like this:

let item2: IShellItem2 = item.cast()?;
// does this variant need to be freed?
let original_location_variant: PROPVARIANT = item2.GetProperty(&SCID_ORIGINAL_LOCATION)?;
// does this BSTR need to be freed?
let original_location_bstr: BSTR = PropVariantToBSTR(&original_location_variant)?;

I spent a while looking through the PROPVARIANT and BSTR source and I'm still not sure if that code leaks memory; there's a lot going on with ManuallyDrop and ::core::ops::Drop, and understanding all of it is beyond my current ability in Rust.

@rgwood
Copy link

rgwood commented Jun 5, 2022

Another question I've had: How can I call methods with out parameters?

Out parameters are common in Win32 APIs but relatively rare in Rust, and IMO it's not obvious how to use them. I eventually ended up settling on a pattern like:

let mut foo = MaybeUninit::<SomeType>::uninit();
SomeWindowsFunction(foo.as_mut_ptr())?;
let foo = foo.assume_init();

@riverar
Copy link
Collaborator

riverar commented Jun 5, 2022

@rgwood Or

let mut foo = Foo::default();
Function(foo.as_mut_ptr())?;

@kennykerr
Copy link
Collaborator Author

Going to close this issue as we now have an FAQ and while its still quite sparse, it covers the most common questions I hear. If there are other pressing questions you think should be added, feel free to open a new issue.

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

10 participants