[Win32]创建模态窗口

Win32编程中,如果要显示一个模态窗口,一般是先创建对话框模板,然后使用DialogBox来显示对话框。这种做法很简单,但存在一个问题:对话框是以资源的形式保存在可执行文件中的,如果可执行文件没有进行加壳处理的话,任何人都可以通过ExeScope等资源修改工具修改对话框的内容,这对于含有版权信息的“关于”对话框来说是非常危险的,怀有不良目的的人只需进行简单的操作就可以将一个软件变成是自己的。

 

保护软件的版权信息不被修改有很多种方法,在这里我想说的一种方法是用代码来创建“关于”对话框的内容,在运行时才创建这个对话框,而不是通过对话框模板早早就创建好。既然不使用对话框模板,那就不能使用DialogBox函数来显示模态的对话框了,所以这种方法的一个难点就是如何不使用DialogBox来创建一个模态的窗口。本文的重点在于如何解决这个难点,而不在于如何在运行时创建窗口的内容。

 

通过查阅MSDN,可以知道DialogBox实际上是一个宏,它调用DialogBoxParam函数来创建对话框。而后者通过CreateWindowEx函数创建窗口,然后disables父窗口,并开启一个新的消息循环来处理对话框的消息。我们可以自己实现这个过程。

 

首先创建一个父窗口:

 1 #include <windows.h>
 2 #include "resource.h"
 3 
 4 LRESULT CALLBACK DialogProc(HWND, UINT, WPARAM, LPARAM);
 5 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
 6 void DisplayModelDialog(HWND hParent);
 7 
 8 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) {
 9 
10     WNDCLASS wndclass;
11     wndclass.style = CS_HREDRAW | CS_VREDRAW;
12     wndclass.cbClsExtra = 0;
13     wndclass.cbWndExtra = 0;
14     wndclass.hInstance = hInstance;
15     wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
16     wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
17     wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
18     wndclass.lpfnWndProc = DialogProc;
19     wndclass.lpszMenuName = NULL;
20     wndclass.lpszClassName = TEXT("Dialog");
21 
22     RegisterClass(&wndclass);
23   
24     wndclass.lpfnWndProc = WndProc;
25     wndclass.lpszMenuName = TEXT("Menu");
26     wndclass.lpszClassName = TEXT("Parent");
27      
28     RegisterClass(&wndclass);
29     
30     HWND hwnd = CreateWindow(
31     TEXT("Parent"), 
32     TEXT("Parent"),
33         WS_OVERLAPPEDWINDOW,
34     CW_USEDEFAULT, 
35     CW_USEDEFAULT,
36     CW_USEDEFAULT,
37     CW_USEDEFAULT,
38     NULL, 
39     NULL, 
40     hInstance, 
41     NULL);
42     
43     ShowWindow(hwnd, iCmdShow);
44     UpdateWindow(hwnd);
45     
46     MSG msg;
47     while(GetMessage(&msg, NULL, 00)) {
48     
49          TranslateMessage(&msg);
50          DispatchMessage(&msg);
51     }
52     return msg.wParam;
53 }
54 
55 
56 LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
57 
58     switch (message) {
59 
60     case WM_COMMAND:
61         if (LOWORD(wParam) == ID_FILE_DIALOG) {
62         DisplayModelDialog(hwnd);
63         }
64         return 0;
65 
66     case WM_DESTROY:
67         PostQuitMessage(0);
68         return 0;
69     }
70 
71     return DefWindowProc(hwnd, message, wParam, lParam);
72 }
73 

 

WinMain函数的开头注册了两个窗口类,第一个“Dialog”窗口类是供模态窗口使用的,它使用DialogProc窗口过程;第二个“Parent”窗口是供父窗口使用的,它使用WndProc窗口过程。

 

父窗口含有一个名为“Menu”的菜单,该菜单有一个“Dialog”菜单项,点击这个菜单项之后调用DisplayModelDialog函数来显示模态的窗口。

 

DisplayModelDialog函数是显示模态窗口的关键,下面是它的实现代码:

 1 void DisplayModelDialog(HWND hParent) {
 2 
 3     EnableWindow(hParent, FALSE);
 4 
 5     HWND hDlg = CreateWindow(
 6     TEXT("Dialog"),
 7     TEXT("Dialog"),
 8     WS_OVERLAPPEDWINDOW | WS_VISIBLE,
 9     CW_USEDEFAULT,
10     CW_USEDEFAULT,
11     CW_USEDEFAULT,
12     CW_USEDEFAULT,
13     hParent,
14     NULL,
15     (HINSTANCE)GetWindowLong(hParent, GWL_HINSTANCE),
16     NULL);
17 
18     ShowWindow(hDlg, SW_SHOW);
19     UpdateWindow(hDlg);
20 
21     MSG msg;
22 
23     while (GetMessage(&msg, NULL, 00)) {
24 
25     TranslateMessage(&msg);
26         DispatchMessage(&msg);
27     }
28 
29     EnableWindow(hParent, TRUE);
30     SetForegroundWindow(hParent);
31 }
32 
33 LRESULT CALLBACK DialogProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
34 
35     switch (message) {
36 
37     case WM_DESTROY:
38         PostQuitMessage(0);
39         return 0;
40     }
41 
42     return DefWindowProc(hwnd, message, wParam, lParam);
43 }
44 

 

上面的代码看上去几乎就是一个新的Windows应用程序,不同的地方在于,函数的开头和结尾增加了对EnableWindowSetForegroundWindow的调用。如果没有对SetForegroundWindow的调用,在关闭模态窗口之后父窗口会最小化。

 

运行程序,点击菜单上的Dialog项,就可以弹出一个模态窗口,点击父窗口任意位置,模态窗口会闪烁,就像模态对话框的行为一样。现在,可以修改模态窗口的样式,并处理WM_CREATE消息在窗口中增加内容了。

posted on 2011-02-20 14:05  Zplutor  阅读(7214)  评论(2编辑  收藏  举报