您的位置:首页 > 编程语言

Windows编程-创建窗口

2016-08-04 23:10 489 查看
窗口创建的基本步骤是:

设计窗口类

注册窗口类

创建窗口

显示更新窗口

消息循环

编写回调函数

——————————帅气的分割线—————————–

下面我们一步一步进行讲解:

1.设计窗口类WNDCLASS

我们查看MSDN可以知道窗口类的成员变量有哪些:

typedef struct _WNDCLASS {
UINT       style;
WNDPROC    lpfnWndProc;
int        cbClsExtra;
int        cbWndExtra;
HINSTANCE  hInstance;
HICON      hIcon;
HCURSOR    hCursor;
HBRUSH     hbrBackground;
LPCTSTR    lpszMenuName;
LPCTSTR    lpszClassName;
} WNDCLASS, *PWNDCLASS;


微软为我们设计好了窗口类,我们只需要按照我们的需求填写其中的值就可以了。

我们先看窗口类的第一个字段UINT style;

这个字段是指明窗口的类型风格,其值是可选项,以”CS_“开头,查看MSDN可以知道它所有的可选项,在这里我说两个选项:”CS_HREDRAW“、”CS_VREDRAW“,分别是水平重绘和竖直重绘,表示窗口在水平和竖直方向大小发生变化时会发出WM_PAINT消息来进行窗口的重新绘制。

接下来看第二个字段:WNDPROC lpfnWndProc;

这是窗口的回调函数,这里赋值一个函数的名字(函数指针),这个回调函数的参数有一定的要求,查看MSDN可以知道回调函数的声明如下:

LRESULT CALLBACK WindowProc(  HWND hwnd,      // handle to window
UINT uMsg,      // message identifier
WPARAM wParam,  // first message parameter
LPARAM lParam   // second message parameter);


这里对回调函数进行一个简单的说明,操作系统(OS)可以捕获程序的消息,并将消息放到该进程的消息队列中,但是OS并不会对这个消息进行处理,如果想要对消息处理就需要程序员自己来写消息响应函数。当用户按下键盘的时候,OS会捕获两个消息WM_KETYDOWN、WM_KEYUP消息,通过TranslateMessage函数将键盘按下和弹起的消息转换成一个WM_CHAR消息,并将按下的按键的ASCII码存放到消息结构体MSG的附加参数中,然后使用DespatchMessage函数将这个消息传递到回调函数中,并由回调函数进行处理。

int cbClsExtra;

int cbWndExtra;

两个附加参数,给0就行

接下来是字段:HINSTANCE hInstance;

它表示一个应用程序的实例句柄,至于应用程序的实例,可以理解为是一个进程,一个执行的程序。这个字段的值是直接从WinMain函数给过来的。

HICON hIcon;

HCURSOR hCursor;

HBRUSH hbrBackground;

这三个字段分别是窗口的图标、光标、背景。如果要设置窗口的图标要使用函数:

HICON LoadIcon(
HINSTANCE hInstance, // handle to application instance
LPCTSTR lpIconName   // name string or resource identifier
);


其中,如果想使用系统自带的图标,第一个参数必须写NULL,第二个参数可以是”IDI_“开头的任意可选项。

而光标一样,需要使用函数:

HCURSOR LoadCursor(
HINSTANCE hInstance,  // handle to application instance
LPCTSTR lpCursorName  // name or resource identifier
);


但是对于背景来说,需要使用下面这个函数来设置:

HGDIOBJ GetStockObject(
int fnObject   // stock object type
);


其中fnObject为可选项,详情可查看MSDN

LPCTSTR lpszMenuName;

LPCTSTR lpszClassName;

下面是最后的两个字段,其中第一个是菜单的名字,第二个是窗口类的名字。由于没有菜单,菜单名设置为空,窗口类名要在CreateWindow时使用。

以上就设计好了窗口类了。

——————–我是华丽的分割线————————–

2. 注册窗口类

使用一下函数就可以,参数是窗口类对象的指针。

ATOM RegisterClass( CONST WNDCLASS *lpWndClass // class data);

3. 创建窗口

使用CreateWindow函数来创建窗口:

HWND CreateWindow(
LPCTSTR lpClassName,  // registered class name
LPCTSTR lpWindowName, // window name
DWORD dwStyle,        // window style
int x,                // horizontal position of window
int y,                // vertical position of window
int nWidth,           // window width
int nHeight,          // window height
HWND hWndParent,      // handle to parent or owner window
HMENU hMenu,          // menu handle or child identifier
HINSTANCE hInstance,  // handle to application instance
LPVOID lpParam        // window-creation data
);


其中第一个参数”LPCTSTR lpClassName“就是 刚才在第一步设置窗口类时所起的类名字,第二个参数是该窗口的标题,后面的四个参数是窗口左上角的坐标和宽高,接下来两个参数”hWndParent“、”hMenu“是窗口的父窗口句柄和菜单句柄,由于没有就填NULL,最后一个参数”lpParam“是附加参数填NULL。

4. 显示更新窗口

ShowWindow(hWnd, SW_SHOWNORMAL); //正常显示窗口
UpdateWindow(hWnd); //更新窗口


5. 消息循环

OS每次从该进程的消息队列中取出第一条消息,使用函数如下:

BOOL GetMessage(
LPMSG lpMsg,         // message information
HWND hWnd,           // handle to window
UINT wMsgFilterMin,  // first message
UINT wMsgFilterMax   // last message
);


在取消息之前,需要声明一个MSG结构的消息变量,它不需要初始化,直接传入第一个参数,该函数会将取出的消息填入该变量。第二个参数是窗口句柄,表明你要接收那个窗口的消息,如果想接收该进程的所有消息,该参数填写NULL。第三、四个参数是消息过滤的最小最大值,表明你要接受消息的范围,如果接收全部消息,这两个值填0就行。

这个GetMessage有一个BOOL型的返回值,如果取到的消息是WM_DESTROY消息则返回假,所以我们将这个函数放到while循环中,只要有消息就执行循环体,直到用户关闭窗口时发出WM_DESTROY消息后while循环条件为假退出。

在循环体中,我们要处理消息,使用以下两个函数:

TranslateMessage(&msg); //翻译消息
DispatchMessageA(&msg); //将消息传入窗口的回调函数


这两个函数上面说到过,这里不进行赘述。

6. 编写回调函数

由于在第五步的时候使用DispatchMessage函数将一个消息传入回调函数,则在回调函数中对参数msg进行判断和进一步处理。

再次特别说明两个消息:”WM_CLOSE“、“WM_DESTROY”

当用户点击关闭窗口按钮的时候会发出一个WM_CLOSE消息,我们进行窗口的销毁,但是一定要注意此时只是销毁了窗口,程序仍然在后台执行。销毁窗口的函数DestroyWindow又会发出一个WM_DESTROY的消息,在处理这个消息的时候使用PostQuitMessage这才真正的退出程序。

对于我们没有处理的其他消息,我们不能对其不管,所以我们对其他不感兴趣的消息调用系统默认的回调函数DefWindowProc

———————–这是另一条分割线————————

下面是程序的结果:



上图为程序运行后的界面



点击鼠标左键后窗口显示文字



按下键盘按键后窗口弹出对话框



点击退出按钮窗口弹出对话框,点击是程序结束

———————–帅气的分割线—————————–

下面就是程序的源代码:

#include <Windows.h>
#include <stdio.h>

/*
* 窗口的回调函数
*/
LRESULT CALLBACK WindowProc(HWND hwnd, // handle to window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam) // second message parameter
{
HDC hDC;
PAINTSTRUCT ps;
switch (uMsg)
{
case WM_PAINT:
/*
* 窗口重绘时调用
* 只有在WM_PAINT消息中才可以使用BeginPaint、EndPaint
* 其他消息使用GetDC、ReleaseDC
*/
hDC = BeginPaint(hwnd, &ps);
TextOut(hDC, 0, 50, "这是在WM_PAINT消息中重绘的文字", strlen("这是在WM_PAINT消息中重绘的文字"));
EndPaint(hwnd, &ps);
break;
case WM_CHAR:
MessageBox(hwnd, "WM_CHAR消息触发了", "提示", MB_OK);
break;
case WM_LBUTTONDOWN:
hDC = GetDC(hwnd);
TextOut(hDC, 0, 70, "这是在WM_LBUTTONDOWN消息中重绘的文字", strlen("这是在WM_LBUTTONDOWN消息中重绘的文字"));
ReleaseDC(hwnd, hDC);
break;
case WM_CLOSE:
if (IDYES == MessageBox(hwnd, "确定要退出吗?", "提示", MB_YESNO))
{
//确定退出,销毁窗口,抛出一个WM_DESTYRY的消息
DestroyWindow(hwnd);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, // handle to current instance
HINSTANCE hPrevInstance, // handle to previous instance
LPSTR lpCmdLine, // command line
int nCmdShow) // show state
{
//第一步、设计窗口类
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW; //设置水平竖直重绘,发送WM_PAINT消息
wndclass.lpfnWndProc = WindowProc; //指定窗口的回调函数
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0; //两个额外数据
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_CROSS);
wndclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = "myWindowClass";

//第二步、注册窗口类
RegisterClass(&wndclass);

//第三步、创建窗口
HWND hWnd = CreateWindow(
"myWindowClass", //窗口类的名字
"my first window", //窗口标题
WS_OVERLAPPEDWINDOW, //样式
0, 0, 500, 500, //左上角坐标,宽高
NULL, //父窗口
NULL, //菜单
hInstance, //实例
NULL); //附加参数

//第四部、显示更新窗口
ShowWindow(hWnd, SW_SHOWNORMAL); //正常显示窗口 UpdateWindow(hWnd); //更新窗口

//第五步、消息循环
MSG msg;
while (GetMessageA(&msg, NULL, 0, 0))
{
TranslateMessage(&msg); //翻译消息 DispatchMessageA(&msg); //将消息传入窗口的回调函数
}

return 0;
}

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息