ユーニックス総合研究所

  • home
  • archives
  • windows-rs-message-box-types

メッセージボックスの型と構造体 - Rustで作るWindowsアプリ

  • 作成日: 2023-05-04
  • 更新日: 2023-12-24
  • カテゴリ: windows-rs

メッセージボックスで使われる型について

先ほどのMessageBoxW()の関数で見慣れない型がいくつか出てきました。それは以下のようなものです。

  • HWND
  • PCWSTR
  • MESSAGEBOX_STYLE
  • MESSAGEBOX_RESULT

Windows APIでは型が高度に抽象化されています。ですのでこのような型がたくさんあるわけです。

windows-rsで定義される文字列の種類

windows-rswindowsクレートには以下のような文字列が準備されています。

  • BSTR ... 長さがプレフィックスされたワイド文字列
  • HSTRING ... 参照カウントされる不変文字列
  • PCSTR ... ナル文字が付加された8ビット定数文字列へのポインタ
  • PCWSTR ... ナル文字が付加された16ビット ユニコード定数文字列へのポインタ
  • PSTR ... ナル文字が付加された8ビット文字列へのポインタ
  • PWSTR ... ナル文字が付加された16ビット ユニコード文字列へのポインタ

この内、メッセージボックスで使用されるPCWSTRの文字列はポインタをラップしたタプル構造体です。

Struct windows::core::PCWSTR  

#[repr(transparent)]  
pub struct PCWSTR(pub *const u16);  

このPCWSTRにはfrom_rawメソッドやas_wideメソッドなどが定義されています。

HWNDの正体

HWNDはWindows APIではウィンドウ・ハンドルと呼ばれるものになります。ウィンドウを識別するための整数がこのHWNDです。
Windowsのウィンドウにはこのようなハンドルが存在していて、このハンドルを通じてウィンドウを操作することができます。ですのでWindows APIプログラミングではこのHWNDはかなり大事なものになります。

windows-rsではHWNDはタプル型構造体として定義されています。

Struct windows::Win32::Foundation::HWND  

#[repr(transparent)]  
pub struct HWND(pub isize);  

このHWNDisizeのラッパーになっています。isizeはRustのプリミティブ型です。符号付き整数で、32ビットのターゲットでは4バイト、64ビットのターゲットでは8バイトになります。
このHWNDにはclone()なども実装されています。以下の様なコードでHWNDの動作を確認できます。

[dependencies.windows]  
version = "0.48"  
features = [  
    "Win32_Foundation",  
]  
use windows::{  
    Win32::Foundation::HWND,  
};  

fn main() {  
    let h: HWND = HWND(10);  
    let hh: HWND = HWND::default();  
    let hhh: HWND = hh.clone();  

    println!("{} {} {}", h.0, hh.0, hhh.0);  
    // 10 0 0  
}  

MESSAGEBOX_STYLE

MessageBoxの最後の引数がこれですが、これにはメッセージボックスのスタイルを設定します。例えばMB_OKと指定するとOKボタンが表示されるメッセージボックスが出ます。
MESSAGEBOX_STYLEもタプル型構造体として定義されています。

Struct windows::Win32::UI::WindowsAndMessaging::MESSAGEBOX_STYLE  

#[repr(transparent)]  
pub struct MESSAGEBOX_STYLE(pub u32);  

これはu32のラッパーでu32はRustのプリミティブ型で32ビット(4バイト)の符号なし整数です。このタプル型構造体にはビット演算が実装されており、たとえば以下の様に複数の定数をスタイルに指定できます。

[dependencies.windows]  
version = "0.48"  
features = [  
    "Win32_Foundation",  
    "Win32_UI_WindowsAndMessaging",  
]  
use windows::{  
    core::*,  
    Win32::UI::WindowsAndMessaging::*,  
};  

fn main() {  
    unsafe {  
        MessageBoxW(  
            None, w!("Hello, World!"), w!("日本から"),  
            MB_OK | MB_ICONINFORMATION  
        );  
    }  
}  

MESSAGEBOX_STYLEはビット演算の他にも例えば以下のようなメソッドが実装されています。

impl Default for MESSAGEBOX_STYLE  
    // デフォルトのスタイルを返す  
    fn default() -> Self  

impl Clone for MESSAGEBOX_STYLE  
    // スタイルをクローンする  
    fn clone(&self) -> Self      

    // selfにsourceをクローンする  
    fn clone_from(&mut self, source: &Self)  

impl Not for MESSAGEBOX_STYLE  
    // !演算後の結果の型  
    type Output = MESSAGEBOX_STYLE  

    // !演算子  
    fn not(self) -> Self  

impl PartialEq<MESSAGEBOX_STYLE> for MESSAGEBOX_STYLE  
    // ==演算子  
    fn eq(&self, other: &MESSAGEBOX_STYLE) -> bool  

    // !=演算子  
    fn ne(&self, other: &Rhs) -> bool  

MESSAGEBOX_STYLEに使える定数には例えば以下があります。

ボタンに関する定数:

  • windows::Win32::UI::WindowsAndMessaging
  • MB_OK ... OKボタンの表示(デフォルト)
  • MB_OKCANCEL ... OKとキャンセルボタンの表示
  • MB_RETRYCANCEL ... 再試行とキャンセルボタンの表示
  • MB_YESNO ... はい、いいえボタンの表示
  • MB_YESNOCANCEL ... はい、いいえ、キャンセルボタンの表示

アイコンに関する定数(一部):

  • windows::Win32::UI::WindowsAndMessaging
  • MB_ICONINFORMATION .... 情報アイコンの表示(iが表示される)
  • MB_ICONQUESTION ... 疑問符(ハテナマーク)アイコンの表示
  • MB_ICONWARNING ... 感嘆符(ビックリマーク)アイコンの表示
  • MB_ICONSTOP ... 停止アイコンの表示
  • MB_ICONERROR ... 同上
use windows::{  
    core::*,  
    Win32::UI::WindowsAndMessaging::*,  
};  

fn main() {  
    let def: MESSAGEBOX_STYLE = MESSAGEBOX_STYLE::default();  
    let ok: MESSAGEBOX_STYLE = MB_OK | MB_ICONINFORMATION;  
    let not = !ok;  
    let cloned = ok.clone();  

    println!("{}", def.0);  // 0  
    println!("{}", ok.0);  // 64  
    println!("{}", not == def);  // false  
    println!("{}", not != def);  // true  
    println!("{}", cloned.0);  // 64  

    unsafe {  
        MessageBoxW(None, w!("hello"), w!("日本"), MB_OK);  
        MessageBoxW(None, w!("hello"), w!("日本"), MB_OKCANCEL);  
        MessageBoxW(None, w!("hello"), w!("日本"), MB_RETRYCANCEL);  
        MessageBoxW(None, w!("hello"), w!("日本"), MB_YESNO);  
        MessageBoxW(None, w!("hello"), w!("日本"), MB_YESNOCANCEL);  
        MessageBoxW(None, w!("hello"), w!("日本"), MB_ICONINFORMATION);  
        MessageBoxW(None, w!("hello"), w!("日本"), MB_ICONQUESTION);  
        MessageBoxW(None, w!("hello"), w!("日本"), MB_ICONWARNING);  
        MessageBoxW(None, w!("hello"), w!("日本"), MB_ICONSTOP);  
        MessageBoxW(None, w!("hello"), w!("日本"), MB_ICONERROR);  
    }  
}  

MESSAGEBOX_RESULT

MESSAGEBOX_RESULTはMessageBoxW()の返り値です。これは同様にタプル構造体で定義されています。

Struct windows::Win32::UI::WindowsAndMessaging::MESSAGEBOX_RESULT  

#[repr(transparent)]  
pub struct MESSAGEBOX_RESULT(pub i32);  

MESSAGEBOX_RESULTには以下のようなメソッドが定義されています。

impl Clone for MESSAGEBOX_RESULT  
    // クローンしてコピーを返す  
    fn clone(&self) -> Self  

    // selfにsourceをクローンする  
    fn clone_from(&mut self, source: &Self)  

impl Default for MESSAGEBOX_RESULT  
    // デフォルト値を返す  
    fn default() -> Self  

impl PartialEq<MESSAGEBOX_RESULT> for MESSAGEBOX_RESULT  
    // ==演算子  
    fn eq(&self, other: &MESSAGEBOX_RESULT) -> bool  

    // !=演算子  
    fn ne(&self, other: &Rhs) -> bool  

MESSAGEBOX_RESULTi32のラッパーですがi32は符号付きの32ビット(4バイト)整数です。
MESSAGEBOX_RESULTは以下のいずれかになります。

  • windows::Win32::UI::WindowsAndMessaging
  • IDOK ... 「OK」ボタンが押された
  • IDYES ... 「はい」ボタンが押された
  • IDNO ... 「いいえ」ボタンが押された
  • IDCANCEL ... 「キャンセル」ボタンが押された
  • IDRETRY ... 「再試行」ボタンが押された
  • IDABORT ... 「中止」ボタンが押された
  • IDIGNORE ... 「無視」ボタンが押された

IDOKIDYESMESSAGEBOX_RESULT型になっています。MessageBoxW()の結果もMESSAGEBOX_RESULTですのでこれらは比較演算子(eq, ne)で比較することができます。

use windows::{  
    core::*,  
    Win32::UI::WindowsAndMessaging::*  
};  

fn main() {  
    unsafe {  
        let result = MessageBoxW(None, w!("hello"), w!("日本"), MB_OKCANCEL);  
        println!("{} {} {}", result.0, IDOK.0, IDCANCEL.0);  // 1 1 2 (OKを押した場合)  

        if result == IDOK {  
            println!("OKが押されました。");  // OK!  
        } else if result == IDCANCEL {  
            println!("キャンセルしました。");  
        }  
    }  
}  

上記のコードをcargo runで実行するとOKボタンとキャンセルボタンのメッセージボックスが出ます。OKを押すと端末に「OKが押されました。」と表示されキャンセルを押すと「キャンセルしました。」と表示されます。