第十课 以对话框为主要界面的应用程序
SendDlgItemMessage proto hwndDlg:DWORD,\该PAI函数对于用在向子窗口控件发送方面是非常有用的。譬如:如果您想得到编辑控件中的字符串可以这么做:
idControl:DWORD,\
uMsg:DWORD,\
wParam:DWORD,\
lParam:DWORD
call SendDlgItemMessage, hDlg, ID_EDITBOX, WM_GETTEXT, 256, ADDR text_buffer具体要发送那些消息应当查询有关的WIN32 API 参考手册。 WINDOWS 还 提供几个快速存取控件数据的函数。譬如:GetDlgItemText、CheckDlgButton等。这样一来,您就可以不用去查询每个消息的wParam和lParam参数获得相关信息了。您应尽可能地使用这些API 函数,这样使得您的代码将来比较容易维护。 对话框的管理函数会把一些消息发送给一个特定的回调函数:对话框过程处理函数,该函数的格式为:
DlgProc proto hDlg:DWORD ,\该函数的格式非常类似于窗口的过程函数,除了返回值是TURE和FALSE,而不是HRESULT,存在于WINDOWS内部的对话框管理器才是对话框真正的窗口过程函数。它会把某些消息传递给我们的窗口过程函数。所以当我们的窗口过程函数处理这些消息时就返回TTRUE,否则就在eax中返回FALSE。这也意味着我们的窗口过程函数在接受到自己不处理的消息时并不会调用DefWindowProc函数,因为它本身不是一个真正的窗口过程函数。 对于对话框有两种用法:一种是把它作为一个主窗口来用,一种是把它作为一种输入输出设备使用。本课中我们将示范第一种用法。“把对话框用作主窗口”有两种意思: 1。您可以调用RegisterClassEx函数把对话框模板注册为一个窗口类。这样该对话框的行为就类似于一个普通的窗口了:它通过在注册窗口时指定的窗口过程来处理所有的消息,通过这种方法来使用对话框的好处是您不需要显示地创建子窗口控件,WINDOWS本身会帮您创建好,另外还会帮您处理所有的按键逻辑,另外您还可以指定您窗口类结构中的光标和图标; 2。您的应用程序创建没有父窗口的对话框窗口,这种方法中,没有必要需要一段处理消息循环的代码,因为所有的消息被直接送到对话框过程处理函数,这样您也可以不要注册一个窗口类。 本课中我门将先使用第一种方法然后使用第二中方法。
iMsg:DWORD ,\
wParam:DWORD ,\
lParam:DWORD
.data 
  ClassName db "DLGCLASS",0 
  MenuName db "MyMenu",0 
  DlgName db "MyDialog",0 
  AppName db "Our First Dialog Box",0 
  TestString db "Wow! I'm in an edit box now",0 
.data? 
  hInstance HINSTANCE ? 
  CommandLine LPSTR ? 
  buffer db 512 dup(?) 
.const 
  IDC_EDIT        equ 3000 
  IDC_BUTTON      equ 3001 
  IDC_EXIT        equ 3002 
  IDM_GETTEXT     equ 32000 
  IDM_CLEAR       equ 32001 
  IDM_EXIT        equ 32002 
.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 hDlg: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,DLGWINDOWEXTRA 
      push  hInst 
      pop   wc.hInstance 
      mov   wc.hbrBackground,COLOR_BTNFACE+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 CreateDialogParam,hInstance,ADDR DlgName,NULL,NULL,NULL 
  
      mov   hDlg,eax 
      invoke ShowWindow, hDlg,SW_SHOWNORMAL 
      invoke UpdateWindow, hDlg 
      invoke GetDlgItem,hDlg,IDC_EDIT 
      invoke SetFocus,eax 
      .WHILE TRUE 
          invoke GetMessage, ADDR msg,NULL,0,0 
  
          .BREAK .IF (!eax) 
         invoke IsDialogMessage, hDlg, ADDR msg 
  
          .IF eax ==FALSE 
              invoke 
  TranslateMessage, ADDR msg 
              invoke 
  DispatchMessage, ADDR msg 
          .ENDIF 
      .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 lParam==0 
              .IF ax==IDM_GETTEXT 
  
                  
  invoke GetDlgItemText,hWnd,IDC_EDIT,ADDR buffer,512 
                  
  invoke MessageBox,NULL,ADDR buffer,ADDR AppName,MB_OK 
              .ELSEIF 
  ax==IDM_CLEAR 
                  
  invoke SetDlgItemText,hWnd,IDC_EDIT,NULL 
              .ELSE 
  
                  
  invoke DestroyWindow,hWnd 
              .ENDIF 
  
          .ELSE 
              mov edx,wParam 
  
              shr edx,16 
  
              .IF dx==BN_CLICKED 
  
                  
  .IF ax==IDC_BUTTON 
                      
  invoke SetDlgItemText,hWnd,IDC_EDIT,ADDR TestString 
                  
  .ELSEIF ax==IDC_EXIT 
                      
  invoke SendMessage,hWnd,WM_COMMAND,IDM_EXIT,0 
                  
  .ENDIF 
              .ENDIF 
  
          .ENDIF 
      .ELSE 
          invoke DefWindowProc,hWnd,uMsg,wParam,lParam 
  
          ret 
      .ENDIF 
      xor    eax,eax 
      ret 
  WndProc endp 
  end start 
#define IDC_EDIT                                       
  3000 
  #define IDC_BUTTON                                
  3001 
  #define IDC_EXIT                                       
  3002 
#define IDM_GETTEXT                             
  32000 
  #define IDM_CLEAR                                  
  32001 
  #define IDM_EXIT                                      
  32003 
    
MyDialog DIALOG 10, 10, 205, 60 
  STYLE 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX | 
  WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME | DS_3DLOOK 
  CAPTION "Our First Dialog Box" 
  CLASS "DLGCLASS" 
  BEGIN 
      EDITTEXT         
  IDC_EDIT,   15,17,111,13, ES_AUTOHSCROLL | ES_LEFT 
      DEFPUSHBUTTON   "Say Hello", IDC_BUTTON,    
  141,10,52,13 
      PUSHBUTTON      "E&xit", 
  IDC_EXIT,  141,26,52,13, WS_GROUP 
  END 
    
MyMenu  MENU 
  BEGIN 
      POPUP "Test Controls" 
      BEGIN 
          MENUITEM "Get Text", IDM_GETTEXT 
  
          MENUITEM "Clear Text", IDM_CLEAR 
  
          MENUITEM "", , 0x0800 /*MFT_SEPARATOR*/ 
  
          MENUITEM "E&xit", IDM_EXIT 
  
      END 
  END 
MyDialog DIALOG 10, 10, 205, 60
先是对话框的名字,然后是关键字“DAILOG”。接下来的四个数字中,前两个是对话框的坐标,后两个是对话框的宽和高(注意:它们的单位是对话框的单位,而不一定是像素点)。
STYLE 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX | 
  WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME | DS_3DLOOK 
上面定义了对话框的风格。
CAPTION "Our First Dialog Box"
这是显示在对话框标题条上的标题。
CLASS "DLGCLASS"
这一行非常关键。正是有了关键字CLASS,我们才可以用它来声明把一个对话框当成一个窗口来用。跟在关键字后面的是“窗口类”的名称。
BEGIN 
      EDITTEXT         
  IDC_EDIT,   15,17,111,13, ES_AUTOHSCROLL | ES_LEFT 
      DEFPUSHBUTTON   "Say Hello", IDC_BUTTON,    
  141,10,52,13 
      PUSHBUTTON      "E&xit", 
  IDC_EXIT,  141,26,52,13 
  END 
上面的一块定义了对话框中的子窗口控件,它们是声明在一头一尾的两个关键字BEGIN和END之间的。
       invoke IsDialogMessage, hDlg, ADDR 
  msg 
          .IF eax ==FALSE 
              invoke 
  TranslateMessage, ADDR msg 
              invoke 
  DispatchMessage, ADDR msg 
          .ENDIF 
现在程序进入消息循环,在我们翻译和派发消息前,该函数使得对话框内置的对话框管理程序来处理有关的键盘跳转逻辑。如果该函数返回TRUE,则表示消息是传给对话框的已经由该函数处理了。注意和前一课不同,当我们想得到控件的文本信息时调用GetDlgItemText函数而不是GetWindowText函数,前者接受的参数是一个控件的ID 号,而不是窗口的句柄,这使得在对话框中调用该函数更方便。
DlgProc proto :DWORD,:DWORD,:DWORD,:DWORD
include \masm32\include\windows.inc 
  include \masm32\include\user32.inc 
  include \masm32\include\kernel32.inc 
  includelib \masm32\lib\user32.lib 
  includelib \masm32\lib\kernel32.lib 
.data 
  DlgName db "MyDialog",0 
  AppName db "Our Second Dialog Box",0 
  TestString db "Wow! I'm in an edit box now",0 
.data? 
  hInstance HINSTANCE ? 
  CommandLine LPSTR ? 
  buffer db 512 dup(?) 
.const 
  IDC_EDIT            
  equ 3000 
  IDC_BUTTON     equ 3001 
  IDC_EXIT            
  equ 3002 
  IDM_GETTEXT  equ 32000 
  IDM_CLEAR       equ 32001 
  IDM_EXIT           equ 
  32002 
    
.code 
  start: 
      invoke GetModuleHandle, NULL 
      mov    hInstance,eax 
      invoke DialogBoxParam, hInstance, ADDR DlgName,NULL, addr 
  DlgProc, NULL 
      invoke ExitProcess,eax 
DlgProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM 
      .IF uMsg==WM_INITDIALOG 
          invoke GetDlgItem, hWnd,IDC_EDIT 
  
          invoke SetFocus,eax 
      .ELSEIF uMsg==WM_CLOSE 
          invoke SendMessage,hWnd,WM_COMMAND,IDM_EXIT,0 
  
      .ELSEIF uMsg==WM_COMMAND 
          mov eax,wParam 
          .IF lParam==0 
              .IF ax==IDM_GETTEXT 
  
                  
  invoke GetDlgItemText,hWnd,IDC_EDIT,ADDR buffer,512 
                  
  invoke MessageBox,NULL,ADDR buffer,ADDR AppName,MB_OK 
              .ELSEIF 
  ax==IDM_CLEAR 
                  
  invoke SetDlgItemText,hWnd,IDC_EDIT,NULL 
              .ELSEIF 
  ax==IDM_EXIT 
                  
  invoke EndDialog, hWnd,NULL 
              .ENDIF 
  
          .ELSE 
              mov edx,wParam 
  
              shr edx,16 
  
              .if dx==BN_CLICKED 
  
                  
  .IF ax==IDC_BUTTON 
                      
  invoke SetDlgItemText,hWnd,IDC_EDIT,ADDR TestString 
                  
  .ELSEIF ax==IDC_EXIT 
                      
  invoke SendMessage,hWnd,WM_COMMAND,IDM_EXIT,0 
                  
  .ENDIF 
              .ENDIF 
  
          .ENDIF 
      .ELSE 
          mov eax,FALSE 
          ret 
      .ENDIF 
      mov eax,TRUE 
      ret 
  DlgProc endp 
  end start 
#define IDC_EDIT                                       
  3000 
  #define IDC_BUTTON                                
  3001 
  #define IDC_EXIT                                       
  3002 
#define IDR_MENU1 3003
#define IDM_GETTEXT                              
  32000 
  #define IDM_CLEAR                                   
  32001 
  #define IDM_EXIT                                       
  32003 
    
MyDialog DIALOG 10, 10, 205, 60 
  STYLE 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX | 
  WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME | DS_3DLOOK 
  CAPTION "Our Second Dialog Box" 
  MENU IDR_MENU1 
  BEGIN 
      EDITTEXT         
  IDC_EDIT,   15,17,111,13, ES_AUTOHSCROLL | ES_LEFT 
      DEFPUSHBUTTON   "Say Hello", IDC_BUTTON,    
  141,10,52,13 
      PUSHBUTTON      "E&xit", 
  IDC_EXIT,  141,26,52,13 
  END 
    
IDR_MENU1  MENU 
  BEGIN 
      POPUP "Test Controls" 
      BEGIN 
          MENUITEM "Get Text", IDM_GETTEXT 
  
          MENUITEM "Clear Text", IDM_CLEAR 
  
          MENUITEM "", , 0x0800 /*MFT_SEPARATOR*/ 
  
          MENUITEM "E&xit", IDM_EXIT 
  
      END 
  END 
DlgProc proto :DWORD,:DWORD,:DWORD,:DWORD
我们已经定义了DlgProc函数的原型,所以可以用操作符ADDR来获得它的地址(记得吗,它可以在运行时动态地获得标识符的有效地址):
invoke DialogBoxParam, hInstance, ADDR DlgName,NULL, addr DlgProc, NULL
上面的几行调用了函数DialogBoxPAram,该函数有五个参数,分别是:实例句柄、对话框模板的名字、父窗口的句柄、对话框过程函数的地址、和对话框相关的数据。该函数产生一个模式对话框。如果不显示地关闭该函数不会返回。
    .IF uMsg==WM_INITDIALOG 
          invoke GetDlgItem, hWnd,IDC_EDIT 
  
          invoke SetFocus,eax 
      .ELSEIF uMsg==WM_CLOSE 
          invoke SendMessage,hWnd,WM_COMMAND,IDM_EXIT,0 
除了不处理WM_CREATE消息外对话框的窗口处理过程函数和一般的窗口处理过程相似。该过程函数接收到的第一个消息是WM_INITDIALOG。通常把初始化的代码放到此处。注意如果您处理该消息必须在eax中返回TRUE。内置的对话框管理函数不会把WM_DESTROY 消息发送到对话框的消息处理函数,所以如果我们想在对话框关闭时进行处理,就把它放到WM_CLOSE消息的处理中。在我们的例子中我们发送消息WM_COMMAND,并在参数wParam中放置IDM_EXIT,这和处理WM_CLOSE 消息效果一样,在处理IDM_EXIT 中我们调用EndDialog函数。 如果我们想要销毁一个对话框,必须调用EndDialog函数,该函数并不会立即销毁一个窗口,而是设置一个标志位,然后对话框管理器会处理接下去的销毁对话框动作。 好,现在我们来看看资源文件,其中最显著的变化是在指定菜单时我们不是用字符串指定该菜单的名称而是用了一个常量 IDR_MENU1。在调用DialogBoxParam产生的对话框中挂接一个菜单必须这么做,注意在该对话框模板中,在该标识符前必须加MENU关键字,这两个例子中的显著不同是后者没有图标,这可以在处理WM_INITDIALOG中发送消息WM_SETICON消息,然后在该消息处理代码中作适当的处理即可。