Windows零基础入门:4.7 获取窗口类ID与注册窗口类返回的ID相互验证

3428 人浏览 | 时间: 2015-12-05 19:34:53 | 作者: codexia 会员文章,禁止转载

    获取窗口类ID的方法,在《Windows零基础入门:4.2 获取窗口类信息之查询窗口类名称ID》已经讲述了。代码如下:

WORD wClsID = (WORD)GetClassLong(hwnd,GCW_ATOM);//WORD和ATOM类型一样
    而注册窗口类RegisterClass返回的就是这个窗口类ID,代码如下:
ATOM Class = RegisterClass(&wndClass);// - ATOM类型就是WORD类型
    而我们平常只检测RegisterClass返回值是否为NULL,如果为NULL则表示注册窗口类失败,否则就返回一个非零值,这个非零值其实就是注册成功的窗口类ID。
    实际上,注册返回的窗口类ID就是窗口类表中唯一标识窗口类的ID,与GetClassLong获取的应该一致。不过为了严谨,我们还是要写代码来验证。
    以下是完整的验证代码:
#include "windows.h"
#include <tchar.h>

LRESULT CALLBACK WinProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
{
    return 0;
}
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrev,LPSTR lpCmd,int iShow)
{
    TCHAR ClassName[] = _T("MyClass");
    TCHAR title[] = _T("title");
    WNDCLASS wndClass;
    wndClass.cbClsExtra=0;
    wndClass.cbWndExtra=0;
    wndClass.hbrBackground= (HBRUSH)GetStockObject(WHITE_BRUSH);
    wndClass.hCursor=LoadCursor(NULL,IDC_ARROW);
    wndClass.hIcon=LoadIcon(NULL,IDI_APPLICATION);
    wndClass.hInstance = hInstance;
    wndClass.lpfnWndProc = WinProc;
    wndClass.lpszClassName = ClassName;
    wndClass.lpszMenuName=NULL;
    wndClass.style=CS_HREDRAW|CS_VREDRAW;

    ATOM Class = RegisterClass(&wndClass);// - ATOM类型就是WORD类型
    if(!Class)
    {
        return 0;
    }

    HWND hwnd = CreateWindow(ClassName,title,WS_OVERLAPPEDWINDOW,0,0,100,100,NULL,NULL,hInstance,NULL);
    DWORD Atom = GetClassLong(hwnd,GCW_ATOM);
    if(Class==Atom)
    {
        MessageBox(hwnd,_T("注册窗口类的窗口类ID和通过GetClassLong获取的窗口类ID是一致的"),_T("tip"),0);
    }
}
    你可以运行看看,但是会发现一个问题,那就是,这两个得到的窗口类ID竟然不相同。因为没有弹出这个消息框提示一致。难道是我们理解的有问题??
    然后仔细检查代码,似乎没有什么问题啊,是不是很郁闷。
    这个时候,我们就要发挥调试技巧了。然后从头到尾来排除一下,当然,只需要先在关键点来排除就可以了。那么,关键点就是注册窗口类、创建窗口和获取窗口类ID。我们分别在这个三个地方打上断点:将光标单击在对应的行,然后按F9,就可在这一行打上断点。打上断点后,就在这一行最左边看到一个大大的原点。然后按F5启动调试。当然,如果代码短,就只在第一个关键位置打一个断点,然后单步执行完就可以了。
    启动调试后,第一次停留在注册窗口类的一行,然后按F10单步运行,也就是一次执行一句代码或者是一行代码。单步执行一下,然后检查RegisterClass的返回值,结果发现,这个值是非零值,那么我们根据之前学习的知识,就知道注册窗口类返回非零就是注册成功了。
    既然注册窗口类没有问题,那么继续往后单步执行找问题,然后到了创建窗口函数,执行完这个函数,检查hwnd变量,发现返回的句柄值为0。这个就可以判定出是创建窗口的问题了。你可以继续单步执行完获取窗口类ID的函数,会发现返回的ID值是0。因为窗口句柄是错误的,空句柄,这里返回的窗口类ID自然也是错误的。后面这一步其实也可以不管,只不过说明一个问题就是,前面的错误会导致后面也得不到正确的结果。所以在调试时,请从前面检查,保证前面的步骤都可以得到可靠的结果,然后才往后面继续找Bug。
    这里要补充一下,当执行到当前语句时,在这一行会有一个箭头横向指着代码,指着代码的这行代码是没有被执行的,是准备执行的,所以,要得到这一行代码执行的结果,就要单步执行一下,执行到当前行的下一行,这样当前行执行完就有返回的结果了。所以,要检查RegisterClass的执行结果,就要让当前执行的横向箭头执行指向if(!Class)以及之后的行,就可以查看RegisterClass的返回值,存储在Class变量中,将光标放在Class变量名上面就可以看到了。
    那么我们锁定了问题出在CreateWindow上,那么我们来仔细检查这里的所有参数,然后对照其他人写的正确的代码或者一个个查询MSDN上的CreateWindow函数说明,发现参数没有错误。
    那这个就奇怪了。当然,如果你足够的理性和坚定,那么就将目光转移到其他未检查的地方。因为问题出在创建窗口这句代码,自然后面的代码就不用看了。那么其他相关的代码就是窗口过程函数里了。
    因为偷懒,就在窗口过程函数里只写了一个返回0的代码。然后查阅《windows程序设计》,书中提到了,窗口过程在处理消息是必须返回0,窗口过程不予处理的所有消息应该传给DefWindowProc函数做默认处理。从DefWindowProc函数返回的值必须作为窗口过程函数的返回值。
    了解到这里,我们就知道,我们偷懒将返回值只设置为0可能有问题。那么我将窗口过程函数改为:
LRESULT CALLBACK WinProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
{
    return DefWindowProc(hwnd, message, wParam, lParam);
}
    然后运行程序,结果创建窗口得到了非零的句柄值,表示窗口创建成功了。
    所以,在要省略处理窗口的消息,比如我们只处理一个消息,或者一个消息都不处理的时候,但是窗口过程函数又必须要,我们不能简单返回0,而是要调用默认处理消息的函数来帮我们处理,然后将其返回值作为返回值即可。这里就演示了,如果不这么做,窗口创建失败了,看来不是说着玩的哦。如果你在处理消息的时候,将一些消息不处理,也不丢进默认处理函数DefWindowProc处理,可能会带来很多问题,而一下子也不容易发现,所以鉴于这个演示,请养成一个好的习惯哦。
    至于说,为什么不让默认消息处理函数处理其他消息会导致窗口创建失败,我现在也不知道。如果你知道原因,请告诉我吧,虚心求教。
    本节课重点演示了创建窗口失败的一个原因联合调试技巧、说明验证的原理加深理解。
当前文章为会员文章,请前往[用户中心]开通会员后继续阅读。

Win32课程菜单