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