メッセージボックス表示コマンド ver 2.0

msgbox


前回のものに以下の修正を加えました。

画面サンプル1

msgbox -t3000 -cサンプル 1行目 2行目 " " 4行目

画面サンプル2

whoami |msgbox -c"whoami"

戻り値(環境変数 %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);
}