2007-09-03

ダイアログベースのアプリケーションを作る

Visual Studioが自動生成したソースを元にダイアログベースのアプリケーションの作り方を調べてみました。
Todayプラグインで日本語入力させるのは諦めました...。
 
ポイントは 4つ、
  1. CreateWindowの代わりに CreateDialogを使う
  2. WNDCLASSの cbWndExtraに DLGWINDOWEXTRAを指定する
  3. メッセージループで IsDialogMessageを呼ぶ
  4. WM_INITDIALOGメッセージでダイアログを初期化する
 
1. CreateWindowの代わりに CreateDialogを使う
 
表示したいダイアログをリソースエディタで作成しておき、 CreateDialogで登録します。 このとき、ウィンドウプロシージャが DLGPROC型でないというエラーが出たのでキャストしてみました。
//hWnd = CreateWindow(szWindowClass, szTitle, WS_VISIBLE,
//    CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
g_hWnd = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), 0, (DLGPROC)WndProc);
 
 
2. WNDCLASSの cbWndExtraに DLGWINDOWEXTRAを指定する
 
この辺を読む限り、cbWndExtraに DLGWINDOWEXTRAを指定する必要があるみたいです。
WNDCLASS wc;

wc.style         = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc   = WndProc;
wc.cbClsExtra    = 0;
wc.cbWndExtra    = DLGWINDOWEXTRA;
wc.hInstance     = hInstance;
wc.hIcon         = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_DIALOGAPP));
wc.hCursor       = 0;
wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
wc.lpszMenuName  = 0;
wc.lpszClassName = szWindowClass;

return RegisterClass(&wc);
 
 
3. メッセージループで IsDialogMessageを呼ぶ
 
CreateDialogのドキュメントによると、ダイアログボックスの動作をサポートするために IsDialogMessageを呼ぶ必要があるそうです。
ただ、プラットホームSDKのドキュメントにはそのような記述がありましたが WM6 SDKのドキュメントには記述がありませんでした。 本当に必要なのでしょうか?
if (!g_hWnd || !IsDialogMessage(g_hWnd, &msg))
{
    if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) 
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}
 
 
4. WM_INITDIALOGメッセージでダイアログを初期化する
 
初期化は WM_CREATEではなく、WM_INITDIALOGで行います。
また、ダイアログの設定にはSHInitDialogを使用します。
 
自動生成されたソースの WM_CREATEで行っていた SHACTIVATEINFOの初期化もここにもって来ました。
case WM_INITDIALOG:
    {
        SHINITDLGINFO   shidi;

        shidi.dwMask = SHIDIM_FLAGS;
        shidi.dwFlags = SHIDIF_DONEBUTTON | SHIDIF_SIPDOWN | SHIDIF_SIZEDLGFULLSCREEN | SHIDIF_WANTSCROLLBAR;
        shidi.hDlg = hWnd;
        SHInitDialog(&shidi);

        // shell アクティベート情報のストラクチャを初期化します。
        memset(&s_sai, 0, sizeof (s_sai));
        s_sai.cbSize = sizeof (s_sai);

        break;
    }
 
 
後は不要な部分を削除する程度でダイアログを表示できるようになりました。
// DialogApp.cpp : アプリケーションのエントリ ポイントを定義します。
//

#include "stdafx.h"
#include "DialogApp.h"
#include <windows.h>
#include <commctrl.h>

#define MAX_LOADSTRING 100

// グローバル変数:
HINSTANCE           g_hInst;    // 現在のインターフェイス
HWND                g_hWnd;     // ウィンドウハンドル(追加)

// このコード モジュールに含まれる関数の宣言を転送します:
ATOM                MyRegisterClass(HINSTANCE, LPTSTR);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPTSTR    lpCmdLine,
                   int       nCmdShow)
{
    MSG msg;

    // アプリケーションの初期化を実行します:
    if (!InitInstance(hInstance, nCmdShow)) {
        return FALSE;
    }

    HACCEL hAccelTable;
    hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_DIALOGAPP));

    // メイン メッセージ ループ:
    while (GetMessage(&msg, NULL, 0, 0)) {
        if (!g_hWnd || !IsDialogMessage(g_hWnd, &msg)) {
            if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
        }
    }

    return (int) msg.wParam;
}

//
//  関数 : MyRegisterClass()
//
//  目的 : ウィンドウ クラスを登録します。
//
//  コメント:
//
ATOM MyRegisterClass(HINSTANCE hInstance, LPTSTR szWindowClass)
{
    WNDCLASS wc;

    wc.style         = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc   = WndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = DLGWINDOWEXTRA;
    wc.hInstance     = hInstance;
    wc.hIcon         = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_DIALOGAPP));
    wc.hCursor       = 0;
    wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName  = 0;
    wc.lpszClassName = szWindowClass;

 return RegisterClass(&wc);
}

//
//   関数: InitInstance(HINSTANCE, int)
//
//   目的 : インスタンス ハンドルを保存して、メイン ウィンドウを作成します。
//
//   コメント:
//
//        この関数で、グローバル変数でインスタンス ハンドルを保存し、
//        メイン プログラム ウィンドウを作成および表示します。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
    TCHAR szTitle[MAX_LOADSTRING];          // タイトル バー テキスト
    TCHAR szWindowClass[MAX_LOADSTRING];    // メイン ウィンドウ クラス名

    g_hInst = hInstance;                    // グローバル変数にインスタンス処理を格納します。

    // CAPEDIT および SIPPREF のようなデバイス固有のコントロールを初期化するには、アプリケーションの
    // 初期化中に SHInitExtraControls を一度呼び出す必要があります。
    SHInitExtraControls();

    LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); 
    LoadString(hInstance, IDC_DIALOGAPP, szWindowClass, MAX_LOADSTRING);

    //既に実行している場合は、ウィンドウにフォーカスを与え、終了します。
    g_hWnd = FindWindow(szWindowClass, szTitle); 
    if (g_hWnd) {
        // 最下位の子ウィンドウにフォーカスを設定します。
        // 所有するすべてのウィンドウを前に配置して、アクティブにするために "| 0x00000001"
        // が使用されました。
        SetForegroundWindow((HWND)((ULONG) g_hWnd | 0x00000001));
        return 0;
    } 

    if (!MyRegisterClass(hInstance, szWindowClass)) {
     return FALSE;
    }

    // CreateWindowの代わりに CreateDialogを使う
    g_hWnd = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), 0, (DLGPROC)WndProc);

    if (!g_hWnd) {
        return FALSE;
    }

    ShowWindow(g_hWnd, nCmdShow);
    UpdateWindow(g_hWnd);


    return TRUE;
}

//
//  関数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  目的 :  メイン ウィンドウのメッセージを処理します。
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int wmId, wmEvent;

    static SHACTIVATEINFO s_sai;
 
    switch (message) {
        // ダイアログ初期化
        case WM_INITDIALOG:
            {
                SHINITDLGINFO   shidi;

                shidi.dwMask = SHIDIM_FLAGS;
                shidi.dwFlags = SHIDIF_DONEBUTTON | SHIDIF_SIPDOWN | SHIDIF_SIZEDLGFULLSCREEN | SHIDIF_WANTSCROLLBAR;
                shidi.hDlg = hWnd;
                SHInitDialog(&shidi);

                // shell アクティベート情報のストラクチャを初期化します。
                memset(&s_sai, 0, sizeof (s_sai));
                s_sai.cbSize = sizeof (s_sai);

                break;
            }

        case WM_COMMAND:
            wmId    = LOWORD(wParam); 
            wmEvent = HIWORD(wParam); 
            // 選択されたメニューの解析:
            switch (wmId) {
                case IDOK:
                    SendMessage (hWnd, WM_CLOSE, 0, 0);
                    break;
                default:
                    return DefWindowProc(hWnd, message, wParam, lParam);
            }
            break;
        
        case WM_CLOSE:
            DestroyWindow(hWnd);
            break;

        case WM_DESTROY:
            PostQuitMessage(0);
            break;

        case WM_ACTIVATE:
            // メッセージのアクティベートを shell に通知します。
            SHHandleWMActivate(hWnd, wParam, lParam, &s_sai, FALSE);
            break;

        case WM_SETTINGCHANGE:
            SHHandleWMSettingChange(hWnd, wParam, lParam, &s_sai);
            break;

        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}
 

0 件のコメント: