Programming Field - プログラミング Tips

ダイアログ: Win16 でメモリ上にテンプレートを作成

ここでは、Win16 版のダイアログ テンプレートを使って、メモリ上にダイアログ テンプレートを作成し、表示する例を示しています。リソースを読み込むプログラムを作る時などにも参考になると思います。

Win32 版のものは「メモリ上にテンプレートを作成」をご覧下さい

Win16 のダイアログ テンプレート

Win32 との大きな違いは、

  1. 拡張スタイル (dwExtendedStyle) が無い
  2. コントロールの最大数が 255 (cdit が BYTE なため)
  3. 文字列はすべて ANSI 文字列 (Unicode が無いため)

です。ただ、ほとんどの構造は変わり無いので、Win32 の時の方法ができるようであれば、これもそれほど難しくはありません。

以下は DLGTEMPLATE、DLGITEMTEMPLATE 構造体の定義です。

DLGTEMPLATE、DLGITEMTEMPLATE という定義はありません。よって、自分でヘッダーファイルに記述する必要があります。

typedef struct
{
    DWORD style;
    BYTE cdit;
    short x;
    short y;
    short cx;
    short cy;

    // <Menu ID>;
    // <Window Class>;
    // <Dialog Title>;
    // WORD fontSize;
    // CHAR fontName[];
    // DLGITEMTEMPLATE controls[cdit];
} DLGTEMPLATE, FAR* LPDLGITEMPLATE;
typedef struct
{
    // Win32 のものとは順番が違います
    short x;
    short y;
    short cx;
    short cy;
    WORD id;
    DWORD style;

    // <Window Class>;
    // <Control Text>;
    // BYTE SizeOfCreationData;
    // BYTE CreationData[SizeOfCreationData];
} DLGITEMTEMPLATE, FAR* LPDLGITEMTEMPLATE;

※ 上記の定義でコメントアウトしている部分は、実際には設定する内容によって型やサイズ・メモリ上での位置が変わってしまうため、明確な定義をすることが出来ません。

構造体のメンバ

style
ダイアログ、またはコントロールのスタイルです。(WS_ 、DS_ など)
cdit
ダイアログが持つコントロールの数です。(Count of DLGITEMTEMPLATE の略)
x, y, cx, cy
ダイアログ、またはコントロールのサイズです。このサイズはダイアログベース単位で指定する必要があります。ピクセルからこの単位に変換するには、GetDialogBaseUnits 関数を使う方法がありますが、本来はフォントのサイズを計算に含める必要があり、おすすめできません。
id
コントロールの ID です。なお、IDOK (= 1) を指定し、スタイルに BS_DEFPUSHBUTTON を指定すると既定のボタン (エディットコントロールで Enter をしたときに自動的に押されるボタン) に、IDCANCEL (= 2) を指定するとキャンセルボタン (ESC キーを押すと自動的に押されるボタン) になります。
<Menu ID>
メニューリソースの ID を指定します。整数値で指定する場合は、このメンバは BYTE + WORDとなり、最初 (BYTE) に 0xFF、2 番目 (WORD) に ID を指定します。
ID を文字列で指定する場合は、このメンバは NULL で終わる ANSI 文字列となります。NULL 文字の次の位置が、次のメンバの始点となります。
指定しない場合 (メニュー無し) は、BYTE サイズで 0 を指定します。(空の文字列と同じ)
<Window Class>
ウィンドウのクラスを指定します。指定方法は <Menu ID> と同じです。
ここでも整数値が指定できます。最初に 0x80 以上の値を指定します。この値は以下の通りで、クラス名の文字列を入れなくてもそのクラスで作成できます。
コントロール
0x80ボタン (Button)
0x81エディットコントロール (Edit)
0x82スタティックコントロール (Static)
0x83リストボックス (ListBox)
0x84スクロールバー (ScrollBar)
0x85コンボボックス (ComboBox)
※ Win32 では 0xFFFF、0x0080 のような指定方法でしたが、Win16 ではいきなり 0x80 などと指定します。
<Dialog Title><Control Text>
ダイアログのタイトル、コントロールのテキストを指定します。指定方法は <Menu ID> と同じです。
ダイアログのタイトルの場合は文字列のみですが、コントロールのテキストの場合、最初が 0xFF なら、このメンバは BYTE + WORD となり、次に来る WORD 値 (最初の BYTE 値は左の通り 0xFF) は ID です。この ID はスタティック コントロールなどで、画像を表示する際にリソースから読み込む時のリソース ID となります。
fontSize
フォントのサイズをポイント単位で指定します。ただし、DS_SETFONT スタイルが無い場合、このメンバを指定することは出来ません
fontName
フォントの名前を、NULL で終わる ANSI 文字列で指定します。ただし、DS_SETFONT スタイルが無い場合、このメンバを指定することは出来ません
SizeOfCreationData, CreationData
WM_CREATE メッセージの lParam のデータで渡される任意のデータを指定します。SizeOfCreationData はそのサイズで、0 を指定した場合は CreationData は存在しません。なお、SizeOfCreationData には SizeOfCreationData 分のサイズ (sizeof(BYTE)) は含みません

※ DWORD 境界に揃える、といった動作はありません。

作成例

ここでは、Win16 版の DLGTEMPLATE、DLGITEMTEMPLATE 構造体を使った例を示します。

// ※ DLGTEMPLATE、DLGITEMTEMPLATE は元々定義されていません。
//   上記の定義をヘッダーなどに含めてください。

typedef BYTE FAR* LPBYTE;

    HGLOBAL hgbl;
    LPDLGTEMPLATE lpdt;
    LPDLGITEMTEMPLATE lpdit;
    LPBYTE lpb;
    LPSTR lpsz;

    // 実際は、サイズはデータが収まりきるように
    // あらかじめ計算しておく必要があります。
    // GMEM_ZEROINIT であらかじめデータを 0 に設定しておきます。
    // (省略されているメンバは 0 になります)
    hgbl = GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, 1024);
    if (!hgbl)
        ErrorAndExit();

    // ダイアログボックスの初期値を設定します。
    // ※ hgbl と lpdt のアドレスは同じになります (GMEM_FIXED のため)。
    lpdt = (LPDLGTEMPLATE) GlobalLock(hgbl);
    lpdt->style = WS_POPUP | WS_BORDER | WS_SYSMENU
                     | DS_MODALFRAME | WS_CAPTION | DS_SETFONT;  // フォント付き
    lpdt->cdit = 2;  // コントロールの数
    lpdt->x = 10;   lpdt->y = 10;
    lpdt->cx = 100; lpdt->cy = 100;

    // 次のメンバの設定に移ります。
    lpb = (LPBYTE) (lpdt + 1);

    // <Menu ID>
    //   ID を指定する場合は以下の通りです。
    //   *lpb++ = 0xFF;
    //   *((LPWORD&) lpb)++ = id;
    *lpb++ = 0;

    // <Window Class>
    *lpb++ = 0;

    // <Dialog Title>
    lpsz = (LPSTR) lpb;

    // 文字列をコピーします。
    strcpy(lpsz, "ダイアログ テスト");

    // strlen 関数の戻り値は NULL 文字を含みません。
    lpb += strlen(lpsz) + 1;

    // WORD fontSize
    // ※ DS_SETFONT を指定しなかった場合、コントロールの設定に
    //   移るまでのデータ設定は行いません。
    *((LPWORD&) lpb)++ = 9;

    // CHAR fontName[]
    lpsz = (LPSTR) lpb;
    strcpy(lpsz, "MS P明朝");
    lpb += strlen(lpsz) + 1;

    // コントロールの設定に移ります。
    //   DWORD 境界に揃える必要はありません。
    lpdit = (LPDLGITEMTEMPLATE) lpb;

    // OK ボタンを作ります。
    lpdit->x = 10;  lpdit->y = 70;
    lpdit->cx = 60; lpdit->cy = 14;
    lpdit->id = IDOK;  // OK ボタンの ID
    lpdit->style = WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON;

    // 次のメンバの設定に移ります。
    lpb = (LPBYTE) (lpdit + 1);

    // <Window Class>
    //   これでも構いません。
    //     lpsz = (LPSTR) lpb;
    //     strcpy(lpsz, "Button");
    //     lpb += strlen(lpsz) + 1;
    *lpb++ = 0x80;  // ボタンのウィンドウクラスを指定します。

    // <Control Text>
    //   ID を指定する場合は以下の通りです。
    //   *lpb++ = 0xFF;
    //   *((LPWORD&) lpb)++ = id;
    lpsz = (LPSTR) lpb;
    strcpy(lpsz, "OK");
    lpb += strlen(lpsz) + 1;

    // BYTE SizeOfCreationData
    //   特にデータは無いため、0 で終わります。
    *lpb++ = 0;

    // 次のコントロールの設定に移ります。
    lpdit = (LPDLGITEMTEMPLATE) lpb;

    lpdit->x = 10;  lpdit->y = 10;
    lpdit->cx = 40; lpdit->cy = 9;
    lpdit->id = IDC_STATIC;  // スタティックコントロールによくある ID (IDC_STATIC = -1)
    lpdit->style = WS_CHILD | WS_VISIBLE | SS_LEFT;

    lpb = (LPBYTE) (lpdit + 1);

    // <Window Class>
    //   これでも構いません。
    //     lpsz = (LPSTR) lpb;
    //     strcpy(lpsz, "Static");
    //     lpb += strlen(lpsz) + 1;
    *lpb++ = 0x82;  // スタティックコントロールのウィンドウクラスを指定します。

    // <Control Text>
    lpsz = (LPSTR) lpb;
    strcpy(lpsz, "メッセージ");
    lpb += strlen(lpsz) + 1;

    // BYTE SizeOfCreationData
    //   特にデータは無いため、0 で終わります。
    *lpb++ = 0;

    // データ設定は終了しました。ロックを解除します。
    GlobalUnlock(hgbl);

    // DialogBoxIndirect を呼び出して作成します。GMEM_FIXED なので
    // 2 番目の引数は HGLOBAL なので hgbl をそのまま指定します。
    // (hInst、hWndOwner、DialogProc は既に定義されているものとします。)
    DialogBoxIndirect(hInst, hgbl, hWndOwner, (DLGPROC) DialogProc);

    // メモリを解放します。
    GlobalFree(hgbl);

最終更新日: 2005/08/10(推定)