##環境
toolchain: stable-i686-pc-windows-msvc
rustc 1.32.0 (9fda7c223 2019-01-16)
winapi 0.3.6
##最小化でタスクトレイに収納
前回ウィンドウを表示したので、そのウィンドウを最小化するとタスクトレイにアイコンとして格納されるようにしてみます。タスクトレイのアイコンを左クリックすると、ウィンドウが再表示されます。
Cargo.toml の features に"shellapi"
を追加します。
[dependencies.winapi]
version = "0.3"
features = ["winuser", "shellapi"]
main.rs に shellapi module を追加。
use winapi::{
um::{
winuser::{RegisterClassW, WNDCLASSW, CS_HREDRAW, CS_VREDRAW,
LoadIconW, IDI_APPLICATION, LoadCursorW, IDC_ARROW,
CreateWindowExW, ShowWindow, SW_NORMAL, SW_HIDE, UpdateWindow,
GetMessageW, TranslateMessage, DispatchMessageW, MSG,
WM_DESTROY, PostQuitMessage, DefWindowProcW, WS_OVERLAPPEDWINDOW,
WM_USER, WM_SYSCOMMAND, SC_MINIMIZE, WM_LBUTTONDOWN},
wingdi::{GetStockObject, WHITE_BRUSH},
shellapi::{Shell_NotifyIconW, NOTIFYICONDATAW, NIM_ADD, NIM_DELETE,
NIF_ICON, NIF_MESSAGE, NIF_TIP},
},
shared::{
windef::{HWND, HBRUSH},
minwindef::{UINT, WPARAM, LPARAM, LRESULT},
},
};
use std::ptr;
use std::mem;
タスクトレイアイコンのメッセージ識別子(?)と識別用のIDを定義し、NOTIFYICONDATA
構造体へのポインタをグローバル変数として持っておきます。
const MYMSG_TRAY: UINT = WM_USER + 1;
const ID_MYTRAY: UINT = 58091;
static mut P_NID: *mut NOTIFYICONDATAW = ptr::null_mut();
main
関数にNOTIFYICONDATA
構造体を初期化するコードを追加します。
fn main() {
unsafe {
let class_name = encode("my_window_class_name");
if !register_wndclass(&class_name) {
return;
}
let hwnd = create_window(&class_name);
if hwnd.is_null() {
return;
}
let mut nid = create_nid(hwnd); //追加
P_NID = &mut nid; //追加
ShowWindow(hwnd, SW_NORMAL);
UpdateWindow(hwnd);
let mut msg = mem::uninitialized::<MSG>();
loop {
if GetMessageW(&mut msg, ptr::null_mut(), 0, 0) == 0 {
return;
}
TranslateMessage(&mut msg);
DispatchMessageW(&mut msg);
}
}
}
create_nid
関数の中身です。例によって、std::mem::zeroed()
で構造体を初期化して、必要なメンバだけに値を設定してきます。szTip
は[u16; 128]
の配列にNULL終端文字列を入れる必要がありますが、Rust ではどうやると良いのでしょう?
とりあえずサンプルでは、[0u16; 128]
で 0 で初期化された配列を用意しておいて、std::ptr::copy()
で UTF-16 にエンコードした文字列を配列にコピーするというやり方で対応しました。
unsafe fn create_nid(hwnd: HWND) -> NOTIFYICONDATAW {
let mut nid = mem::zeroed::<NOTIFYICONDATAW>();
nid.cbSize = mem::size_of::<NOTIFYICONDATAW>() as u32;
nid.uID = ID_MYTRAY;
nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
nid.hWnd = hwnd;
nid.uCallbackMessage = MYMSG_TRAY;
nid.hIcon = LoadIconW(ptr::null_mut(), IDI_APPLICATION);
let mut buf = [0u16; 128];
let tip = "Hello, World!";
ptr::copy(encode(tip).as_ptr(), &mut buf[0], tip.len());
nid.szTip = buf;
nid
}
最後にウィンドウプロシージャです。最小化したらアイコンをタスクトレイに表示してウィンドウを非表示に、タスクトレイのアイコンを左クリックしたらアイコンを削除してウィンドウを再表示するという処理です。終了時にタスクトレイにアイコンが残らないように、WM_DESTROY
のところでアイコンを消すようにしています。
NOTIFYICONDATA
構造体のポインタをグローバル変数として準備しておいたのは、下記のようにウィンドウプロシージャ内でShell_NotifyICon
関数に引数として渡す必要があったからです。
unsafe extern "system" fn win_proc(hwnd: HWND, msg: UINT, w_param: WPARAM, l_param: LPARAM) -> LRESULT {
match msg {
WM_SYSCOMMAND => {
match w_param {
SC_MINIMIZE => {
Shell_NotifyIconW(NIM_ADD, P_NID);
ShowWindow(hwnd, SW_HIDE);
},
_ => return DefWindowProcW(hwnd, msg, w_param, l_param),
};
},
MYMSG_TRAY => {
match (w_param as u32, l_param as u32) {
(ID_MYTRAY, WM_LBUTTONDOWN) => {
Shell_NotifyIconW(NIM_DELETE, P_NID);
ShowWindow(hwnd, SW_NORMAL);
},
_ => return DefWindowProcW(hwnd, msg, w_param, l_param),
}
},
WM_DESTROY => {
Shell_NotifyIconW(NIM_DELETE, P_NID);
PostQuitMessage(0);
},
_ => return DefWindowProcW(hwnd, msg, w_param, l_param),
};
0
}
main.rs をまとめてどうぞ。
use winapi::{
um::{
winuser::{RegisterClassW, WNDCLASSW, CS_HREDRAW, CS_VREDRAW,
LoadIconW, IDI_APPLICATION, LoadCursorW, IDC_ARROW,
CreateWindowExW, ShowWindow, SW_NORMAL, SW_HIDE, UpdateWindow,
GetMessageW, TranslateMessage, DispatchMessageW, MSG,
WM_DESTROY, PostQuitMessage, DefWindowProcW, WS_OVERLAPPEDWINDOW,
WM_USER, WM_SYSCOMMAND, SC_MINIMIZE, WM_LBUTTONDOWN},
wingdi::{GetStockObject, WHITE_BRUSH},
shellapi::{Shell_NotifyIconW, NOTIFYICONDATAW, NIM_ADD, NIM_DELETE,
NIF_ICON, NIF_MESSAGE, NIF_TIP},
},
shared::{
windef::{HWND, HBRUSH},
minwindef::{UINT, WPARAM, LPARAM, LRESULT},
},
};
use std::ptr;
use std::mem;
const MYMSG_TRAY: UINT = WM_USER + 1;
const ID_MYTRAY: UINT = 58091;
static mut P_NID: *mut NOTIFYICONDATAW = ptr::null_mut();
fn main() {
unsafe {
let class_name = encode("my_window_class_name");
if !register_wndclass(&class_name) {
return;
}
let hwnd = create_window(&class_name);
if hwnd.is_null() {
return;
}
let mut nid = create_nid(hwnd);
P_NID = &mut nid;
ShowWindow(hwnd, SW_NORMAL);
UpdateWindow(hwnd);
let mut msg = mem::uninitialized::<MSG>();
loop {
if GetMessageW(&mut msg, ptr::null_mut(), 0, 0) == 0 {
return;
}
TranslateMessage(&mut msg);
DispatchMessageW(&mut msg);
}
}
}
fn encode(source: &str) -> Vec<u16> {
source.encode_utf16().chain(Some(0)).collect()
}
unsafe fn register_wndclass(class_name: &[u16]) -> bool {
let mut winc = mem::zeroed::<WNDCLASSW>();
winc.style = CS_HREDRAW | CS_VREDRAW;
winc.lpfnWndProc = Some(win_proc);
winc.hIcon = LoadIconW(ptr::null_mut(), IDI_APPLICATION);
winc.hCursor = LoadCursorW(ptr::null_mut(), IDC_ARROW);
winc.hbrBackground = GetStockObject(WHITE_BRUSH as i32) as HBRUSH;
winc.lpszClassName = class_name.as_ptr();
RegisterClassW(&winc) > 0
}
unsafe fn create_window(class_name: &[u16]) -> HWND {
CreateWindowExW(
0,
class_name.as_ptr(),
encode("Hello, World!").as_ptr(),
WS_OVERLAPPEDWINDOW,
0,0, 200, 200,
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
)
}
unsafe fn create_nid(hwnd: HWND) -> NOTIFYICONDATAW {
let mut nid = mem::zeroed::<NOTIFYICONDATAW>();
nid.cbSize = mem::size_of::<NOTIFYICONDATAW>() as u32;
nid.uID = ID_MYTRAY;
nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
nid.hWnd = hwnd;
nid.uCallbackMessage = MYMSG_TRAY;
nid.hIcon = LoadIconW(ptr::null_mut(), IDI_APPLICATION);
let mut buf = [0u16; 128];
let tip = "Hello, World!";
ptr::copy(encode(tip).as_ptr(), &mut buf[0], tip.len());
nid.szTip = buf;
nid
}
unsafe extern "system" fn win_proc(hwnd: HWND, msg: UINT, w_param: WPARAM, l_param: LPARAM) -> LRESULT {
match msg {
WM_SYSCOMMAND => {
match w_param {
SC_MINIMIZE => {
Shell_NotifyIconW(NIM_ADD, P_NID);
ShowWindow(hwnd, SW_HIDE);
},
_ => return DefWindowProcW(hwnd, msg, w_param, l_param),
};
},
MYMSG_TRAY => {
match (w_param as u32, l_param as u32) {
(ID_MYTRAY, WM_LBUTTONDOWN) => {
Shell_NotifyIconW(NIM_DELETE, P_NID);
ShowWindow(hwnd, SW_NORMAL);
},
_ => return DefWindowProcW(hwnd, msg, w_param, l_param),
}
},
WM_DESTROY => {
Shell_NotifyIconW(NIM_DELETE, P_NID);
PostQuitMessage(0);
},
_ => return DefWindowProcW(hwnd, msg, w_param, l_param),
};
0
}
また何かネタを思いついたら投稿します。
今までのネタ
Rust で Windows プログラミング - MessageBox編
Rust で Windows プログラミング - CreateWindow編