windows-rsのメモ

2021/01/23に公開

追記(2021/03/21)

雑多な新しい情報はスクラップのほうに書いています。

https://zenn.dev/lnseab/scraps/b714d8885b2bba

windows-rs

windows-rsはRust向けにWin32 APIの関数や構造体などを自動生成してくれるWin32 API本家本元のMicrosoftが作っているクレートで、クレート名はwindowsになっています。win32metadataで作られたwinmdファイルをwindows-rsbuildマクロでRustの関数や構造体を生成します。使い方はwindows-rsのGetting startedをご覧ください。
以下のIssueでwindows-rswinapi-rsを置き換えるように取り組んでいることがわかります。
https://github.com/microsoft/windows-rs/issues/419

ちなみにwinrt-rsを改名してwindows-rsになったようです。

winapi-rsとどっちがいいの?(0.7.0 2021/04/08の話)

windwos-rsが0.7.0において少なくとも自分が使った範囲ではとなりますが、ウィンドウやDirect2D、Direct3Dのようなよく使われるAPIではインポート時点でエラーが出るようなことはなく動作で問題がありませんでした。2021年1月の頃に比べると各段に使いやすくなっておりwinapi-rsの置き換えを考え始めてもいいのではないかと思います。また、winapi-rsは2020年11月の終わり頃を最後にコミットがなく足りないAPIが追加されるかどうかも正直わからないような状況です。したがって、winapi-rsよりwindows-rsを使っていく方向でいいのではないかと思います。ただし、windows-rsやwin32metadataのリリースは2021年末の見込み[1]で安定版ではない事を留意してください。

2021/01/23の話

2021/01/23においては、下記のようにコンパイル失敗することがあったりしてまだ安定していないのでwinapi-rsのほうが使いやすいと思います。しかし、winapi-rsは足りないAPIがあったり最新のWindows SDKに追従できていないので、現状でも自分でwindows-rsに修正を加えたりして対処できるならwindows-rsのほうが使える場面があるでしょう。win32metadataやwindows-rsが安定してくればwindows-rsを使うほうがよくなるかなと思いますが、win32metadataのリリースは2021年末の見込み[1:1]なので安定板はまだ待たなければなりません。

あれこれ

BOOL

BOOLwindows::BOOLで定義されており、is_okなどのRustに合わせた便利な関数が定義されていますが、is_okself.0 != 0is_errself.0 == 0となっているので、例えばGetMessageWのようなBOOL-1を返す関数では注意してください。
また、From<bool>が定義されているのでtrue.into()のようにintoで変換することができます。

bindings::Windows::Win32::SystemServices::BOOLになりました。
ok()Result<()>にできたり、From<bool>も定義されているのでintoboolに変換もできます。

返り値のHRESULT

返り値のHRESULTwindows::ErrorCodeになっており、is_okなどいくつかRustに合わせた便利な関数が定義されています。i32intoで変換することはできません。

NewTypeパターンの型

WPARAMLPARAMLRESULTなどの型がNewTypeパターンになっており、例えばLRESULT0を返そうする場合LRESULT(0)のようにする必要があります。

(2021/04/08更新) 以下のエラーはなくなりました

UCharIteratorなどの関数ポインタを持つ構造体でmapがないと言われる

windows::win32::intl::UCharIteratorのように*mut Option<関数ポインタの型>を持つ構造体でコンパイルが失敗します。Issueを出してみたところ、win32metadata側の問題のようです。
https://github.com/microsoft/windows-rs/issues/438
https://github.com/microsoft/win32metadata/issues/132

(2021/01/29追記)
windows-rs側でworkaroundを出そうという動きがあります。

https://github.com/microsoft/windows-rs/issues/444

api-ms-win-で始まるlibをリンクしようとして失敗する

winmdファイルにはWin32 APIの関数がどのDLLにあるか書かれているのですが、windows-rsがそのままDLL名を#[link(name = "...")]nameに入れて生成するために起こります。以下のwindows-rsのコメントによるとRustでDLLに対する動的リンクのサポートまでDLL名を対応したlib名に書く必要があるようです。

https://github.com/microsoft/windows-rs/blob/master/crates/gen/src/function.rs

// TODO: need to generate libs until Rust supports dynamic linking against DLLs.
// This is actually the DLL name

ちなみに上記のコメントの下に次のコードがあり、既に書かれたコードと同様に必要なDLLとlibの対応を書き足すとリンクを通せるようになります。

let mut link = self.signature.method.impl_map().unwrap().scope().name();
if link == "ext-ms-win-core-iuri-l1-1-0" {
    link = "urlmon";
} else if link == "api-ms-win-core-winrt-l1-1-0" {
    link = "onecoreuap";
}
脚注
  1. https://forest.watch.impress.co.jp/docs/news/1301910.html ↩︎ ↩︎

Discussion