メッセージボックス表示コマンド ver 2.0
前回のものに以下の修正を加えました。
- MessageBoxTimeout() に対応。-t コマンドライン引数でタイムアウトまでの時間(ミリ秒単位)を指定できます。 (この関係でコマンドライン引数で以前は -tタイトル だったところを -cキャプション に変更。)
- コマンドライン引数でメッセージが指定されなかった場合は標準入力からメッセージを取得。
戻り値(環境変数 %ERRORLEVEL% に設定される値)
ID | 値 | WINVER |
---|---|---|
IDOK | 1 | - |
IDCANCEL | 2 | - |
IDABORT | 3 | - |
IDRETRY | 4 | - |
IDIGNORE | 5 | - |
IDYES | 6 | - |
IDNO | 7 | - |
IDCLOSE | 8 | 0x0400以上 |
IDHELP | 9 | 0x0400以上 |
IDTRYAGAIN | 10 | 0x0500以上 |
IDCONTINUE | 11 | 0x0500以上 |
IDTIMEOUT | 32000 | 0x0501以上 |
注意事項1
MessageBoxTimeout() は Windows XP から user32.dll に搭載されている Undocumented API です。 Windows XP 以降に対応している winuser.h にはこの API で使う為の IDTIMEOUT が定義されているし、 user32.lib には MessageBoxTimeoutA() 及び MessageBoxTimeoutW() の定義があるから関数宣言するだけで使えるかとも思ったのですが、どうもうまくいかず GetProcAddress() で API の呼び出しアドレスを取得しています。
注意事項2
リダイレクトやパイプの機能でメッセージを渡す場合は問題ないけど、コンソールでキーボードを叩いて標準入力へメッセージを渡した場合はメッセージボックスにフォーカスが当たらないので注意。この現象が再現するのはこのコマンドの動作確認やってる時だけで実用上は問題がないんで、特に原因を追及して直すつもりもありません。恐らくは親コンソールのGUIスレッドにアタッチすれば解決するんじゃなかろうかと思ってはいるんだけど、相手が通常のウィンドウ制御の及ばないコンソールウィンドウなのでちとやっかいかも。
ソースコード
////////////////////////////////////////////////////////////////////////////// // // includes // #if !defined(WINVER) #define WINVER 0x0501 #endif #if !defined(_WIN32_WINNT) #define _WIN32_WINNT WINVER #endif #include <assert.h> #include <stdio.h> #include <stdlib.h> #include <windows.h> #include <wincon.h> #include <string> #include <iostream> #include <iterator> #if defined(_MSC_VER) #pragma comment(lib, "USER32.lib") #pragma comment(lib, "SHELL32.lib") #endif /////////////////////////////////////////////////////////////////////////////// // // main // int main(int, char *[]) { HWND parent = #if 0x0500 <= WINVER GetConsoleWindow(); #else NULL; #endif std::wstring title = L"msgbox"; std::wstring message; DWORD icon = MB_ICONINFORMATION; DWORD button = MB_OK; #if 0x0501 <= WINVER DWORD timeout = 0; typedef int (*MessageBoxTimeoutW_type)(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType, WORD wLanguageId, DWORD dwTimeout); MessageBoxTimeoutW_type MessageBoxTimeoutW = NULL; #endif int argc; LPWSTR *args = CommandLineToArgvW(GetCommandLineW(), &argc); try { int i = 1; while(i < argc) { if (L'-' == args[i][0]) { switch(args[i][1]) { case L'I': case L'i': switch(args[i][2]) { case L'I': case L'i': icon = MB_ICONINFORMATION; break; case L'E': case L'e': icon = MB_ICONEXCLAMATION; break; case L'S': case L's': icon = MB_ICONSTOP; break; case L'Q': case L'q': icon = MB_ICONQUESTION; break; default: throw L"不明なオプションが指定されました。"; } break; case L'B': case L'b': switch(args[i][2]) { case L'A': case L'a': button = MB_ABORTRETRYIGNORE; break; case L'C': case L'c': button = MB_OKCANCEL; break; case L'Y': case L'y': button = MB_YESNO; break; case L'N': case L'n': button = MB_YESNOCANCEL; break; case L'O': case L'o': button = MB_OK; break; case L'R': case L'r': button = MB_RETRYCANCEL; break; #if 0x0500 <= WINVER case L'T': case L't': button = MB_CANCELTRYCONTINUE;break; #endif default: throw L"不明なオプションが指定されました。"; } break; #if 0x0501 <= WINVER case L'T': case L't': timeout = _wtol(args[i] +2); if (timeout) { HMODULE user32_lib = LoadLibraryA("user32.dll"); MessageBoxTimeoutW = (MessageBoxTimeoutW_type)GetProcAddress(user32_lib, "MessageBoxTimeoutW"); FreeLibrary(user32_lib); // ↑DLLは参照カウンタを持っており、C++ランタイムライブラリによる // LoadLibrary() 呼び出し分のカウントがあるので少なくとも main() // を抜けるまでは user32.dll はアンロードされない。 } break; #endif case L'C': case L'c': title = args[i] +2; break; default: throw L"不明なオプションが指定されました。"; } } else { break; } ++i; } if (argc <= i) { // コマンドライン引数でメッセージが指定されていないので標準入力からメッセージを取得 #if defined(_MSC_VER) && 1300 <= _MSC_VER std::wcin.imbue(std::locale("")); message.assign(std::istreambuf_iterator<wchar_t>(std::wcin), std::istreambuf_iterator<wchar_t>()); #else std::string buffer; #if defined(__BORLANDC__) buffer.assign(std::istreambuf_iterator<char>(std::cin), std::istreambuf_iterator<char>()); #else std::istreambuf_iterator<char> cin_i(std::cin), cin_end; while(cin_i != cin_end) { buffer += (*cin_i); ++cin_i; } #endif if (2 <= buffer.size() && '\xFF' == buffer[0] && '\xFE' == buffer[1]) { message.assign(((LPCWSTR)buffer.data()) +1, (buffer.size() /sizeof(WCHAR)) -1); } else { size_t wbuffer_size = buffer.size() +16; WCHAR *wbuffer = new WCHAR[wbuffer_size]; MultiByteToWideChar(CP_ACP, 0, buffer.c_str(), -1, wbuffer, wbuffer_size); message.assign(wbuffer); } #endif } else { // コマンドライン引数で指定されているメッセージを取得 while(i < argc) { message += args[i]; message += L"\n"; ++i; } } } catch(const wchar_t *a_message) { title = GetCommandLineW(); message = a_message; message += L"\n"; message += L"\n"; #if 0x0501 <= WINVER message += L"msgbox [-ii|-ie|-is-|-iq] [-ba|-bc|-by|-bn|-bo|-br|-bt] [-tタイムアウトまでの時間(ミリ秒)] [-cキャプション] [メッセージ1 [メッセージ2...]]"; #elif 0x0500 <= WINVER message += L"msgbox [-ii|-ie|-is-|-iq] [-ba|-bc|-by|-bn|-bo|-br|-bt] [-cキャプション] [メッセージ1 [メッセージ2...]]"; #else message += L"msgbox [-ii|-ie|-is-|-iq] [-ba|-bc|-by|-bn|-bo|-br] [-cキャプション] [メッセージ1 [メッセージ2...]]"; #endif icon = MB_ICONSTOP; button = MB_OK; } LocalFree(args); return #if 0x0501 <= WINVER (MessageBoxTimeoutW) ? MessageBoxTimeoutW(parent, message.c_str(), title.c_str(), icon |button, 0, timeout): #endif MessageBoxW(parent, message.c_str(), title.c_str(), icon |button); }