Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1758902
  • 博文数量: 290
  • 博客积分: 10653
  • 博客等级: 上将
  • 技术积分: 3178
  • 用 户 组: 普通用户
  • 注册时间: 2007-10-24 23:08
文章存档

2013年(6)

2012年(15)

2011年(25)

2010年(86)

2009年(52)

2008年(66)

2007年(40)

分类:

2007-12-20 23:11:55

Tutorial 10: Dialog Box as Main Window

第十课:作为主窗口的对话框


Now comes the really interesting part about GUI, the dialog box. In this tutorial (and the next), we will learn how to use a dialog box as our main window.

现在,来到在图形用户界面中真正有趣味的部分--对话框。 在这一课(和下一课)中, 我们将学习如何利用对话框来作为我们的主窗口。

Download the first example here, the second example here.

Theory:

原理:

If you play with the examples in the previous tutorial long enough, you 'll find out that you cannot change input focus from one child window control to another with Tab key. The only way you can do that is by clicking the control you want it to gain input focus. This situation is rather cumbersome. Another thing you might notice is that I changed the background color of the parent window to gray instead of normal white as in previous examples.

 This is done so that the color of the child window controls can blend seamlessly with the color of the client area of the parent window. There is a way to get around this problem but it's not easy. You have to subclass all child window controls in your parent window.
The reason why such inconvenience exists is that child window controls are originally designed to work with a dialog box, not a normal window. The default color of child window controls such as a button is gray because the client area of a dialog box is normally gray so they blend into each other without any sweat on the programmer's part.

 

如果你与前面课程中的例子一起玩耍了足够长的时间,你将发现你不能用TAB键在两个子控件窗口中来回切换输入焦点.如果你想让一个子窗口控件再次获得输入焦点,唯一的方法就是用鼠标点击那个子控件窗口.这种情形是令人讨厌的.

另外的一件事你可能会注意到,在上一个例子中,我改变了父窗口的背景色,我用灰色代替了正常的白色.

这样做的原因是为了让子控件窗口的颜色能与父窗口客户区的颜色无缝隙的混合.这是捕获这个应用程序的一种方式但是它并不是容易实现的.你必须在你的父窗口中对所有的子窗口控件分类.

为什么如此麻烦的原因是,原先设计子窗口控件的时候是为了让它和对话框一起工作的. 而不是标准窗口.例如像按钮这种子窗口控件的默认颜色被设置成灰色,因为对话框的客户区通常也是灰色所以它们彼此就能很好的混合在一起而不用依靠程序员加入其它的处理代码。


Before we get deep into the detail, we should know first what a dialog box is. A dialog box is nothing more than a normal window which is designed to work with child window controls. Windows also provides internal "dialog box manager" which is responsible for most of the keyboard logic such as shifting input focus when the user presses Tab, pressing the default pushbutton if Enter key is pressed, etc so programmers can deal with higher level tasks.

 

Dialog boxes are primarily used as input/output devices.As such a dialog box can be considered as an input/output "black box" meaning that you don't have to know how a dialog box works internally in order to be able to use it, you only have to know how to interact with it. That's a principle of object oriented programming (OOP) called information hiding. If the black box is *perfectly* designed, the user can make use of it without any knowledge on how it operates. The catch is that the black box must be perfect, that's hard to achieve in the real world. Win32 API is also designed as a black box too.

 

在我们深入细节之前,首先,我们应该知道什么是对话框。一个对话框仅仅是一个常规的窗口。只不过这个窗口是为了和子窗口控件一起工作而被设计的。 Windows内部也提供一“对话框管理器”用来为大多数的键盘逻辑负责。例如当用户按下TAB键时,移动输入焦点。(从一个子窗口控件到另一个子窗口控件),如果用户按下ENTER,相当于按下(用鼠标)那个默认按钮等.这样一来,程序员就能涉及一些更高层次的任务.

 

对话框主要用来作为输入输出设备(接口).同样的,一个对话框也可以当作一个输入输出的黑匣子来考虑.”黑匣子的意思是你并不知道对话框的内部是如何工作的,你仅知道的是 如何和它交互.这是面向对象的一个原理称作信息隐藏 .如果这个黑匣子的设计是完美的,那么用户就能使用它而不需要任何关于如何操作对话框的知识.我们应该抓住黑匣子必须是完美的这一点,并努力去实现它.Win32 API ( 应用程序接口) 也被设计成一黑匣子.

 

Well, it seems we stray from our path. Let's get back to our subject. Dialog boxes are designed to reduce workload of a programmer. Normally if you put child window controls on a normal window, you have to subclass them and write keyboard logic yourself. But if you put them on a dialog box, it will handle the logic for you. You only have to know how to get the user input from the dialog box or how to send commands to it.

,这看起来像是偏离了我们的道路.让我们回到主题.对话框是为了减少程序员的工作量而被设计的.通常,如果你放置一个子窗口控件在一个常规窗口中, 您就必须自己处理其中的按键逻辑和细分类它的窗口过程。但是如果你把它们放置在对话框中,它将为你处理内在联系.(上面的工作由对话框管理器代替)你仅需要知道的是: 如何获得用户的输入或者如何发送命令给它 .

 

A dialog box is defined as a resource much the same way as a menu. You write a dialog box template describing the characteristics of the dialog box and its controls and then compile the resource script with a resource editor.  

一个对话框被作为资源定义,这和菜单的定义有很多相同之处.你写一个对话框模版用来描述这个对话框的特征面( 属性) 和它拥有的控件并用资源编辑器将这些资源脚本组合在一起.

 

 Note that all resources are put together in the same resource script file. You can use any text editor to write a dialog box template but I don't recommend it. You should use a resource editor to do the job visually since arranging child window controls on a dialog box is hard to do manually. Several excellent resource editors are available. Most of the major compiler suites include their own resource editors. You can use them to create a resource script for your program and then cut out irrelevant lines such as those related to MFC.

注意,所有的资源都放在同一个资源脚本文件中.你能用任何文本编辑器来写一个对话框模版但是我并不推荐你这样做.你应该用可视化的资源编辑器做这个工作因为用写脚本的方式在对话框上安排子窗口控件是非常难的. 这里有几个极好的资源编辑器你可能用到.大多数主流的编译器都有它们自己的资源编辑器.你能用它们为你的应用程序创建一资源脚本文件然后裁减掉不相干的行例如:那些涉及到MFC的.  


There are two main types of dialog box: modal and modeless. A modeless dialog box lets you change input focus to other window. The example is the Find dialog of MS Word. There are two subtypes of modal dialog box: application modal and system modal. An application modal dialog box doesn't let you change input focus to other window in the same application but you can change the input focus to the window of OTHER application. A system modal dialog box doesn't allow you to change input focus to any other window until you respond to it first.

这里有两种类型的对话框: 模式的和非模式的.非模式的对话框能让你切换输入焦点给其他窗口.如word中的查找对话框就是非模式的.

模式对话框有两种子类型: 应用程序模式和系统模式.一个应用程序模式对话框不让你切换输入焦点给属于同一应用程序的其他窗口了,但是你能切换输入焦点给其他的应用程序窗口.一个系统模式的对话框不允许你切换输入焦点给任何其它窗口,直到你响应它为止.


A modeless dialog box is created by calling CreateDialogParam API function. A modal dialog box is created by calling DialogBoxParam. The only distinction between an application modal dialog box and a system modal one is the DS_SYSMODAL style. If you include DS_SYSMODAL style in a dialog box template, that dialog box will be a system modal one.
You can communicate with any child window control on a dialog box by using SendDlgItemMessage function. Its syntax is like this:

 

非模式对话框是通过调用CreateDialogParam API 函数来创建的.模式对话框是通过调用DialogBoxParam函数创建。应用程序模式对话框和系统模式对话框唯一的区别就是系统模式对话框指定DS_SYSMODAL样式.如果在一个对话框模版中包含DS_SYSMODAL样式,那么这个对话框将是系统模式的对话框.

 

你能使用SendDlgItemMessage函数来和在一个对话框上面的任何子窗口控件通信.它的句法像这样:
 

SendDlgItemMessage proto hwndDlg:DWORD,\
                                             idControl:DWORD,\
                                             uMsg:DWORD,\
                                             wParam:DWORD,\
                                             lParam:DWORD

This API call is immensely useful for interacting with a child window control. For example, if you want to get the text from an edit control, you can do this:

call SendDlgItemMessage, hDlg, ID_EDITBOX, WM_GETTEXT, 256, ADDR text_buffer

这个API函数调用是非常有用的,因为它和一个子窗口控件相结合.例如, 如果你想从一个编辑控件中获得文本,你可以这样做:

call SendDlgItemMessage, hDlg, ID_EDITBOX, WM_GETTEXT, 256, ADDR text_buffer

In order to know which message to send, you should consult your Win32 API reference.

为了知道是那一个消息被发送,你应该参考你的Win32 API手册.
Windows also provides several control-specific API functions to get and set data quickly, for example, GetDlgItemText, CheckDlgButton etc. These control-specific functions are provided for programmer's convenience so he doesn't have to look up the meanings of wParam and lParam for each message. Normally, you should use control-specific API calls when they're available since they make source code maintenance easier. Resort to SendDlgItemMessage only if no control-specific API calls are available.
The Windows dialog box manager sends some messages to a specialized callback function called a dialog box procedure which has the following format:

Windows为了快速的获取和设置数据提供了几个特殊的控件类API函数,例如,

GetDlgItemText,CheckDlgButton,.使用这些特殊控件类函数给程序员带来了方便从而让程序员没有必要为每一个消息去查找wParamlParam参数的含义.

通常地, 当用到子控件的时候你应该使用这些特殊控件类函数,因为它们构成的源代码容易维护.如果某一类子窗口控件没有特殊的控件类函数可以调用则用SendDlgItemMessage 函数.

Windows对话框管理器发送一些消息给指定的回叫函数调用对话框过程.这个过程格式如下: 

 

DlgProc  proto hDlg:DWORD ,\
                        iMsg:DWORD ,\
                        wParam:DWORD ,\
                        lParam:DWORD

 

The dialog box procedure is very similar to a window procedure except for the type of return value which is TRUE/FALSE instead of LRESULT. The internal dialog box manager inside Windows IS the true window procedure for the dialog box. It calls our dialog box procedure with some messages that it received. So the general rule of thumb is that: if our dialog box procedure processes a message,it MUST return TRUE in eax and if it does not process the message, it must return FALSE in eax. Note that a dialog box procedure doesn't pass the messages it does not process to the DefWindowProc call since it's not a real window procedure.
There are two distinct uses of a dialog box. You can use it as the main window of your application or use it as an input device. We 'll examine the first approach in this tutorial.

对话框过程和窗口过程是非常相似的除了返回值的类型对话框的是TRUEFALSE替代了窗口过程的LRESULT.windows系统内部的对话框管理器才是对话框真正的窗口过程.它用它接受到的一些消息调用我们外部的对话框过程.所以一般的规则是: 如果我们的对话框过程处理一个消息,它就在eax中返回TRUE,如果它没处理这条消息,它必须在eax中返回FALSE .注意:一个对话框过程不传递任何消息,它也不调用DefWindowProc这样的函数来处理其他未被处理的消息.因为对话框过程并不是真的窗口过程.

一个对话框有两种截然不同的用法. 你能用它作为你的应用程序的主窗口或者用它作为输入设备.在这一课中,我们将验证第一种用法: 

 


"Using a dialog box as main window" can be interpreted in two different senses.

“ 用对话框作为主窗口有两种不同的认识。

 

 

1.You can use the dialog box template as a class template which you register with RegisterClassEx call. In this case, the dialog box behaves like a "normal" window: it receives messages via a window procedure referred to by lpfnWndProc member of the window class, not via a dialog box procedure. The benefit of this approach is that you don't have to create child window controls yourself, Windows creates them for you when the dialog box is created. Also Windows handles the keyboard logic for you such as Tab order etc. Plus you can specify the cursor and icon of your window in the window class structure.

2Your program just creates the dialog box without creating any parent window. This approach makes a message loop unnecessary since the messages are sent directly to the dialog box procedure. You don't even have to register a window class!

1.你能通过调用RegisterClassEx函数把对话框模版注册为类模版。既然这样,这个对话框的窗口行为就如同一个正常的窗口。它通过窗口类的lpfnWndProc成员指定的窗口过程来处理它所接收到的消息。而不通过对话框过程。这种方法的好处是你不必要创建你自己的子窗口控件,因为在对话框被创建时由windows来为你创建它们。同样 ,windows也为你处理诸如tab 或其他一系列的键盘逻辑。另外,你还可以在窗口类结构中为你的窗口指定光标和图标。

2.你的程序仅仅创建了一个没有任何父窗口的对话框。因为消息被发送给对话框过程,所以对用这种方法创建的对话框而言,消息循环是多余的。你甚至可以不注册窗口类。



This tutorial is going to be a long one. I'll present the first approach followed by the second.

这一课将是长时间的。首先,我将为你呈现第一种方法,随后是第二种。

Examples:

例子:


dialog.asm



.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
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib

.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


Dialog.rc



#include "resource.h"

#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

Analysis:

分析:

Let's analyze this first example.
This example shows how to register a dialog template as a window class and create a "window" from that class. It simplifies your program since you don't have to create the child window controls yourself.
Let's first analyze the dialog template.

让我们分析第一个例子。

这个例子为我们展示了如何把一个对话框模版注册为一窗口类并基于这个类创建一个“窗口”。它简化了你的程序因为你不用自己去创建子窗口控件了。

让我们分析这个对话框模版。

MyDialog DIALOG 10, 10, 205, 60

Declare the name of a dialog, in this case, "MyDialog" followed by the keyword "DIALOG". The following four numbers are: x, y , width, and height of the dialog box in dialog box units (not the same as pixels).

声明对话框的名字,在这个例子中, “MyDialog”后跟随着关键字“DIALOG”。后面那四个数是:对话框的 x坐标,y坐标,宽,和高。它们用对话框的单位。

(注意:是对话框的单位,并不一定是像素 )

STYLE 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX |
WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME | DS_3DLOOK

Declare the styles of the dialog box.

声明对话框的样式。 

CAPTION "Our First Dialog Box"

This is the text that will appear in the dialog box's title bar.

这个文本将出现在对话框的标题栏。

CLASS "DLGCLASS"

This line is crucial. It's this CLASS keyword that allows us to use the dialog box template as a window class. Following the keyword is the name of the "window class"

这一行是至关紧要的。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

The above block defines the child window controls in the dialog box. They're defined between BEGIN and END keywords. Generally the syntax is as follows:

上面这块代码定义在对话框中的子窗口控件.它们必须写在BEGINEND关键字之间.一般语法如下:

control-type  "text"   ,controlID, x, y, width, height [,styles]

control-types are resource compiler's constants so you have to consult the manual.

因为控件类型是资源编辑器的常数值,(宏定义)所以你必须参照手册来定义它们.

 


Now we go to the assembly source code. The interesting part is in the window class structure:

现在我们继续组合源代码. 有趣味的部分在窗口类结构中.

mov   wc.cbWndExtra,DLGWINDOWEXTRA
mov   wc.lpszClassName,OFFSET ClassName

Normally, this member is left NULL, but if we want to register a dialog box template as a window class, we must set this member to the value DLGWINDOWEXTRA. Note that the name of the class must be identical to the one following the CLASS keyword in the dialog box template. The remaining members are initialized as usual. After you fill the window class structure, register it with RegisterClassEx. Seems familiar? This is the same routine you have to do in order to register a normal window class.

 

正常的,这个成员的左值是空( NULL),但是如果我们想把一个对话框模版注册为一个窗口类,我们必须设置这个成员的值为DLGWINDOWEXTRA. 注意: 类名必须和对话框模版中CLASS关键字后面的跟随的类名一致.剩下的成员照常被初始化.当你填满这个窗口类结构并用RegisterClassEx注册它后.看看, 像熟客吗?这和你注册一个普通的窗口是一样的例程吧.

 

invoke CreateDialogParam,hInstance,ADDR DlgName,NULL,NULL,NULL

After registering the "window class", we create our dialog box. In this example, I create it as a modeless dialog box with CreateDialogParam function. This function takes 5 parameters but you only have to fill in the first two: the instance handle and the pointer to the name of the dialog box template. Note that the 2nd parameter is not a pointer to the class name.

再注册了这个窗口类"后,我们创建我们的对话框.在这个例子中,我用CreateDialogParam函数创建一个模式对话框.这个函数有5个参数,但是你只需要填写前两个:实例句柄和一个指向对话框模版名的指针.注意:第二个参数并不是指向类名的指针.


At this point, the dialog box and its child window controls are created by Windows. Your window procedure will receive WM_CREATE message as usual.

 

在这里,对话框和它的子窗口控件是windows创建的. 你的窗口过程将照常接收到WM_CREATE消息.

 

invoke GetDlgItem,hDlg,IDC_EDIT
invoke SetFocus,eax

After the dialog box is created, I want to set the input focus to the edit control. If I put these codes in WM_CREATE section, GetDlgItem call will fail since at that time, the child window controls are not created yet. The only way you can do this is to call it after the dialog box and all its child window controls are created. So I put these two lines after the UpdateWindow call. GetDlgItem function gets the control ID and returns the associated control's window handle. This is how you can get a window handle if you know its control ID.

 

当对话框被创建后,我想设置输入焦点给编辑框.如果我把这些代码放在WM_CREATE节区中,GetDlgItem将失效因为在那个时候,这个子窗口控件还没有被创建.你唯一的方式就是等对话框和这个对话框的所有子控件被创建后再这样做.所以我们把这两行代码放在UpdateWindow调用之后.GetDlgItem函数获取控件ID并返回与这个控件窗口相关联的窗口句柄.所以如果你知道一个控件的ID你就能得到和它相关联的窗口句柄.

       invoke IsDialogMessage, hDlg, ADDR msg
        .IF eax ==FALSE
            invoke TranslateMessage, ADDR msg
            invoke DispatchMessage, ADDR msg
        .ENDIF

The program enters the message loop and before we translate and dispatch messages, we call IsDialogMessage function to let the dialog box manager handles the keyboard logic of our dialog box for us. If this function returns TRUE , it means the message is intended for the dialog box and is processed by the dialog box manager. Note another difference from the previous tutorial. When the window procedure wants to get the text from the edit control, it calls GetDlgItemText function instead of GetWindowText. GetDlgItemText accepts a control ID instead of a window handle. That makes the call easier in the case you use a dialog box.

这个窗口进入消息循环,在我们翻译和分派消息之前,我们调用IsDialogMessage函数来让对话框管理器为我们处理我们对话框的键盘逻辑.如果这个函数返回值是TRUE, 它意味着这个消息是打算分派给对话框使用的并且已经被对话框管理器处理.在这里注意和先前课程中的一个不同之处.当这个窗口过程想从编辑控件中得到文本时它调用GetDlgItemText函数而不是GetWindowText函数.GetDlgItemText接收一个控件的ID来替代窗口句柄。这样让你在对话框中调用这个函数变得更容易。



Now let's go to the second approach to using a dialog box as a main window. In the next example, I 'll create an application modal dialog box. You'll not find a message loop or a window procedure because they're not necessary!

现在让我们转到用对话框作为我们主窗口的第二种方法。在下一个例子中,我将创建一个应用程序模式对话框,你将找不到消息循环或者是窗口过程因为它们是不需要的。


dialog.asm (part 2)



.386
.model flat,stdcall
option casemap:none

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


dialog.rc (part 2)



#include "resource.h"

#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



The analysis follows:

分析如下:

    DlgProc proto :DWORD,:DWORD,:DWORD,:DWORD

We declare the function prototype for DlgProc so we can refer to it with addr operator in the line below:

我们声明函数的类型为DlgProc 所以我们能在下面用addr操作引用它:

    invoke DialogBoxParam, hInstance, ADDR DlgName,NULL, addr DlgProc, NULL

The above line calls DialogBoxParam function which takes 5 parameters: the instance handle, the name of the dialog box template, the parent window handle, the address of the dialog box procedure, and the dialog-specific data. DialogBoxParam creates a modal dialog box. It will not return until the dialog box is destroyed.

上面这行调用的DialogBoxParam函数有5个参数:实例句柄, 一个对话框模版的名字,父窗口句柄,对话框过程的地址,还有对话框特有的数据。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

The dialog box procedure looks like a window procedure except that it doesn't receive WM_CREATE message. The first message it receives is WM_INITDIALOG. Normally you can put the initialization code here. Note that you must return the value TRUE in eax if you process the message.
The internal dialog box manager doesn't send our dialog box procedure the WM_DESTROY message by default when WM_CLOSE is sent to our dialog box. So if we want to react when the user presses the close button on our dialog box, we must process WM_CLOSE message. In our example, we send WM_COMMAND message with the value IDM_EXIT in wParam. This has the same effect as when the user selects Exit menu item. EndDialog is called in response to IDM_EXIT.

这个对话框过程看起来像一个窗口过程除了了它不接受WM_CREATE消息外.第一个被他接收的消息是WM_INITDIALOG.通常你能在这放置一些初始化代码.注意, 如果你处理了这个消息,你必须在eax中返回TRUE. WM_CLOSE消息被发送给我们的对话框过程时,内部的对话框管理器并不发送WM_DESTROY这个消息给对话框过程.所以当用户按了对话框的关闭按钮的时候我们如果想让它起作用,我们就必须处理WM_CLOSE消息.在我们的例子中,我们发送WM_COMMAND消息并置wParam的值为IDM_EXIT.这样做和用户选择退出菜单项是一样的效果.EndDialog函数被调用来响应IDM_EXIT.

The processing of WM_COMMAND messages remains the same.
When you want to destroy the dialog box, the only way is to call EndDialog function. Do not try DestroyWindow! EndDialog doesn't destroy the dialog box immediately. It only sets a flag for the internal dialog box manager and continues to execute the next instructions.

这和处理WM_COMMAND消息是一样的.

当你想销毁一个对话框时, 唯一的方式就是调用EndDialog函数.不要尝试DestroyWindow! EndDialog并不立即销毁这个对话框.它仅为内部的对话框管理器设置一个标志字然后继续执行下一条指令.


Now let's examine the resource file. The notable change is that instead of using a text string as menu name we use a value, IDR_MENU1. This is necessary if you want to attach a menu to a dialog box created with DialogBoxParam. Note that in the dialog box template, you have to add the keyword MENU followed by the menu resource ID.
A difference between the two examples in this tutorial that you can readily observe is the lack of an icon in the latter example. However, you can set the icon by sending the message WM_SETICON to the dialog box during WM_INITDIALOG.

现在我们来分析一下资源文件. 最显著的改变是这里用一个值-IDR_MENU1代替作为菜单名的字符串.如果你想调用DialogBoxParam函数创建对话框和一个菜单相关联,这就是必须的.(指用ID值代替菜单名)注意在对话框模版中,你必须增加一个菜单资源ID在关键字MENU的后面.

你能很容易的发现这一课的两个例子有一个不同之处就是后一个例子缺少一个图标.然而,你同样可以设置这个图标.只需在对话框处理WM_INITDIALOG消息的时候发送一个WM_SETICON给对话框并添加相应的代码即可.


This article come from Iczelion's asm page

风向改变 翻译于 20071220

 

阅读(2900) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~