第五篇 利用已注册的窗口类来创建一个窗口 白云小飞 一 用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;了。太好啦!这说明窗口创建成功了。当然并没把窗口显示出来,所以你只看到程序停都不停一下就马上就结束了。 关于窗口的显示我就留到下一篇了。

评论