Bootstrap

《Windows API每日一练》10.2 非模态对话框

在本章开始,解释了对话框可以是“模态”或“非模态”的。上一节中,我们详细讲述了模态对话框。模态对话框(除了系统模态对话框外)允许用户在对话框和其他程序之间切换。但是,用户无法切换到同一程序的另一个窗口中,直到该模态对话框被销毁。非模态对话框则允许用户在对话框和窗口之间,以及在对话框和其他程序之间进行切换。非模态对话框因此更接近于你的程序产生的正常弹出窗口。

如果在一段时间内,一直要显示某个对话框,用户更倾向于使用非模态对话框,因为它更方便。举例来说,字处理程序经常使用非模态对话框来实现文本查找和修改对话框。如果查找对话框是一个模态对话框,用户将不得不从菜单中选择査找,输入想找的字符串,结束对话框以返回到文档,然后重复整个过程査找同一字符串的另一实例。允许在文档和对话框之间进行切换使用户的操作更方便。

使用DialogBox能产生模态对话框。只有对话框销毁后,函数才返回一个值。它返回的值是EndDialog的第二个参数所指定的,对话框过程使用该函数来终止对话框。调用CreateDialog可以创建非模态对话框。此函数的参数与DialogBox相同:

hDlgModeless = CreateDialog ( hInstance, szTemplate, hwndParent, DialogProc) ;

不同的是,CreateDialog函数会立即返回对话框的窗口句柄。通常,你把这个窗口句柄存储在一个全局变量中。

本节我们将介绍非模态对话框的使用方法。

本节必须掌握的知识点:

        模态与非模态对话框的区别

        第66练:非模态对话框中创建滚动条控件

        第67练:十六进制计算器

10.2.1 模态与非模态对话框的区别

非模态对话框与模态对话框的原理是类似的,但也有一些重要的不同。

■首先,非模态对话框通常包括标题栏和系统菜单框。当你在VS中创建一个对话框时,这实际上是默认选项。非模态对话框模板的样式语句如下:

STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_VISIBLE

标题栏和系统菜单允许用户使用鼠标或键盘把非模态对话框移动至其他地方。你通常不会为模态对话框提供一个标题栏和系统菜单,因为用户对它下面的窗口根本无法做任何事情。

■第二个显著差异:请注意,WS_VISIBLE样式被包含在我们的样式语句的例子中。在

VS中,请在Dialog 属性对话框的More Styles选项卡中选中此选项。如果你省略了 WS_VISIBLE,则必须在调用CreateDialog后调用ShowWindow显示对话框窗口:

hDlgModeless = CreateDialog ( . . . ) ;

ShowWindow (hDlgModeless, SW_SHOW) ;

如果你既不包括WS_VISIBLE也不调用ShowWindow,那么该非模态对话框将不会被显示。 掌握了模态对话框的程序员往往忽视这一特殊性,因此经常在第一次试图创建一个非模态对话框时遇到困难。

■第三个差异:与模态对话框和消息框的消息不同的是,非模态对话框的消息要进入你 程序的消息队列。而消息队列必须经过改动才能把这些消息传递给对话框窗口过程。具体做法如下:使用CreateDialog创建一个非模态对话框时,把该函数返回的对话框句柄保存到一个全局变量(例如hDlgModeless)。如下更改消息循环:

while (GetMessage (&msg, NULL, 0, 0))

{

       if (hDlgModeless == 0 || !IsDialogMessage (hDlgModeless, &msg))

       {

              TranslateMessage (&msg) ;

              DispatchMessage (&msg) ;

       }

}

如果消息是针对非模态对话框的,IsDialogMessage就会将其发送到对话框窗口过程并返回 TRUE(非零),否则返回FALSE(0)。只有当hDlgModeless为0或者该消息不是给对话框的才应该调用TranslateMessage和DispatchMessage函数。如果对程序窗口还使用了键盘快捷键,那么消息循环看起来会像下面这样:

while (GetMessage (&msg, NULL, 0, 0))

{

       if (hDlgModeless == 0 || !IsDialogMessage (hDlgModeless, &msg))

       {

              if (!TranslateAccelerator (hwnd, hAccel, &msg))

              {

                     TranslateMessage (&msg) ;

                     DispatchMessage (&msg) ;

              }

       }

}

因为全局变量初始化为0,所以hDlgModeless将保持为0直到对话框被创建为止,从而确保了IsDialogMessage不会在使用无效窗口句柄的情况下被调用。当你销毁非模态对话框时,你必须采取同样的预防措施,原因下面会有解释。

hDlgModeless变量也可以用在程序的其他地方,用来检测非模态对话框是否存在。例 如,当hDlgModeless不等于0时,程序中的其他窗口可以将消息发送到该对话框。

■最后的大区别:请使用DestroyWindow而不是EndDialog来结束非模态对话框。当你 调用DestroyWindow时,还要把hDlgModeless全局变量设置为NULL 。

用户习惯于从系统菜单中选择Close来终止非模态对话框。虽然Close选项是可用的, Windows内的对话框窗口过程并不处理WM_CLOSE消息。你必须在对话框过程中自己完成这个任务:

case WM_CLOSE :

       DestroyWindow (hDlg) ;

       hDlgModeless = NULL ;

       break ;

【注意】这两个窗口句柄的区别:DestroyWindow中的hDlg参数是传递给对话框过程的参 数:hDlgModeless则是从CreateDialog返回的、用来在消息循环中进行检测的全局变量。

也可以允许用户使用按钮来关闭非模态对话框。对此应使用和WM_CLOSE消息相同的逻辑。任何对话框必须“返回”给创建它的窗口的信息,都可以存储在全局变量中。如果不希望使用全局变景,那么如前所述,可以使用CreateDialogParam来创建非模态对话框,并给它传递一个指向结构的指针。

图10-10 Windows内置的对话框代码

      

如上图所示,模态对话框的消息循环使用Windows系统内置的消息循环,包含一个自定义对话框窗口过程和系统内建的默认对话框窗口过程。非模态对话框则和主窗口过程共用同一个消息循环。同样也包含一个自定义对话框窗口过程和系统内建的默认对话框窗口过程。

10.2.2 第66练:非模态对话框中创建滚动条控件

/*------------------------------------------------------------------

066  WIN32 API 每日一练

     第66个例子COLORS2.C:非模态对话框

     CreateDialog函数

     IsDialogMessage 函数

     SetDlgItemInt函数

(c) www.bcdaren.com, 2020

----------------------------------------------------------------*/

#include <windows.h>

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;

BOOL CALLBACK ColorScrDlg (HWND, UINT, WPARAM, LPARAM) ;

HWND hDlgModeless ;

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,

 PSTR szCmdLine, int iCmdShow)

{

     static TCHAR szAppName[] = TEXT("Colors2");

     HWND hwnd;

    (略)

     hwnd = CreateWindow(szAppName, TEXT("Color Scroll"),

         WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,//不擦除对话框的情况下重绘主窗口

          CW_USEDEFAULT, CW_USEDEFAULT,

          CW_USEDEFAULT, CW_USEDEFAULT,

          NULL, NULL, hInstance, NULL);

     ShowWindow(hwnd, iCmdShow);

     UpdateWindow(hwnd);

//创建非模态对话框

     hDlgModeless = CreateDialog(hInstance, TEXT("ColorScrDlg"),

          hwnd, ColorScrDlg);

     while (GetMessage(&msg, NULL, 0, 0))

     {//非模态对话框句柄为0或者不是非模态对话框消息时

          if (hDlgModeless == 0 || !IsDialogMessage(hDlgModeless, &msg))

          {

               TranslateMessage(&msg);

               DispatchMessage(&msg);

          }

     }

     return msg.wParam;

}

LRESULT CALLBACK WndProc ( HWND hwnd, UINT message, WPARAM wParam,LPARAM

lParam)

{

     switch (message)

     {

     case WM_DESTROY:

          DeleteObject((HGDIOBJ)SetClassLong(hwnd, GCL_HBRBACKGROUND,//删除画刷

               (LONG)GetStockObject(WHITE_BRUSH)));

          PostQuitMessage(0);

          return 0;

     }

     return DefWindowProc(hwnd, message, wParam, lParam);

}

//非模态对话框窗口过程

BOOL CALLBACK ColorScrDlg ( HWND hDlg, UINT message,WPARAM wParam, LPARAM

lParam)

{

      static int iColor[3] ;

      HWND hwndParent, hCtrl ;

      int iCtrlID, iIndex ;

      switch (message)

      {

      case WM_INITDIALOG : //设置3条滚动条的滚动范围和位置

           for (iCtrlID = 10; iCtrlID < 13; iCtrlID++)

           {

                hCtrl = GetDlgItem(hDlg, iCtrlID);

                SetScrollRange(hCtrl, SB_CTL, 0, 255,

                     FALSE);

                SetScrollPos(hCtrl, SB_CTL, 0, FALSE);

           }

           return TRUE;

      case WM_CLOSE://增加的---销毁对话框代码

           DestroyWindow(hDlg);

           hDlgModeless = NULL;//养成好习惯,防止出错

           break;

      case WM_VSCROLL : //滚动条处理

           hCtrl = (HWND) lParam ;

           iCtrlID = GetWindowLong (hCtrl, GWL_ID) ;

           iIndex = iCtrlID - 10 ;

           hwndParent = GetParent (hDlg) ;

           switch (LOWORD (wParam))

           {

           case SB_PAGEDOWN :

               iColor[iIndex] += 15 ; //继续往下执行

           case SB_LINEDOWN :

                iColor[iIndex] = min (255, iColor[iIndex] + 1) ;

                break ;

           case SB_PAGEUP :

               iColor[iIndex] -= 15 ; //继续往下执行

           case SB_LINEUP :

                iColor[iIndex] = max (0, iColor[iIndex] - 1) ;

                break ;

           case SB_TOP :

                iColor[iIndex] = 0;

                break;

           case SB_BOTTOM :

                iColor[iIndex] = 255 ;

                break ;

           case SB_THUMBPOSITION :

           case SB_THUMBTRACK :

                iColor[iIndex] = HIWORD(wParam);

                break;

           default:

                return FALSE;

           }

           SetScrollPos (hCtrl, SB_CTL,iColor[iIndex], TRUE) ; //设置滑块位置

//设定颜色文本数值

           SetDlgItemInt (hDlg, iCtrlID + 3, iColor[iIndex], FALSE) ;

            //替换画刷

           DeleteObject ((HGDIOBJ) SetClassLong (hwndParent,GCL_HBRBACKGROUND,

           (LONG) CreateSolidBrush ( RGB (iColor[0], iColor[1], iColor[2])))) ;

           InvalidateRect (hwndParent, NULL, TRUE) ; //重绘窗口

           return TRUE ;

     }

      return FALSE ;

}

/*****************************************************************************

CreateDialog函数:用于创建非模态对话框的函数,它是在 Windows 操作系统平台上使用的函数。

它允许开发人员通过资源模板创建对话框,并将其显示在应用程序的窗口中。

HWND CreateDialog(

    HINSTANCE hInstance,     // 应用程序实例句柄

    LPCTSTR lpTemplateName,  // 对话框模板的名称或标识符

    HWND hWndParent,         // 父窗口的句柄

    DLGPROC lpDialogFunc     // 对话框过程函数的地址

);

返回值:

如果函数调用成功,将返回非模态对话框的句柄(HWND)。

如果函数调用失败,将返回 NULL。可以使用 GetLastError 函数获取具体的错误代码。

注意事项:

在使用 CreateDialog 函数之前,需要先定义和实现一个对话框过程函数(Dialog Procedure),用于处理对话框的消息。

对话框过程函数的格式和规则需要遵循特定的要求,以正确处理对话框的消息。

对话框过程函数中可以使用一些 Windows API 函数来操作对话框的控件,例如 GetDlgItem、SetDlgItemText 等。

******************************************************************************

IsDialogMessage函数:用于在消息循环中处理特定于对话框的键盘输入消息。

它检查指定的消息是否为对话框消息,并在是对话框消息的情况下执行相应的处理,例如处理 Tab 键切换焦点、处理默认按钮等。

以下是 IsDialogMessage 函数的函数原型:

BOOL IsDialogMessage(

    HWND hDlg,//对话框的句柄

    LPMSG lpMsg//指向 MSG 结构体的指针,用于传递要处理的消息

);

返回值:

如果函数处理了消息,则返回值为 TRUE。

如果消息不是对话框消息,或者没有进行任何处理,则返回值为 FALSE。

注意事项:

IsDialogMessage 函数通常用于消息循环中,以便在消息循环中处理对话框特定的消息。

如果消息是对话框消息,并且被 IsDialogMessage 函数处理,函数会执行相应的操作,并返回 TRUE,告知消息已被处理。

如果消息不是对话框消息,或者没有被 IsDialogMessage 函数处理,函数将返回 FALSE,表示消息需要继续传递给其他消息处理机制进行处理。

******************************************************************************

SetDlgItemInt函数:用于设置对话框中指定编辑框控件的整数值的函数。它将整数值转换为字符串,并将其显示在指定的编辑框中。

以下是 SetDlgItemInt 函数的函数原型:

BOOL SetDlgItemInt(

    HWND hDlg,      // 对话框的句柄

    int nIDDlgItem,  // 编辑框控件的标识符

    UINT uValue,    // 要设置的整数值

    BOOL bSigned    // 指定整数值是否为有符号数

);

返回值:

如果函数调用成功,返回值为 TRUE。

如果函数调用失败,返回值为 FALSE。

注意事项:

SetDlgItemInt 函数将整数值转换为字符串,并将字符串显示在指定的编辑框中。如果编辑框中已经包含了其他文本,该函数将替换掉原有的文本。

如果整数值超出了编辑框的表示范围,函数将尽可能地在编辑框中显示该值。但请注意,超出范围的值可能会被截断或显示为错误的结果。

******************************************************************************

DestroyWindow函数:用于销毁指定窗口,并释放与其关联的系统资源。

当窗口不再需要时,可以使用 DestroyWindow 函数来关闭窗口并清理相关资源。

以下是 DestroyWindow 函数的函数原型:

BOOL DestroyWindow(

    HWND hWnd//要销毁的窗口的句柄

);

返回值:

如果函数调用成功,返回值为非零值 (TRUE)。

如果函数调用失败,返回值为零 (FALSE)。

*/

       资源脚本文件066_COLORS2.rc包含一个ICON资源和一个对话框资源。

/

//

// Dialog

//

COLORSCRDLG DIALOGEX 0, 0, 147, 177

STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION

CAPTION "Color Scroll Scrollbars"

FONT 8, "MS Shell Dlg", 400, 0, 0x1

BEGIN

    CTEXT           "Red",IDC_STATIC,19,24,21,8

    CTEXT           "Green",IDC_STATIC,60,25,22,8

    CTEXT           "Blue",IDC_STATIC,97,26,22,8

    CTEXT           "0",13,19,142,22,8

    CTEXT           "0",14,59,143,22,8

    CTEXT           "0",15,95,142,22,8

    SCROLLBAR       10,23,48,10,85,SBS_VERT

    SCROLLBAR       11,65,50,10,85,SBS_VERT

    SCROLLBAR       12,102,50,10,85,SBS_VERT

END

/

//

// Icon

//

// Icon with lowest ID value placed first to ensure application icon

// remains consistent on all systems.

Color2                  ICON                    "Main.ico"

运行结果:

图10-11 非模态对话框

 

总结

实例COLORS2.C添加了一个非模态对话框资源,对话框内包含3个滚动条子窗口控件,和6个静态文本控件。相对于在程序中创建这些子窗口控件要简单的多。

●主程序WinMain:调用CreateDialog函数创建一个非模态对话框,绑定非模态对话框窗口过程ColorScrDlg并返回一个非模态对话框句柄。接着在消息循环中调用IsDialogMessage函数,判断从窗口消息队列获取的消息是否是非模态对话框消息。如果是则转给对话框窗口过程处理。如果不是,则继续按照原有方式交给主窗口过程处理。

【注意】因为非模态对话框与主窗口共用一个消息循环,因此在主程序中需要激活非模态对话框窗口并分发对话框消息。

●主窗口过程WndProc:WM_DESTROY消息销毁新创建的画刷(遵循GDI对象三原则)。

●非模态对话框窗口过程ColorScrDlg:

WM_INITDIALOG消息:使用一个for循环语句设定3个滚动条的滑块位置和滚动范围。

WM_CLOSE消息:调用DestroyWindow函数销毁非模态对话框,并将非模态对话框句柄设置为NULL:

hDlgModeless = NULL;//养成好习惯,防止出错

WM_VSCROLL消息:修正滚动条滑块位置,更新3个静态文本控件RGB值,替换新画刷后重绘窗口客户区。

10.2.3 第67练:十六进制计算器

/*------------------------------------------------------------------------

 067 WIN32 API 每日一练

     第67个例子HEXCALC.C:自定义的非模态对话框模板

     DLGWINDOWEXTRA

     CreateDialog函数

     SetDlgItemText函数

 (c) www.bcdaren.com 编程达人

-----------------------------------------------------------------------*/

#include <windows.h>

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,

 PSTR szCmdLine, int iCmdShow)

{

      static TCHAR szAppName[] = TEXT ("HexCalc") ;

      HWND hwnd ;

      MSG msg ;

      WNDCLASS wndclass ;

      wndclass.style = CS_HREDRAW | CS_VREDRAW;

      wndclass.lpfnWndProc = WndProc ;

      wndclass.cbClsExtra = 0 ;

     //如果应用程序使用WNDCLASS在资源文件中注册使用CLASS指令创建的对话框 ,

//则它必须将此成员设置为DLGWINDOWEXTRA。

      wndclass.cbWndExtra = DLGWINDOWEXTRA ; // Note!

      wndclass.hInstance = hInstance ;

      wndclass.hIcon = LoadIcon (hInstance, szAppName) ;

      wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;

      wndclass.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1) ;

      wndclass.lpszMenuName = NULL ;

      wndclass.lpszClassName = szAppName ;

      if (!RegisterClass (&wndclass))

      {

           MessageBox(NULL, TEXT("This program requires Windows NT!"),

                szAppName, MB_ICONERROR);

           return 0;

      }

     //创建非模态对话框---等同于创建窗口,共用窗口过程WndProc(非内置的!)

//没有父窗口,也没有对话框过程

      hwnd = CreateDialog (hInstance, szAppName, 0, NULL) ;

      ShowWindow (hwnd, iCmdShow) ;

      while (GetMessage (&msg, NULL, 0, 0))

      {

           TranslateMessage(&msg);

           DispatchMessage(&msg);

      }

      return msg.wParam ;

}

void ShowNumber (HWND hwnd, UINT iNumber)

{

     TCHAR szBuffer[20];

     wsprintf(szBuffer, TEXT("%X"), iNumber);//显示十六进制数字

     //显示数字的文本框ID为27(即等于VK_ESCAPE)

     SetDlgItemText(hwnd, VK_ESCAPE, szBuffer);//在对话框中设置控件的标题或文本

}

//计算结果,使用C语言算术逻辑运算符

DWORD CalcIt (UINT iFirstNum, int iOperation, UINT iNum)

{

     switch (iOperation)

     {

     case '=': return iNum;

     case '+': return iFirstNum + iNum;

     case '-': return iFirstNum - iNum;

     case '*': return iFirstNum * iNum;

     case '&': return iFirstNum & iNum;

     case '|': return iFirstNum | iNum;

     case '^': return iFirstNum ^ iNum;

     case '<': return iFirstNum << iNum;

     case '>': return iFirstNum >> iNum;

     case '/': return iNum ? iFirstNum / iNum : MAXDWORD;

     case '%': return iNum ? iFirstNum % iNum : MAXDWORD;

     default: return 0;

     }

}

LRESULT CALLBACK WndProc ( HWND hwnd, UINT message, WPARAM wParam,LPARAM

lParam)

{

      static BOOL bNewNumber = TRUE ;

      static int iOperation = '=' ;

      static UINT iNumber, iFirstNum ;

      HWND hButton ;

      switch (message)

      {

      case WM_KEYDOWN: //左方向键执行删除操作

           if (wParam != VK_LEFT)

                break;

           wParam = VK_BACK;

      // fall through

      case WM_CHAR:

          //CharUpper的参数如果高位字为0,表示转换单个字符。

//否则表示一个字符串的指针,将转换字符串。

if ((wParam = (WPARAM) CharUpper ((TCHAR *) wParam)) == VK_RETURN)

//如果是回车键

                wParam = '=' ;

          //获得字符所对应的按钮

           if (hButton = GetDlgItem (hwnd, wParam))

           {

                SendMessage(hButton, BM_SETSTATE, 1, 0);//按下状态,高亮显示

                Sleep(100);

                SendMessage(hButton, BM_SETSTATE, 0, 0);//释放状态

           }

           else

           {

                MessageBeep(0);

                break;

           }

      // 继续往下执行

      case WM_COMMAND: //LOWORD(wParam)为控件ID,也是字符的虚拟键码

           SetFocus (hwnd) ;  //每次单击按钮时,焦点总是设为父窗口。

           if (LOWORD (wParam) == VK_BACK) //退格键

                ShowNumber (hwnd, iNumber /= 16) ;

           else if (LOWORD (wParam) == VK_ESCAPE) //取消键

                ShowNumber (hwnd, iNumber = 0) ;

           else if (isxdigit (LOWORD (wParam))) //十六进制数

           {

                if (bNewNumber)

                {

                     iFirstNum = iNumber;

                     iNumber = 0;

                }

                bNewNumber = FALSE;

//iNumber<=0x0FFFFFFF才可再输入一位,即iNumber最大值=0xFFFFFFFF

                if (iNumber <= MAXDWORD >> 4)

                     ShowNumber(hwnd, iNumber = 16 * iNumber + wParam -

                     (isdigit(wParam) ? '0' : 'A' - 10));//显示数字,数字时

//wParam-'0',A~F时wParam-37h

                else

                     MessageBeep(0);

           }

           else // 操作数

           { //防止连续多次按操作符(如多次按加号),出现重复计算现象

                if (!bNewNumber)

//计算结果并显示

                     ShowNumber(hwnd, iNumber =

                          CalcIt(iFirstNum, iOperation, iNumber));

                bNewNumber = TRUE;

                iOperation = LOWORD(wParam);

           }

           return 0 ;

      case WM_DESTROY:

           PostQuitMessage(0);

           return 0;

      }

      return DefWindowProc (hwnd, message, wParam, lParam) ;

}

/******************************************************************************

DLGWINDOWEXTRA

//如果应用程序使用WNDCLASS在资源文件中注册使用CLASS指令创建的对话框 ,则它必须将此成员设置为DLGWINDOWEXTRA。

wndclass.cbWndExtra = DLGWINDOWEXTRA ; // Note!

*******************************************************************************

CreateDialog函数:

//创建非模态对话框---等同于创建窗口

hwnd = CreateDialog (hInstance, szAppName, 0, NULL) ; //没有父窗口,也没有对话框过程

*******************************************************************************

SetDlgItemText函数:功能是设置对话框中控件的文本和标题。

参数

hDlg:指定含有控件的对话框。

nlDDlgltem:标识带有将被设置的标题和文本的控件。

IpString:指向一个以NULL结尾的字符串指针,该字符串指针包含了将被复制到控件的文本。

返回值:如果函数调用成功,则返回值为非零值。如果函数调用失败,则返回值为零。

*/

       资源脚本文件067_HEXCALC.rc包含一个对话框资源和一个ICON图标资源。

/

//

// Dialog

//

HAXCALC DIALOGEX 65535, 65535, 102, 122

STYLE DS_SETFONT | WS_CAPTION | WS_SYSMENU

CAPTION "Hex Calculator"

CLASS "HexCalc"

FONT 10, "System", 0, 0, 0x0

BEGIN

    PUSHBUTTON      "D",68,8,24,14,14

    PUSHBUTTON      "A",65,8,40,14,14

    PUSHBUTTON      "7",55,8,56,14,14

    PUSHBUTTON      "4",52,8,72,14,14

    PUSHBUTTON      "1",49,8,88,14,14

    PUSHBUTTON      "0",48,8,104,14,14

    PUSHBUTTON      "0",27,26,4,50,14

    PUSHBUTTON      "E",69,26,24,14,14

    PUSHBUTTON      "B",66,26,40,14,14

    PUSHBUTTON      "8",56,26,56,14,14

    PUSHBUTTON      "5",53,26,72,14,14

    PUSHBUTTON      "2",50,26,88,14,14

    PUSHBUTTON      "Back",8,26,104,32,14

    PUSHBUTTON      "C",67,44,40,14,14

    PUSHBUTTON      "F",70,44,24,14,14

    PUSHBUTTON      "9",57,44,56,14,14

    PUSHBUTTON      "6",54,44,72,14,14

    PUSHBUTTON      "3",51,44,88,14,14

    PUSHBUTTON      "+",43,62,24,14,14

    PUSHBUTTON      "-",45,62,40,14,14

    PUSHBUTTON      "*",42,62,56,14,14

    PUSHBUTTON      "/",47,62,72,14,14

    PUSHBUTTON      "%",37,62,88,14,14

    PUSHBUTTON      "Equals",61,62,104,32,14

    PUSHBUTTON      "&&",38,80,24,14,14

    PUSHBUTTON      "|",124,80,40,14,14

    PUSHBUTTON      "^",94,80,56,14,14

    PUSHBUTTON      "<",60,80,72,14,14

    PUSHBUTTON      ">",62,80,88,14,14

END

/

//

// Icon

//

// Icon with lowest ID value placed first to ensure application icon

// remains consistent on all systems.

HEXCALC   ICON   "D:\\code\\windows5.0\\A daily practice\\067_HEXCALC\\favicon.ico"

       运行结果:

图10-12 十六进制计算器

 

总结

       实例HEXCALC.C使用非常简洁的方法实现了一个十六进制计算器。实现的代码似乎有点不合常理。我们看一下实现的过程。

       ●主程序WinMain:窗口类wndclass初始化的时候第一次使用了cbWndExtra字段。

wndclass.cbWndExtra = DLGWINDOWEXTRA ; // Note!

如果应用程序使用WNDCLASS在资源文件中注册使用CLASS窗口类"HexCalc"创建的对话框 ,则它必须将此成员设置为DLGWINDOWEXTRA。

static TCHAR szAppName[] = TEXT ("HexCalc") ;

wndclass.lpszClassName = szAppName ;//主窗口类名就是对话框窗口类名

接下来我们调用CreateDialog函数激活一个非模态对话框窗口。

//创建非模态对话框---等同于创建窗口,共用窗口过程WndProc(非内置的!)

hwnd = CreateDialog (hInstance, szAppName, 0, NULL) ;

有意思的是,这个非模态对话框没有父窗口,也没有自己的对话框过程,主窗口就是对话框窗口,主窗口过程就是对话框窗口过程。

●窗口过程

1.WM_KEYDOWN消息:左方向键等同于回退键。

2.WM_CHAR消息:当按下一个字符按键时,将其转换为大写字母,如果是回车键,将其转换为”=”。接着调用GetDlgItem函数获得字符所对应的button按钮控件,然后调用SendMessage函数向按钮控件发送BM_SETSTATE消息,显示一个高亮状态,然后100毫秒后,改为释放状态。

3.WM_COMMAND消息:

处理退格键:ShowNumber (hwnd, iNumber /= 16) ;

处理ESC取消键:ShowNumber (hwnd, iNumber = 0) ;

第一个十六进制数:将十六进制字符转换为数值。

iNumber<=0x0FFFFFFF才可再输入一位,即iNumber最大值=0xFFFFFFFF。

显示数字,数字时wParam-'0',A~F时wParam-37h。

处理第二个十六进制数:调用CalcIt函数计算结果,并调用ShowNumber函数显示结果。

;