正文

《Win32SDK应用程序》第五篇 利用已注册的窗口类来创建一个窗口 2005-10-22 21:00:00

【评论】 【打印】 【字体: 】 本文链接:http://blog.pfan.cn/whyhappy/6246.html

分享到:

第五篇 利用已注册的窗口类来创建一个窗口


白云小飞


一 用CreateWindowEx函数来创建窗口
    上篇中我们完成了向Windows系统进行窗口的“申请”工作(即注册一个窗口类)。本篇就是要用这个窗口类来创建一个窗口。
    下面这个API函数就是专门用来创建一个窗口的:
HWND  WINAPI  CreateWindowEx(
                        DWORD dwExStyle,
                        LPCSTR lpClassName,
                        LPCSTR lpWindowName,
                        DWORD dwStyle,
                        int X,  int Y,
                        int nWidth,  int nHeight,
                        HWND hWndParent ,
                        HMENU hMenu,
                        HINSTANCE hInstance,
                        LPVOID lpParam);
    这是一个拥有一大串参数的函数。(唉!我又要一个个地介绍了。)
    DWORD dwExStyle:这是用来指定扩展样式标志,绝大多数情况,我们只要指定为NULL,所以我不想多说。
    LPCSTR lpClassName:我们要用前篇中注册的窗口类来完成创建窗口,所以lpCassName所指字符串值要与前篇中注册时所用的窗口类名值相同(即wndclass.lpszClassName的值)。本例中就是"WINCLASS1"字符串值。
    LPCSTR lpWindowName:此指针所指的字符串会显示在标题栏上(即标题栏文字)。
    DWORD dwStyle:这是用来指定窗口外观类型的。以下是它可能的值(部分):

                 表 dwStyle可以设置的值
WS_POPUP             弹出式窗口
WS_OVERLAPPED    带有标题栏和边界的重叠式窗口。
WS_OVERLAPPEDWINDOW    具有WS_OVERLAPPED、WS_CAPTION、WS_SYSMENU、WS_THICK、FRAME、WS_MINIMIZEBOX、WS_MAXIMIZEOBX样式的重叠式窗口
WS_VISIBLE    创建之后就立即显示窗口
……    (还有很多其它值呢!请自行参看其它参考书)

    还记得上篇中注册窗口类时窗口类结构体成员中有一项:
     wndclass.style = CS_VREDRAW | CS_HREDRAW | CS_OWNDC | CS_DBLCLKS;
    你可不要搞糊涂啦!wndclass.style所指的是针对窗口内在特性的类型,而CreateWindowEx参数中的dwStyle窗口类型是窗口外观的类型。
    int X,  int Y,:这是指定相对于桌面的窗口左上角位置(坐标)。
    int nWidth,  int nHeight,:指定窗口的宽度与高度。
    (上面这四个参数应该好理解吧!)
    HWND hWndParent :父窗口句柄,不明白它的作用吧?现在你只要赋值为NULL就可。
    HMENU hMenu:你还记得吗?我们在前篇的窗口类结构体变量赋值中有这么一句: wndclass.lpszMenuName =NULL; 它是可以为窗口指定一个菜单的。
    CreateWindowEx函数的这个hMenu菜单句柄(这又是一个句柄噢!)也可以为窗口指定一个菜单,它将代替前面的设置。不过我们现在暂时不要菜单,所以也赋值为NULL。
    HINSTANCE hInstance:要与wndclass.hInstance值相同,本应用程序的句柄代入这里。
    LPVOID lpParam:高级特征,现在我们只要设置为NULL就行。
     (噢,好不容易介绍完这些参数了。)
    返回值:如果窗口成功创建出来,则返回这个窗口的句柄,如果创建不成功,则返回0值。
    下面我就给出具体的创建窗口代码:
    hWnd=CreateWindowEx(NULL,”WINCLASS1”,
                "这是我的第一个窗口",
                WS_OVERLAPPEDWINDOW ,
                0, 0,
                400,400,
                NULL,
                NULL,
                hinstance,
                NULL );
    if (!hWnd)   //这里应该判断是否创建成功
        return 0;
    程序写到这里似乎窗口就可以创建出来了。那就让我们调试调试?OK,Let’s go!请在
    if (!hWnd)
    return 0;   //设断点
    中的return 0处设置一个断点。看看你的程序会不会执行到这个return 0;当创建不成功,则hWnd==0,程序会执行到这个return 0。反之,程序成功地创建了这个窗口。
    (哦!我并不知道你是否会用VC6来设置断点并进行调试。
    只要光标放在return 0 处按F9就可以设置此处的断点。如果你再按F9一次,则会取消这个断点。
    设置完断点后,按F5编译运行这个程序试试。)
    矣?不太对劲啊,调试中我们发现hWnd值为0,即说明窗口并没有创建成功。
    (在这里请再按F5,可以从断点处继续运行,程序结束了。)
    原因是?……

二 调用缺省窗口过程DefWindowProc函数
    原因是:我们还要在这个窗口的自定义窗口过程WinProc中增加如下代码(黑体字部分):
LRESULT CALLBACK WinProc(HWND hwnd,
                         UINT msg,
                         WPARAM wparam,
                         LPARAM lparam)
{
//……
   return DefWindowProc(hwnd, msg, wparam, lparam);
}
    真让我疑惑啊!
    CreateWindowEx函数创建一个窗口与这个DefWindowProc函数什么关系。
    又为什么要把这个函数写在WinProc回调函数里呢?
    首先,看DefWindowProc在Winuser.h中的原型定义:
LRESULT CALLBACK DefWindowProc(
                        HWND hWnd,
                        UINT Msg,
                        WPARAM wParam,
                        LPARAM lParam);
    你有没有看出,它的函数格式与我们自定义WinProc回调函数的格式一完全一样的?只不过DefWindowProc是Window系统提供给我们的一个API函数。
    那么在整个调用CreateWindowEx函数中到底发生了什么事啊?让我来告诉你吧!
    从我们的自定义窗口过程WinPro函数说起:
    当应用程序运行时,用户对该程序窗口的操作,都会自动产生一系列的消息。比如创建一个窗口会产生WM_CREATE消息;移动窗口会产生WM_MOVE消息;按下键盘时会产生WM_KEYDOWN消息;关闭窗口时会产生WM_CLOSE消息等等。也就是说,在执行CreateWindowEx函数期间会产生若干个消息(先别管都是些什么消息)。
    Window系统会将这些由一系列的消息添加到该进程的消息队例中(噢,你可要有一点想象力哟!)。在CreateWindowEx函数中同时会调用我们写的WinPro函数若干次,并把消息的各项信息通过WinPro函数参数传递进来。(我再说一遍:你得有点想象力啊)
    说白了,就是在执行CreateWindowEx创建窗口过程中会引发对WinPro函数的多次调用。
    创建窗口过程中要用到缺省窗口过程函数DefWindowProc:
    前面说过,在执行CreateWindowEx创建窗口过程中会引发对WinPro函数的多次调用。嘿嘿,这可不是可有可无的调用啊!在这里,我们要让缺省窗口过程DefWindowProc来完成一些默认的消息处理操作。你不必知道它做了什么事,只要把这一切消息都“扔”给它就行啦!只有让 DefWindowProc函数完成必要的消息处理,CreateWindowEx函数才能全程地完成窗口的创建(否则,嘿嘿!窗口的创建必将失败。)。所以我们添加了调用DefWindowProc的代码。(DefWindowProc的返回值返回的是对消息处理的结果,我们再将它作为WinPro的返回值。)
    最后,本篇中所增加的全部代码总括如下(黑体部分):
LRESULT CALLBACK WinProc(HWND hwnd,
                         UINT msg,
                         WPARAM wparam,
                         LPARAM lparam)
{
   return DefWindowProc(hwnd, msg, wparam, lparam);
}

int WINAPI WinMain(HINSTANCE hinstance,
                 HINSTANCE hprevinstance,
                 LPSTR lpcmdline,
                 int ncmdshow)
{
       HWND hWnd;
    MSG msg;
    WNDCLASSEX wndclass; 
   //……  这里省略了前篇所述的注册窗口类的过程
    hWnd=CreateWindowEx(NULL,WND_CLS_NAME,
                "这是我的第一个窗口",
                WS_OVERLAPPEDWINDOW|WS_VISIBLE ,
                CW_USEDEFAULT, 0,
                400,400,
                NULL,
                NULL,
                hinstance,
                NULL );
    if (!hWnd)
        return 0;
  //……
  return 0;
}
    现在现具体地分析一下调用CreateWindowEx函数的过程:
    1. 首先从调用CreateWindowEx开始,程序进入了CreateWindowEx函数体内的代码(就是Window系统的代码)。
    2. 在此期间,CreateWindowEx会产生若干个消息(我们不用管是什么消息)。
    3. 每当一个消息产生后,CreateWindowEx会自动调用我们自己写的WinProc函数,将消息信息传递进来。
    4. 这些消息我们自己不作处理而是直接在WinProc调用缺省窗口过程DefWindowProc来作缺省处理(你不用去管DefWindowProc做了什么),并返回处理结果。
    5. 每次WinProc结束后回到CreateWindowEx代码处,CreateWindowEx会根据返回值果进行判断,完成最终的窗口创建。
    怪不得嘛!没有调用DefWindowProc这个函数,CreateWindowEx无法完成全部的窗口创建过程,所以最终创建窗口失败了。
    很好!现在再按前面讲的调试步骤试试看。
    if (!hWnd)
        return 0;   //这里设断点
    程序不再执行这个return 0;了。太好啦!这说明窗口创建成功了。当然并没把窗口显示出来,所以你只看到程序停都不停一下就马上就结束了。
    关于窗口的显示我就留到下一篇了。

阅读(6418) | 评论(1)


版权声明:编程爱好者网站为此博客服务提供商,如本文牵涉到版权问题,编程爱好者网站不承担相关责任,如有版权问题请直接与本文作者联系解决。谢谢!

评论

loading...
您需要登录后才能评论,请 登录 或者 注册