2018年01月07日

Rust言語でWin32apiしてみたメモ 2018

Rust言語でWin32apiしてみたメモ 2018
はじまり
Rust言語の練習のためWin32APIのファイルオープンダイアログを起動する
簡単なソフトを作ってみたメモ。

Rust winapiのページ
https://github.com/retep998/winapi-rs

今時、Win32API直たたきはアレだけど、一応やってみた。
./target/debug/win32_fopen_dlg.exe
or
./target/release/win32_fopen_dlg.exe
をコマンドラインから実行してファイルを選択すると、ファイル名を表示するだけ。
ファイル未選択でキャンセルすると別なメッセージが出るだけ。
なソフト。

以下のソースコードは日本語を含むので「UTF-8」で保存する必要がある。
src/main.rs
/*
 * Windows File open dialog demo.
 * rustc 1.22.1 (05e2e1c41 2017-11-22): stable.
 * This file must be saved as UTF-8 format.
 *
 * 2018/01/02 by audin
 */
#![windows_subsystem = "windows"]

extern crate winapi;
extern crate user32;

use std::env;
use std::ffi::OsStr;
use std::iter::once;
use std::os::windows::ffi::OsStrExt;
use std::ptr::null_mut;
use std::mem::size_of;
use winapi::um::commdlg::{OPENFILENAMEW, OFN_HIDEREADONLY};
use winapi::um::commdlg::GetOpenFileNameW;
use std::result;

const SZW_SIZE: usize = 512;
// Type alias to simplify representation.
type Result<T> = result::Result<T,String>;
type TSzwBuf   = [u16; SZW_SIZE];

// Open the fileopen dialog with Windows GUI.
#[allow(non_snake_case)]
fn file_open_dialog() -> Result<String> {
    let szwFilter = str_to_szw("プログラム:(*.exe)\0*.exe\0すべて:(*)\0*\0");
    let szwTitle  = str_to_szw("ファイルを選択します");
    let mut szw_buf: TSzwBuf = [0; SZW_SIZE];
    let mut dlgOpen = OPENFILENAMEW {
        lStructSize:       size_of::<OPENFILENAMEW>() as u32, //DWORD,
        hwndOwner:         null_mut(),                        // HWND,
        hInstance:         null_mut(),                        // HINSTANCE,
        lpstrFilter:       szwFilter.as_ptr(),                // LPCWSTR,
        lpstrCustomFilter: null_mut(),                        // LPWSTR,
        nMaxCustFilter:    0,                                 // DWORD,
        nFilterIndex:      0,                                 // DWORD,
        lpstrFile:         szw_buf.as_mut_ptr(),              // LPWSTR,
        nMaxFile:          szw_buf.len() as u32,              // DWORD,
        lpstrFileTitle:    null_mut(),                        // LPWSTR,
        nMaxFileTitle:     0,                                 // DWORD,
        lpstrInitialDir:   null_mut(),                        // LPCWSTR,
        lpstrTitle:        szwTitle.as_ptr(),                 // LPCWSTR,
        Flags:             OFN_HIDEREADONLY,                  // DWORD,
        nFileOffset:       0,                                 // WORD,
        nFileExtension:    0,                                 // WORD,
        lpstrDefExt:       null_mut(),                        // LPCWSTR,
        lCustData:         0,                                 // LPARAM,
        lpfnHook:          None,                              // LPOFNHOOKPROC,
        lpTemplateName:    null_mut(),                        // LPCWSTR,
        pvReserved:        null_mut(),                        // *mut c_void,
        dwReserved:        0,                                 // DWORD,
        FlagsEx:           0,                                 // DWORD,
    };

    match unsafe { GetOpenFileNameW(&mut dlgOpen) } {
        0 => Err("Nothing is selected !".to_string()),
        _ => szw_to_string( &szw_buf ),
    }
}

// Convert from TSzwBuf([u16;512]) to String type.
fn szw_to_string( szwbuf: &TSzwBuf ) -> Result<String> {
    szwbuf.iter()
        .position(|wch| wch == &0)
        .ok_or("String : Can't find zero terminator !".to_owned())
        .and_then(|ix| String::from_utf16( &szwbuf[..ix] )
                              .map_err(|e| e.to_string()))
}

// Convert from String to Vec<u16> with trailing \0.
fn str_to_szw(str_body: &str) -> Vec<u16> {
    return OsStr::new(str_body)
        .encode_wide()
        .chain(once(0))  // 終端文字\0を追加
        .collect::<Vec<u16>>();
}

fn main() {

    let args = env::args().collect::<Vec<String>>();

    if args.len() >= 2 {
        println!("{}", &args[1]);
    }
    else {
        match file_open_dialog() {
            Ok(file_path) => println!("{}", file_path),
            Err(e)        => println!("{}", e),
        };
    }
}
上のコードの (1) 黄色い部分で、 コンソールウインドウを出さない様にしている。 (2) 赤い部分は、winapi 0.3.0以降で変更になった書き方。 winapi 0.2.8 までは use winapi::GetOpenFileNameW; の様に短く書けたんだけど winapi 0.3.0以降は上記のフル指定で書く必要がある。 さらに Cargo.tomlに追加設定が必要。 Cargo.toml
[package]
name = "win32_fopen_dlg"
version = "0.1.0"
authors = ["audin"]

[dependencies]
user32-sys="0.2.0"

[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3.3", features = ["winuser","commdlg"] }
上で、winapi 0.3.0以降を使う時は緑色の部分の追加が必要。 さらに赤い部分で、どのモジュールファイルを使うのかを指定する必要がある。 例えば、GetOpenFileNameW()というAPIの指定方法は、以下のページの一番上の行の検索欄に "GetOpenFileNameW"を入れる。 https://docs.rs/winapi/*/x86_64-pc-windows-msvc/winapi/index.html すると以下の様にフル指定方法がわかる。 https://docs.rs/winapi/*/x86_64-pc-windows-msvc/winapi/um/index.html?search=GetOpenFileNameW また例えば、 USB_DEVICE_SPEEDという定数を使いたい場合、 https://docs.rs/winapi/*/x86_64-pc-windows-msvc/winapi/shared/usbspec/type.USB_DEVICE_SPEED.html?search=USB_DEVICE_SPEED 上の様になるので、Cargo.tomlの featuresは、 features = ["winuser","commdlg","usbspec"] となり、ソースコードには use winapi::shared::usbspec::USB_DEVICE_SPEED; を追加する。 umとかsharedの様な上位階層はfeaturesに指定する必要はない様だ。 native-windows-gui Win32APIのGUIラッパーを作った人がいました。 https://github.com/gabdube/native-windows-gui gitでクローンして Cargo.tomlのあるフォルダで、 cargo run --example showcase cargo run --example canvas cargo run --example templating などと、exapmlesフォルダの中のファイルをコンパイルすると、 ./target/debug/examples/*.exe ができるので実行する。 iniファイルを操作するライブラリ ライブラリというかクレイトと言うらしい。 rust-ini https://crates.io/crates/rust-ini gitリポ https://github.com/zonyitoo/rust-ini 使い方 https://docs.rs/rust-ini/0.10.0/ini/ Windowsのレジストリを操作するライブラリ winreg https://crates.io/crates/winreg gitリポ https://github.com/gentoo90/winreg-rs 試したところちゃんと動きました。
posted by Copyright (C) avrin All Rights Reserved. at 09:01| Comment(0) | Rust言語 | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前: [必須入力]

メールアドレス:

ホームページアドレス:

コメント: [必須入力]

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。