W.I.S. Laboratory
menu-bar

Rust winapi


RustからWindowsAPIを叩いてGUIをやってみる

RustからWindowsAPIを叩いてGUIをやってみた。
使ったクレートはwinapiの0.3.9と、乱数生成のためにrandの0.8.4。
CでAPIを叩くときと同じく、CreateWindow関数でウィンドウを生成し、GetMessageまたはPeekMessage関数でメッセージを取得、DispatchMessage関数でウィンドウプロシージャへメッセージを送出、ウィンドウプロシージャでメッセージを受け取って処理を振り分けるという記述になる。

1. プロジェクトを作成したいフォルダへ移動して cargo new する

例)D:\Rust\ にプロジェクトを作成する場合
1. コマンドプロンプトを起動
2. 「D:」(エンター)
3. 「cd \Rust」(エンター)
4. 「cargo new winapi」(エンター)
5. 「cd winapi」(エンター)
6. 「cargo run」(エンター)
7. 「Hello World」が出てきたらOK
8. コマンドプロンプトは閉じずにそのままにしておく

2. プロジェクトフォルダ内の Cargo.toml の [dependencies] 以下を編集する。


※バージョン情報はコマンドプロンプトで「cargo search winapi」「cargo search rand」とすると各々確認できる。

3. srcフォルダ内の「main.rs」を編集する。


4. cargo run する

1. コマンドプロンプトで「cargo run」(エンター)

これは本当にRustか?(笑)
なんというか・・Cそのものという感じで、まったくRustっぽくない。
関数名も型名も windows.h ほぼそのままなので、ぱっと見は本当にCでAPIを叩いている感じがする。
ところどころ、型名がむき出しになっている箇所(*mut winapi::shared::windef::POINT など)があるのはコンパイラがそう書きなさいと言ってきたのでそのまま書いたものだ。

Cと比べてRustならではの点は以下のような感じ。

  • ウィンドウクラスやメッセージ、RECT構造体などの宣言をするときに必ず全フィールドの初期値を書かなくてはならない
  • リテラルの 0 を「as HWND」などとして関数が求める型にキャストしなくてはならない
  • API関数の戻り値も「as HANDLE」などとしてキャストする必要がある
  • VC++やMinGWなら「TEXT("...")」とすればコンパイラが必要に応じて適切にUTF-16変換などをしてくれるが、Rustは自前で変換関数を用意する必要がある
  • 結構ハマったのが、GetClientRect()関数でRECT構造体に現在のウィンドウサイズを取得する箇所だ。
    Cだとここは &rect として構造体のポインタを渡すところなので、「let mut rect = RECT {...} 」とした上で「&rect」をGetClientRectに渡したのだが、「types differ in mutability」エラーが出てしまう。
    変数をミュータブルにしているのに「ミュータブルにしろ」とコンパイラが言ってくるのだ。
    結局「let rect = &mut RECT {...}」として、構造体ではなくアドレスを変数に入れて型名の前に&mutを付けた上でGetClientRectに渡すとうまくいった。
    なのでCなら「rect.right」ではなく「rect->right」としてアクセスしなくてはいけないところだが、このあたりはさすがRust、「rect.right」であっさりとアクセスできる。
    参照(借用)で渡す場合は変数側ではなく型側をミュータブルにしなくてはならないというRustの基本で躓いていたわけだが、Cに慣れきった頭ではここにたどり着くのに随分時間を要した。
    Rustはこのミュータブル回りが結構難しいというかややこしい。(見慣れたAPI関数なのも要因かも)
    Rustを書いているとCが緩々な言語に思えてくる。

    マイクロソフトがRustからWindowsAPIを叩くためのクレートを公開するらしいので、これならもっと簡単に(Rustっぽく)APIを叩くことができそう。


    [ 戻る ]
    saluteweb