Tutorial 11: More about Dialog Box

ではさらにダイアログボックスについて詳しく解説しよう。 特に、データの入出力としてダイアログボックスをどのように使用するか、についてみていくことにしよう。 前章のチュートリアルを読めば、この章は簡単なものだろう。 というのも、メインウィンドウの補助として使用するための変更しか行っていないからである。 このチュートリアルではさらに、コモンダイアログボックスについても解説する。
 ソース1   リソーススクリプト1   実行結果1 
 ソース2   リソーススクリプト2   実行結果2 
 ソース3   リソーススクリプト3   実行結果3 

Theory:

プログラムの入出力としてダイアログボックスを使用することについて、 この章で改めて説明することはごく少ない。 いつも通りにメインウィンドウを作成し、 ダイアログボックスを表示したい時に、 CreateDialogParam関数かDialogBoxParam関数をCALLすればよい。 DialogBoxParam関数をCALLした場合、もうそれ以上何もすることは無く、 メッセージ処理はダイアログボックスプロシージャが行うこととなる。 CreateDialogParam関数で作成した場合は、 メッセージループ中でIsDialogMessage関数を呼び出さなければならず、 ダイアログボックスマネージャにあなたが作成したダイアログボックスのキーボード処理をやってもらわないといけない。 2つともそれ程大したことは無いので、ここにはコードを載せないことにする。 各自ダウンロードして確かめてみるように。

では、コモンダイアログボックスの説明を行うことにする。 Windowsはダイアログボックスを既にいくつか用意してくれている。 ダイアログボックスはユーザインターフェイスの標準化を行ってくれている。 ファイル、印刷、色、フォント、検索などに使用するダイアログボックスがある。 できるだけこれらのダイアログボックスを使用するようにすべきで、 ダイアログボックスは comdlg32.dll にある。 これらを使用するために、comdlg32.lib をリンクしなければならず、 使用するダイアログボックスに対応したコモンダイアログライブラリ関数をCALLして作成する。 ファイルをオープンするダイアログボックスを作成するときは GetOpenFileName 関数、 ファイルをセーブするときのダイアログボックスはGetSaveFileName関数、 印刷するときのダイアログボックスはPrintDlg関数などといったものだ。 これらの関数はそれぞれ、対応した構造体へのポインタを引数にとることになっているが、 詳細はWin32APIリファレンスを参照せよ。 このチュートリアルでは、ファイルをオープンするダイアログボックスの作成方法、使用方法を紹介する。

以下は、GetOpenFileName関数のプロトタイプだ。

GetOpenFileName proto lpofn:DWORD

見てわかるとおり、引数はOPENFILENAME構造体へのポインタだけだ。 戻り値はTRUEのときにユーザがファイルを選択したことを意味し、 FALSEだとユーザは何も選択しなかったということだ。 次にOPENFILENAME構造体について見てみよう。

OPENFILENAME STRUCT

  lStructSize           DWORD     ?
  hwndOwner             HWND      ?
  hInstance             HINSTANCE ?
  lpstrFilter           LPCSTR    ?
  lpstrCustomFilter     LPSTR     ?
  nMaxCustFilter        DWORD     ?
  nFilterIndex          DWORD     ?
  lpstrFile             LPSTR     ?
  nMaxFile              DWORD     ?
  lpstrFileTitle        LPSTR     ?
  nMaxFileTitle         DWORD     ?
  lpstrInitialDir       LPCSTR    ?
  lpstrTitle            LPCSTR    ?
  Flags                 DWORD     ?
  nFileOffset           WORD      ?
  nFileExtension        WORD      ?
  lpstrDefExt           LPCSTR    ?
  lCustData             LPARAM    ?
  lpfnHook              DWORD     ?
  lpTemplateName        LPCSTR    ?

OPENFILENAME ENDS

よく使用するメンバについてみていこう。

Example:

以下のプログラムはメニューから「File」→「Open」と選択したときにファイルをオープンするダイアログボックスが表示される。 そのダイアログボックスでユーザがファイルを選択すれば、選択されたファイル名、拡張子、フルパス名をメッセージボックスで表示するようになっている。

.386
.model flat,stdcall
option casemap:none
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\comdlg32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\comdlg32.lib

.const
IDM_OPEN equ 1
IDM_EXIT equ 2
MAXSIZE equ 260
OUTPUTSIZE equ 512

.data
ClassName db "SimpleWinClass",0
AppName db "Our Main Window",0
MenuName db "FirstMenu",0
ofn  OPENFILENAME <>
FilterString db "All Files",0,"*.*",0
            db "Text Files",0,"*.txt",0,0
buffer db MAXSIZE dup(0)
OurTitle db "-=Our First Open File Dialog Box=-: Choose the file to open",0
FullPathName db "The Full Filename with Path is: ",0
FullName db "The Filename is: ",0
ExtensionName db "The Extension is: ",0
OutputString db OUTPUTSIZE dup(0)
CrLf db 0Dh,0Ah,0

.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?

.code
start:
   invoke GetModuleHandle, NULL
   mov   hInstance,eax
   invoke GetCommandLine
    mov CommandLine,eax
   invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
   invoke ExitProcess,eax

WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
   LOCAL wc:WNDCLASSEX
   LOCAL msg:MSG
   LOCAL hwnd:HWND
   mov  wc.cbSize,SIZEOF WNDCLASSEX
   mov  wc.style, CS_HREDRAW or CS_VREDRAW
   mov  wc.lpfnWndProc, OFFSET WndProc
   mov  wc.cbClsExtra,NULL
   mov  wc.cbWndExtra,NULL
   push hInst
   pop  wc.hInstance
   mov  wc.hbrBackground,COLOR_WINDOW+1
   mov  wc.lpszMenuName,OFFSET MenuName
   mov  wc.lpszClassName,OFFSET ClassName
   invoke LoadIcon,NULL,IDI_APPLICATION
   mov  wc.hIcon,eax
   mov  wc.hIconSm,eax
   invoke LoadCursor,NULL,IDC_ARROW
   mov  wc.hCursor,eax
   invoke RegisterClassEx, addr wc
   invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR ClassName,ADDR AppName,\
          WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
          CW_USEDEFAULT,300,200,NULL,NULL,\
          hInst,NULL
   mov  hwnd,eax
   invoke ShowWindow, hwnd,SW_SHOWNORMAL
   invoke UpdateWindow, hwnd
   .WHILE TRUE
       invoke GetMessage, ADDR msg,NULL,0,0
       .BREAK .IF (!eax)
       invoke TranslateMessage, ADDR msg
       invoke DispatchMessage, ADDR msg
   .ENDW
   mov    eax,msg.wParam
   ret
WinMain endp

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
   .IF uMsg==WM_DESTROY
       invoke PostQuitMessage,NULL
   .ELSEIF uMsg==WM_COMMAND
       mov eax,wParam
       .if ax==IDM_OPEN
           mov ofn.lStructSize,SIZEOF ofn
           push hWnd
           pop ofn.hwndOwner
           push hInstance
           pop ofn.hInstance
           mov ofn.lpstrFilter, OFFSET FilterString
           mov ofn.lpstrFile, OFFSET buffer
           mov ofn.nMaxFile,MAXSIZE
           mov ofn.Flags, OFN_FILEMUSTEXIST or \
               OFN_PATHMUSTEXIST or OFN_LONGNAMES or\
               OFN_EXPLORER or OFN_HIDEREADONLY
           mov ofn.lpstrTitle, OFFSET OurTitle
           invoke GetOpenFileName, ADDR ofn
           .if eax==TRUE
               invoke lstrcat,offset OutputString,OFFSET FullPathName
               invoke lstrcat,offset OutputString,ofn.lpstrFile
               invoke lstrcat,offset OutputString,offset CrLf
               invoke lstrcat,offset OutputString,offset FullName
               mov eax,ofn.lpstrFile
               push ebx
               xor ebx,ebx
               mov bx,ofn.nFileOffset
               add eax,ebx
               pop ebx
               invoke lstrcat,offset OutputString,eax
               invoke lstrcat,offset OutputString,offset CrLf
               invoke lstrcat,offset OutputString,offset ExtensionName
               mov eax,ofn.lpstrFile
               push ebx
               xor ebx,ebx
               mov bx,ofn.nFileExtension
               add eax,ebx
               pop ebx
               invoke lstrcat,offset OutputString,eax
               invoke MessageBox,hWnd,OFFSET OutputString,ADDR AppName,MB_OK
               invoke RtlZeroMemory,offset OutputString,OUTPUTSIZE
           .endif
       .else
           invoke DestroyWindow, hWnd
       .endif
   .ELSE
       invoke DefWindowProc,hWnd,uMsg,wParam,lParam
       ret
   .ENDIF
   xor   eax,eax
   ret
WndProc endp
 end start

Analysis:

mov  ofn.lStructSize,SIZEOF ofn
push hWnd
pop  ofn.hwndOwner
push hInstance
pop  ofn.hInstance

お決まりの ofn 構造体をセットしていく。

mov ofn.lpstrFilter, OFFSET FilterString

ファイル名をフィルタするフィルタ文字列を以下のようにする。

FilterString db "All Files",0,"*.*",0
             db "Text Files",0,"*.txt",0,0

これら4つの文字列は全て「0」終端でなければならない。 これらはペアとなっており、1つ目の文字列が説明で、2つ目の文字列がフィルタする文字列だ。 この例ではフィルタする文字列は "*.*" と "*.txt" となる。 ここにはどんなフィルタパターンでも記述可能となっている。 そして、忘れてはならないのは、一番最後のおまけの「0」である。 これはフィルタ文字列がここで終了していることを表しており、これを忘れるとヒドイことになる。

mov ofn.lpstrFile, OFFSET buffer
mov ofn.nMaxFile,MAXSIZE

ユーザが選択したファイル名を格納するバッファをセットする。 そして、nMaxFileメンバでそのバッファのサイズを記述しなければならない。 後ほど、このバッファからファイル名を抽出する。

mov ofn.Flags, OFN_FILEMUSTEXIST or \
    OFN_PATHMUSTEXIST or OFN_LONGNAMES or\
    OFN_EXPLORER or OFN_HIDEREADONLY

ダイアログボックスのスタイルを選択するフラグだ。
OFN_FILEMUSTEXIST と OFN_PATHMUSTEXIST はファイル名を記述するエディットコントロールにファイル名が記述されていなければならないことを要求する。
OFN_LONGNAMES はダイアログボックスに表示されるファイル名が長いファイル名で表示することを意味する(長いファイル名でない場合は従来の 8.3形式のファイル名で表示される)
OFN_EXPLORER はダイアログボックスがエクスプローラ風で表示される。
OFN_HIDEREADONLY はダイアログボックスの読み取り専用チェックボックスを隠す。

ホントはもっといっぱいフラグがあるのだが、ここでは説明しきれないので、必要な場合はWin32APIリファレンスを参照せよ。

mov ofn.lpstrTitle, OFFSET OurTitle

ダイアログボックスのタイトルを記述する。

invoke GetOpenFileName, ADDR ofn

引数にofn構造体を渡し、GetOpenFileName関数をCALLする。
このとき、ファイルオープンダイアログボックスは画面に表示され、ユーザがファイルを選択するか、 キャンセルボタンを押すか、ダイアログボックスを閉じるまでこの関数は制御を返さない。

(ユーザが何らかの選択をして)この関数から返ってきたときに、eaxレジスタがTRUEだったらユーザがファイルを選択したということで、FALSEだったらそうではないということだ。

.if eax==TRUE
    invoke lstrcat,offset OutputString,OFFSET FullPathName
    invoke lstrcat,offset OutputString,ofn.lpstrFile
    invoke lstrcat,offset OutputString,offset CrLf
    invoke lstrcat,offset OutputString,offset FullName

ユーザがファイルを選択した場合、メッセージボックスに表示する文字列を準備する。 OutputString変数にメモリをセットし、文字列を連結するためにAPI関数 lstrcat をCALLする。 数行を1つの文字列とするために、改行コードで文字列を区切る。

mov    eax,ofn.lpstrFile
push   ebx
xor    ebx,ebx
mov    bx,ofn.nFileOffset
add    eax,ebx
pop    ebx
invoke lstrcat,offset OutputString,eax

上の数行は説明を要するだろう。nFileOffset は lpstrFile にセットされているフルパス文字列中の何番目からファイル名が始まるかを表している。 しかし、lpstrFile はDWORDサイズで、nFileOffsetはWORDサイズなので直接2つの変数を計算することはできなくて、 nFileOffset をひとまずebxの下位ワード領域(bx)に格納し、それとlpstrFileを足しあわさなければならない。

invoke MessageBox,hWnd,OFFSET OutputString,ADDR AppName,MB_OK

メッセージボックスに文字列を表示させる。

invoke RtlZerolMemory,offset OutputString,OUTPUTSIZE

OutputStringは今度使うときに備えて、絶対にクリアしておかなければならない。 なので、RtlzeroMemory関数をCALLして、クリアしてもらう。


[戻る]