/*Win 32 项目创建窗体的过程
*创建窗体主要分为这么几个过程
*1. 声明一个窗体类,并完成窗体基本元素(包括窗体背景颜色,光标等)的设置
*2. 注册这个窗体类
*3. 创建这个窗体
*4. 创建消息响应函数
*5. 显示窗体
* 主要用的函数有两个, 第一个是 WinMain的入口函数
* int WINAPI WinMain(
* HINSTANCE hInstance, //handle to current instance
* HINSTANCE hPrevInstance, //handle to previous instance
* LPSTR lpCmdLine , //command line
* int nCmdShow //show state
*);
* 第二个使用到的函数是响应消息的窗体回调函数
* LRESULT CALLBACK WinSProc(
* HWND hwnd, //handle to window
* UINT uMsg, //message identifier
* WPARAM wParam, //first message parameter
* LPARAM lParam, //second message parameter
*);
*/
#include<windows.h>
#include<stdio.h>
LRESULT CALLBACK WinSProc(
HWND hwnd, //handle to window
UINT uMsg, //message identifier
WPARAM wParam, //first messsage parameter
LPARAM lParam //second message parameter
);
int WINAPI WinMain(
HINSTANCE hInstance, //handle to current instance
HINSTANCE hPrevInstance, //handle to previous instance
LPSTR lpCmdLine, //command line
int nCmdShow //show state
)
{
//设计一个窗口类, 定义一个wndcls 窗口类
WNDCLASS wndcls;
wndcls.cbClsExtra = 0; //额外的类的附加字节数, 不需要,设置为0
wndcls.cbWndExtra = 0; //窗口额外的附加字节数, 不需要,设置为0
wndcls.hbrBackground = (HBRUSH) GetStockObject(LTGRAY_BRUSH); //设置背景色
wndcls.hCursor = LoadCursor(hInstance,IDC_ARROW);
wndcls.hIcon = LoadIcon(NULL,IDI_ERROR);
wndcls.hInstance = hInstance;
wndcls.lpfnWndProc = WinSProc;//设置回调函数
wndcls.lpszClassName = "SW"; // 设置类的名字, 供创建窗体使用
wndcls.lpszMenuName = NULL; // 没有菜单, 所以设置为空
wndcls.style = CS_HREDRAW|CS_VREDRAW;
RegisterClass(&wndcls);
HWND hwnd;
hwnd = CreateWindow("SW","WinMainT",WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,
CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,hInstance,NULL);
ShowWindow(hwnd,SW_SHOWNORMAL);
UpdateWindow(hwnd);
MSG msg;
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK WinSProc(
HWND hwnd, // handle to window
UINT uMsg, //message identifier
WPARAM wParam, //first message parameter
LPARAM lParam //second message parameter
)
{
switch(uMsg)
{
case WM_CHAR:
char szChar[20];
//WCHAR wCh[20];
sprintf(szChar,"char is %d",wParam);
//MultiByteToWideChar(CP_ACP,0,szChar,2,wCh,2 );
MessageBox(hwnd,szChar,"winST",0);
/* char szChar[20];
wchar_t *pwText;
sprintf(szChar,"char is %d",wParam);
*/
//MultiByteToWideChar(0,0,szChar,-1,pwText,dwSize);
/*MultiByteToWideChar(0,0,szChar,-1,pwText,MultiByteToWideChar(CP_ACP,0,szChar,-1,NULL,0));
MessageBox(hwnd,pwText,L"WinST",0);
if(!pwText)
{
delete []pwText;
}
*/
break;
case WM_LBUTTONDOWN:
MessageBox(hwnd,"mouse clicked","winST",0);
HDC hdc;
hdc = GetDC(hwnd);
TextOut(hdc,0,50,"winST",strlen("winST"));
ReleaseDC(hwnd,hdc);
break;
case WM_PAINT:
HDC hDC;
PAINTSTRUCT ps;
hDC = BeginPaint(hwnd,&ps);
TextOut(hDC,0,0,"WinSProc",strlen("WinSProc"));
EndPaint(hwnd,&ps);
break;
case WM_CLOSE:
if(IDYES == MessageBox(hwnd,"Y R N","WinST",MB_YESNO))
{
DestroyWindow(hwnd);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd,uMsg,wParam,lParam);
}
return 0;
}
分析如下:
1. 首先新建项目, 选择win32 Project, 然后OK , 然后选择 win32 APP, 并选择Empty Project 选择finish 创建本项目
2. 选择File,Add new file, 选择 C++ File 选择OK , 然后Ctrl + S, 取名为 winMain.cpp, 然后回到项目属性管理器中, 选择source 右键 Add ,Existing Items 选择刚新建winMain.cpp 选择OK
3. 打开winMain.cpp, 因为要用到windows API 所以第一行 #include < windows.h> , 还有用到标准库的中函数, 所以#include <stdio.h>
4. 自绘窗体分5步: 1.声明一个窗体类,并完成窗体基本元素的初始化设置,2.注册这个窗体类, 3.创建这个窗体, 4. 构建属于这个窗体的消息循环函数,否则没法响应鼠标键盘的动作,为了指明这个消息响应函数最终的结果动作, 需要创建一个结果回调函数,, 5. 显示窗体
那么在5步创建之前,首先需要创建程序执行的入口函数WinMain()函数, WinMain()函数的原型:
int WINAPI WinMain(
HINSTANCE hInstance, //目前窗体的实例句柄
HINSTANCE hPrevInstance, // 之前实例的句柄
LPSTR, lpCmdLine, // 命令行, 用于接收命令行参数
int nCmdShow //显示状态
){
这里是程序开始执行的起点, 按照创建窗体的第一步,需要声明一个窗体类
WNDCLASS wndcls;
有了这个窗体类, 下面就需要初始化有关窗体基本元素的设置
wndcls.cbClsExtra = 0;
wndcls.cbWndExtra = 0; // 以上这个两个属性分别是额外的类附加字节数和窗体额外的附加字节数,由于不需要,所以默认设置为0
// 设置窗体客户区的颜色,使用了GetStockObject 来选择画刷句柄并返回一个逻辑对象的句柄,但是背景色的设置需要启用画刷,这里只是返回一个逻辑画刷对象的句柄,所以需要使用HBRUSH来强制转换为某一个具体画刷句柄才能使用,像这里就是转换为填充颜色为白色的具体的画刷句柄来赋值给背景色对象类使用
wndcls.hbrBackground = (HBRUSH) GetStockObject(WITHE_BRUSH);
wndcls.hCursor = LoadCursor(NULL , IDC_ARROW);// 设置光标,如果设置的是标准光标, 则LoadCursor的第一个参数应该设置为NULL, 否则应用程序刚开始运行的时,你会看到这个光标没有,而一直是一个在加载中的圆圈, 其实是此时这个hInstance 它是属于窗体的,而你这个圆圈加载的地方是是属于窗体上方的这个客户区域,它是盖在窗体上方的,所以看不到这个光标,等你的鼠标移动窗体上点击一下,再移动回来,就会发现这时候光标出现了, 这是这时候 hInstance 创建了这个实例了
wndcls.hIcon = LoadIcon ( NULL , IDI_ERROR); //创建了一个标准的图标, 原理同上
wndcls.hInstance =hInstance; // 实例句柄
wndcls.lpfnWndProc = WinSProc; //设置回调函数
wndcls.lpszClassName = "sw" // 设置窗体类名, 在下面的创建窗体函数中用类标识是创建这个窗体而用,
wndcls.lpszMenuName = NULL; // 应为我们没有创建菜单,所以设置为NULL
wndcls.style =CS_HREDRAW | CS_VREDRAW;// 设置窗体的样式, 这里是设置窗体在创建被绘制的时候的绘制样式, 是水平绘制和垂直绘制
以上部分是窗体基本元素的初始化, 不需要的则设置为0 或者设置为NULL , 但是都要初始化,否则运行之后看不到窗体
声明窗体类, 初始化基本元素设置之后, 第2步就是注册这个窗体类
RegisterClass(&wndcls);
注册完了之后, 就可以创建这个窗体了, 创建窗体首先需要一个窗体句柄, 声明一个窗体句柄,
HWND hwnd;
然后调用CreateWindow();来创建这个窗体
hwnd = CreateWindow("SW","WinMainT",WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,
CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,hInstance,NULL);
那么CreateWindow()原型如下:
HWND CreateWindow(
LPCTSTR lpClassName, // 注册类的名字, 就是那个SW
LPCTSTR lpWindowName, //这个是窗体显示时的标题
DWORD dwstyle, // 窗体的类型 这里选择了 WS_OVERLAPPEDWINDOW 包括了大小按钮的这么一种类型
int x, //窗体创建显示的左上角的水平位置 , 可以由系统自动设置,那么设置为 CW_USEDEFAULT 即可
int y, //窗体创建显示的左上角的垂直位置, 可以由系统自动设置,那么设置为 CW_USEDEFAULT 即可
int nWidth, // 窗体的宽度, 可以由系统自动设置,那么设置为 CW_USEDEFAULT 即可
int nHeight, // 窗体的高度, 可以由系统自动设置,那么设置为 CW_USEDEFAULT 即可
HWND hWndParent, // 父窗口的句柄, 如果没有, 则设置为NULL
HMENU hMenu, // 菜单句柄, 没有设置为NULL
LPVOID lpParam // window-creation data , 没有设置为NULL ,即可
};
创建完窗体之后,就是要显示窗体了
ShowWindow(hwnd, SW_SHOWNORMAL);
显示完窗体之后, 则需要更新窗体
UpdateWindow(hwnd);
好现在窗体的创建和显示都完成了,但是还缺少一个消息响应函数, 没有这个函数则窗体显示出来只能看不能操作它
要创建一个消息响应函数, 首先创建一个消息结构体实例变量
MSG msg;
然后利用一个循环来获取消息
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
如果消息队列中被分发进来了消息,那么GetMessage()函数就会调用一个消息队列线程来接收一个消息
BOOL GetMessage(
LPMSG lpMsg, // 指向接收到的消息的指针
HWND hWnd, //接收某个窗口消息的窗口句柄, 如果想要接收所有窗口发出的消息, 则这里可设置为NULL,即可
UINT wMsgFilterMin, // 第一条消息
UINT wMsgFilterMax // 最后一条消息
如果wMsgFilterMin,wMsgFilterMax的值都被设置为0, 则表明返回所有可获取的消息
);
如果GetMessage()接收到消息,且这个消息不是WM_QUIT ,怎返回一个非零值, 那么while则一直循环下去,否则退出循环
那么GetMessage()接收到消息, 进入循环体中, 此时调用这个TranslateMessage() 把这个virtual-key消息先转换为字符串消息,然后
发送到所调用线程的消息队列中, 发送到消息队列中此时就要调用DispatchMessage()函数来分发这些消息队列中的消息给windows 处理消息的回调函数来处理这些消息, 这样就进入了辅助消息实现动作操作的window处理回调函数中
本例中的回调函数就是winSProc()函数
WindowProc函数原型如下:
LRESULT CALLBACK WindowProc(
HWND hwnd, // handle to window
UINT uMsg, // message identifier
WPARAM wParam, //first message parameter
LPARAM lParam //second message parameter
);
在WinSProc()
{
使用了switch语句类分类管理各种消息
case WM_CHAR: // 如果是按下键盘,则响应这个字符消息
char szChar[20];
sprintf(szChar,"char is %d",wParam); // 将键盘响应值格式化后存入szChar中
通过messageBox()来输出
MessageBox(hwnd,szChar, "winST",0);
break;
case WM_LBUTTONDOWN: //响应左键按下事件
HDC hdc; // 声明一个设备上下文句柄
hdc=GetDC(hwnd);// 获取hwnd这个窗体的设备上下文句柄
TextOut(hDC,0,0,"wind",strlen("wind"));
ReleaseDC(hwnd,hdc); // 注意在使用了GetDC()来获得一个设备上下文句柄,用完之后,一定要用ReleaseDC(hwnd,hdc)来释放掉,否则可能造成内存泄露
break;
case WM_PAINT: // 响应窗体绘制事件
HDC hDC;
PAINTSTRUCT ps; // 声明一个paintStruct
hDC = BeginPaint(hwnd,&ps); // BeginPaint 只能在WM_PAINT这个消息中使用,并且用完之后,要用EndPaint()来释放掉这个hDC
TextOUt();
EndPaint(hwnd,&ps);
break;
case WM_CLOSE: // 响应关闭窗口消息, 在关闭之后弹出一个框问是否确定关闭
if(IDYES==MessageBox(hwnd,"Y R N", "winSt" ,MB_YESNO))
{
如果是,则:
DestroyWindow(hwnd);
}
break;
DestroyWindow(hwnd); 则会向这个hwnd窗体发出WM_DESTROY 销毁的消息
case WM_DESTROY:
PostQuitMessage(0); // 那么PostQuitMessage(0)怎会发出WM_QUIT消息,那么GetMessage()收到这个消息是返回0, 怎while 退出循环 那么执行return 0 , 则整个程序退出
break;
default:
return DefWindowProc(hwnd, uMsg,wParam,lParam); 如果不是上面的case情况,怎DefWindowProc返回,由系统默认处理
}
return 0;
}