相信很多人在VC下开发程序的时候大多都会采用基于对话框的开发的吧, 我也不例外,大多数的小型测试程序没有必要开发基于文档,
视图的结构来测试,
只要使用一些基本的对话框的程序就可以达到这个目的. 但是在开发基于对话框的程序时,要使用到一些 Spy++ 的功能检测的时候,
就会出现一些问题.
什么问题?
我使用 Spy++ 去检测一个对话框的窗口类的, 并想得到一个对话框的窗口类, 以便我在使用钩子的时候可以指定一个对话框进行 hook,
但是出乎我意料之外, 一个对话框的窗口类不是我在注册时所指定的对话框窗口类名.
它的类名是 "#32770(Dialog)" 是一个MFC自动为基于对话框所产生的默认窗口类, 所有的基于MFC所产生的对话框程序都是使
用这一个默认类名, 即是说, 我在使用一个基于对话框的程序时, 无论多少个对话框产生, 它们的类名都会是 "#32770(Dialog)"
这样我在打开对话框进行测试时,无法指定我需要的对话框的句柄.
但是,当指定一个对话框的窗口标题的时候,这个对话框的名柄就可以找到了
HWND hWnd = MULL;
hWnd = FindWindow( "#32770", lpszWindowName );
_ASSERT( hWnd != NULL );
//其中 lpszWindowName 是对话框的窗口标题目.
这种方法也有一定的缺点,就是一个对话框的标题目不确定时会怎么样,或对话框的标题在运行过程中改变呢? 这样根本无法保证所找到
的句柄是所需要的句柄.
最好的方法就是在对话框的产生过程中为对话框指定一个唯一的窗口类,这样的方法就可以找到想要的指定句柄的,而不必与其它的对话
框混淆.
HWND hWnd = MULL; hWnd = FindWindow( lpszClassName, NULL ); _ASSERT( hWnd != NULL ); //其中 lpszClassName 是对话框的窗口类名.
那怎么样实现自已定制的对话框类!
看过<<深入浅出MFC>>的读者一定会想到, 在重载的 CWinApp 的 InitInstance() 函数中进行修改,不错, 确实要在这儿修改.
// 在派生类的 InitIntace() 中 BOOL CLimitDlgInstanceApp::InitInstance() { ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// WNDCLASS wc; // Get the info for this class. // #32770 is the default class name for dialogs boxes. ::GetClassInfo(AfxGetInstanceHandle(), "#32770", &wc); // Change the name of the class. wc.lpszClassName = "MyPrivateClassName"; // Register this class so that MFC can use it. AfxRegisterClass(&wc); ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// // ... }
这里采用的方法是在 产生注册窗口时, 将注册窗口的窗口类名修改. 再重新注册窗口类, 一切看来很顺利,也不是非常难的操作,
但是一切都如你预期一样么.
很不辛,你再打开 Spy++ 观察窗口的时候, 仍是 "#32770(Dialog)"
好了,你有其它办法吗? MSDN在这个时候还是最有用的,缺少 MSDN 就如在没有桨的船,
MSDN 中提供了两种方法让我们可以定制自已的对话框窗口类.
第一种:
1. 打开这个对话框工程文件, 打开 ResourceView.
2. 打开 Resource Editor, 右击对话框,选择选项 Properties, 在对话框的属性项中, 最下角是一个
类名的输入项, 但是这一个选项是禁止的, 你无法在些输入类名, 因为你在这里是选择了 MFC 类库的
支持. 为了使这个选项可以输入. 如图所示, 在Resource View 的最顶项选择 右键->属性, 就会弹出
一个资源文件属性对话框, 把其中的 Enable MFC Features 的项设为禁止, 则对话框的类名就可以在
在资源编辑器中设定了. (在Visual C++ .NET, 设置 MFC Mode property 属性为 FALSE)
第二种可供选择的方法就是修改 RC 文件和源代码!
在 CWinApp 的派生类的 Initinstance 函数中进行修改
// 在派生类的 InitIntace() 中 BOOL CLimitDlgInstanceApp::InitInstance() { ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// WNDCLASS wc; // Get the info for this class. // #32770 is the default class name for dialogs boxes. ::GetClassInfo(AfxGetInstanceHandle(), "#32770", &wc); // Change the name of the class. wc.lpszClassName = "MyPrivateClassName"; // Register this class so that MFC can use it. AfxRegisterClass(&wc); ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// // ... }
其中 ::GetClassInfo 保证了即使你的资源文件在不同的 Dll 中也能正确得到 HINSTANCE
跟着就是要修改资源文件了, 用文本编辑器打开 rc 文件, 加上" ClASS 类名 "如下图
注意, 如果你 rc 文件中的类名与 Initinstance 中的类名不一致, 程序不会运行, 这是非常要注意的. 切记.
结束语:
这只是一个小技巧, 但是对于开发帮助有很大的用处, 例如在开发基于对话框的程序中, 使整个运行过程中只有一个实例
运行, 注册唯一的窗口类是非常有用的. 或作一个进行程序监控的时候, 可以快速定位名柄.