使用C++编写一个可视化窗口
初学C++时,控制台会给我们提供一个黑窗口,而这篇文章所做的就是:创建一个属于图形界面窗口,而不是使用控制台黑窗口如图,这是一个标题为XSZY的窗口看完这篇文章,你想要的窗口大小,图标等都可以自己定义甚至是在窗口上鼠标的样子,都是可以随意改的所有有关视窗的基本函数和类型都定义在头文件 <windows.h> 中我们只需要引用就好了//引用头文件 windows.h#include<
初学C++时,控制台会给我们提供一个黑窗口,而这篇文章所做的就是:创建一个属于图形界面窗口,而不是使用控制台黑窗口
如图,这是一个标题为XSZY的窗口
这都是自定义的
所有有关视窗的基本函数和类型都定义在头文件 <windows.h> 中
我们只需要引用就好了
//引用头文件 windows.h
#include<windows.h>
然后定义程序的入口函数main
int __stdcall WinMain(HINSTANCE hInst,
HINSTANCE hPrev,
LPSTR lpCmdLine,
int nCmdShow)
{}
说到这个入口函数,其实使用main()也是可以的
int main() {}
int main(int argc, char** argv) {}
int main(int argc, char* argv[]) {}
其实C++定义了很多程序入口,只要程序中包含其中一个,程序就可以运行了
而WinMain的声明同样也是有依据可寻的
在头文件 <winbase.h> 中, 有这样两句话
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd);
int WINAPI wWinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd);
它们分别声明了两种程序入口
WinMain 和 wWinMain
wWinMain 是 WinMain 的宽字符版本, 形参列表中的LPWSTR说明了这个事实
顺带说一下吧 基本所有的函数、类型、结构体、宏等都带有A 和 W,
比如 WNDCLASSA 和 WNDCLASSW
一般A是窄字符, W是宽字符
LPSTR 其实就是 char* ; 而LPWSTR 是 wchar_t*
表示宽字符时需要标记L
比如定义宽字符串HelloWorld应该写为 L"HelloWorld!" ,系统就会识别为宽字符了
说了这么多,该定义窗口实例句柄了
这是用来指向一个实例( instance ) 的类型 HINSTANCE
前缀H就是Handle句柄的意思, instance就是实例的意思
这个句柄的作用就是:指定一个窗口实例
如果你使用的时WinMain,那么可以选择直接使用形参表中的第一个或第二个参数,但是如果你使用的时int main(),那么就需要定义一个新的实例句柄
int WINAPI WinMain(HINSTANCE hInst, //可以使用这个
HINSTANCE hPrev, //也可以使用这个(习惯上使用上面的)
LPSTR lpCmdLine,
int nShowCmd)
{}
int main()
{
HINSTANCE hInst; //定一个新的实例句柄
}
定义来有什么用呢?
我们需要做一个窗口,就需要创建它,而定义它的标准,是由结构体WNDCLASS来完成的,能够让窗口和窗口结构体链接上的,需要实例句柄一致,并且和窗口类名一致
窗口类在 <winuser.h> 中的定义
typedef struct tagWNDCLASSA {
UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HINSTANCE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCSTR lpszMenuName;
LPCSTR lpszClassName;
} WNDCLASSA,*PWNDCLASSA,*NPWNDCLASSA,*LPWNDCLASSA;
typedef struct tagWNDCLASSW {
UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HINSTANCE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCWSTR lpszMenuName;
LPCWSTR lpszClassName;
} WNDCLASSW,*PWNDCLASSW,*NPWNDCLASSW,*LPWNDCLASSW;
我们需要对其成员进行填充, 不然编译器无法为它分配内存,就会报错的。
解释:style 是窗口样式
lpfnWndProc 是回调函数(等会再说)
cbClsExtra 是窗口类拓展 一般无拓展
cbWndExtra 是窗口扩展 一般无拓展
hInstance 就是实例句柄,需要将其定义为我们刚刚定义的实例句柄
hIcon 窗口的图标
hCursor是鼠标
lpszMenuName 菜单名 一般为空
lpszClassName 窗口类名
现在可以把我们需要的赋值, 不需要的全部memset为0
WNDCLASSA wc;
memset(&wc, 0, sizeof(wc));
wc.lpszClassName = "WindowClassName";
wc.hInstance = hInst;
wc.lpfnWndProc = ; //回调函数
回调函数是用来处理消息的
在消息循环的过程中,消息被分发到回调函数,回调函数内的代码提供操作
定义回调函数
long long WINAPI WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
return DefWindowProc(hwnd, msg, wp, lp);
}
此时给lpfnWndProc复制只用函数名即可
wc.lpfnWndProc = WndProc;
定义窗口结构体也可以使用WNDCLASSEX(拓展过的窗口类)
WNDCLASSEXA wc;
wc.cbSize = sizeof(WNDCLASSEXA);
wc.hInstance = hinst;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = "WindowClassName";
使用拓展类可以给成员cbSize赋值,就可以正确分配内存了
定义完窗口类,应该向Windows注册窗口,让它授权创建一个带有此窗口类的窗口
RegisterClassA(&wc);
注册完之后,就要将窗口创建到窗口句柄中了
窗口句柄类型 HWND
创建窗口的函数很长,如果你没有使用EX的结构体,创建窗口的函数大体如下
CreateWindow(窗口类名,窗口标题,窗口风格,窗口的位置x, y, 窗口宽, 窗口长,父窗口,菜单,实例句柄,附加函数);
窗口类名需要和窗口结构体的类名lpszClassName一致,实例句柄也必须引用同一个
CreateWindowA 和 CreateWindowW 的区别不用我重申了吧
如果你使用了WNDCLASSEX的结构体
那么创建窗口相应地得加上EX
CreateWindowEx(窗口拓展风格,...);后面和CreateWindow一样,只是在CreateWindow的基础上在前面添加了一个拓展风格的形参,一般我会使用客户区的拓展(WS_EX_CLIENTEDGE)
代码:
HWND hwnd = CreateWindowA("WindowClassName",
"XSZY",
WS_OVERLAPPEDWINDOW,
100, 100,
640, 480,
NULL, NULL, hInst, NULL);
其他的比较好理解,就是窗口风格是不是看不懂?
它是一个宏定义
一共有这些宏定义表示窗口的风格,要添加多个就使用按位OR(|)运算符就可以了
#define WS_OVERLAPPED __MSABI_LONG(0x00000000) //窗口的基本样式
#define WS_POPUP __MSABI_LONG(0x80000000) //弹出式窗口
#define WS_CHILD __MSABI_LONG(0x40000000) //子窗口(指嵌在主窗口内部的子窗口)
#define WS_MINIMIZE __MSABI_LONG(0x20000000) //最小化
#define WS_VISIBLE __MSABI_LONG(0x10000000) //可见
#define WS_DISABLED __MSABI_LONG(0x08000000)
#define WS_CLIPSIBLINGS __MSABI_LONG(0x04000000)
#define WS_CLIPCHILDREN __MSABI_LONG(0x02000000)
#define WS_MAXIMIZE __MSABI_LONG(0x01000000) //最大化
#define WS_CAPTION __MSABI_LONG(0x00C00000) //标题栏
#define WS_BORDER __MSABI_LONG(0x00800000) //边框
#define WS_DLGFRAME __MSABI_LONG(0x00400000) //对话框风格(只有一个叉叉)
#define WS_VSCROLL __MSABI_LONG(0x00200000) //竖滚动轴
#define WS_HSCROLL __MSABI_LONG(0x00100000) //横滚动轴
#define WS_SYSMENU __MSABI_LONG(0x00080000) //系统菜单(如果只添加了这个风格就和对话框一样)
#define WS_THICKFRAME __MSABI_LONG(0x00040000) //可变大小
#define WS_GROUP __MSABI_LONG(0x00020000)
#define WS_TABSTOP __MSABI_LONG(0x00010000)
#define WS_MINIMIZEBOX __MSABI_LONG(0x00020000) //最小化控件框
#define WS_MAXIMIZEBOX __MSABI_LONG(0x00010000) //最大化控件框
#define WS_TILED WS_OVERLAPPED
#define WS_ICONIC WS_MINIMIZE
#define WS_SIZEBOX WS_THICKFRAME
#define WS_TILEDWINDOW WS_OVERLAPPEDWINDOW
#define WS_OVERLAPPEDWINDOW (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX)
#define WS_POPUPWINDOW (WS_POPUP | WS_BORDER | WS_SYSMENU)
#define WS_CHILDWINDOW (WS_CHILD)
这里有可能会失败,所以添加一条判断:
if(hwnd == NULL)
return -1;
如果你刚刚已经添加WS_VISIBLE 那么这一步可以省略
因为我这一步要设置窗口可见
ShowWindow函数就能够提供此功能
ShowWindow(hwnd, SW_SHOW);
SW_SHOW是一个宏,表示显示窗口
可用的还有SW_HIDE等宏,这里就不详细说了,可以在头文件 <winuser.h>中找到
最后是消息循环
消息结构体是MSG 消息循环的三个函数都要用到MSG类
所以定义一个先
MSG msg;
消息循环的过程应该是获得消息–>处理消息–>执行操作三步,分别对应三个函数
获得消息GetMessage(LPMSG, HWND,UINT,UINT)
处理消息(编码处理)TranslateMessage(LPMSG)
执行操作要在回调函数里执行,但是我们需要将消息发送给回调函数,这个函数是DispatchMessage(LPMSG)
LPMSG 理解为MSG的指针
为了不断处理消息,应该设置一个循环while(true)
在它的循环体内,写一个判断:如果收到消息,处理并分发消息
代码如下
while(true) if(GetMessage(&msg, NULL, 0, 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
HWND之所以没有锁定为hwnd,是因为别的消息它也要接受的
消息发到回调函数了,但是回调函数现在还是这样的:
long long WINAPI WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
return DefWindowProc(hwnd, msg, wp, lp);
}
并不能处理消息。
消息传过去后,msg,wp,lp三个参数负责接收消息,而msg是区分各个消息的
现在先让我们处理窗口关闭的消息,不然关不了
做法很简单,在return上面加上一个switch来分发消息(当然你用if else if 也行)
switch(msg)
{
}
窗口关闭、破坏、退出分别是一个消息,都由宏来定义
WM_CLOSE 关闭
WM_DESTROY 破坏
WM_QUIT 退出
我们只要让发生时退出就行了
switch(msg)
{
case WM_CLOSE:case WM_DESTROY:case WM_QUIT:exit(0);break;
}
然后放到回调函数中
long long WINAPI WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
switch(msg)
{
case WM_CLOSE:case WM_DESTROY:case WM_QUIT:exit(0);break;
}
return DefWindowProc(hwnd, msg, wp, lp);
}
好了
完整代码(直接复制就好)
#include <windows.h>
LPCSTR classname = " "; //设置你的窗口类名
LPCSTR caption = " "; //设置你的窗口标题
int x = 0,y = 0; //设置你的窗口位置
int width = 640, height = 480; //设置你的窗口宽高
//回调函数
long long WINAPI WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
switch(msg)
{
case WM_CLOSE:case WM_DESTROY:case WM_QUIT: //窗口关闭的消息
exit(0);
break;
}
return DefWindowProc(hwnd, msg, wp, lp); //定义回调函数的返回值
}
int WINAPI WinMain(HINSTANCE hInst, //实例句柄
HINSTANCE hPrev,
LPSTR lpCmdLine,
int nCmdShow)
{
WNDCLASSA wc; //窗口结构体
memset(&wc, 0, sizeof(wc));
wc.lpszClassName = classname;
wc.hInstance = hInst; //实例句柄
wc.lpfnWndProc = WndProc;
RegisterClassA(&wc); //注册
HWND hwnd = CreateWindowA(classname, caption, WS_VISIBLE|WS_OVERLAPPEDWINDOW, x, y, width, height, NULL, NULL, hInst, NULL); //创建窗口
if(hwnd == NULL) return -1;
MSG msg; //消息类
while(true) if(GetMessage(&msg, NULL, 0, 0)) //消息循环 获得消息
{
TranslateMessage(&msg); //处理消息
DispatchMessageA(&msg); //分发消息
}
}
感谢观看。
更多推荐
所有评论(0)