#include <Windows.h>
#include <stdio.h>//sprintf
//对调函数:窗口过程函数,由操作系统调用
LRESULT CALLBACK WinProc(HWND hwnd, UINT uMsg,WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow
)
{
//1.定义该窗口类
WNDCLASS wndcls;
wndcls.cbClsExtra = 0;
wndcls.cbWndExtra = 0;
wndcls.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);//窗口背景画刷子
wndcls.hCursor = LoadCursor(NULL,IDC_ARROW); //加载鼠标形状
wndcls.hIcon = LoadIcon(NULL,IDI_APPLICATION); //窗口的最小化图标为缺省图标
wndcls.hInstance = hInstance; //当前实例句柄
wndcls.lpfnWndProc = WinProc; //回调函数,用于处理消息相应函数
wndcls.lpszClassName = LPCSTR("UpdateWindow_Test");//窗口类的名字
wndcls.lpszMenuName = NULL;//菜单名称
wndcls.style = CS_HREDRAW | CS_VREDRAW;
//2.注册该窗口类
RegisterClass(&wndcls);
//3.实例化该窗口(创建该窗口)
HWND hwnd;
hwnd = CreateWindow(LPCSTR("UpdateWindow_Test"),LPCSTR("UpdateWindow的作用测试"),WS_OVERLAPPEDWINDOW,\
800,20,300,600,NULL,NULL,hInstance,NULL);
//4.显示窗口
//不发送消息,当消息队列为空时,Windows会自动探测窗口的内容是否需要重画、以及需要重画的区域组成,
//并且在消息队列中放置一个WM_PAINT
ShowWindow(hwnd,SW_SHOWNORMAL);
//假若当前被标记为重画的区域存在(不存在的话它什么也不做),
//UpdateWindow函数可使WM_PAINT被直接发送到目标窗口,从而导致窗口立即重绘。
//SendMessage,发送的消息不需要进入到消息队列,直接由WINDOWS调用窗口过程。
//窗口过程完成刷新以后立刻退出,WINDOWS将控制返回给程序中UpdateWindow调用之后的语句。
UpdateWindow(hwnd);
//5.消息循环
MSG msg;
while(GetMessage(&msg,NULL,0,0))//主要部分
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK WinProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
)
{
static int n = 0;//用于查看进入重绘的次数
char szChar[100] = {0};
static int mLine = 40;//控制打印的位置
switch(uMsg)
{
case WM_PAINT:
n++;
sprintf(szChar,"第%d次进入WM_PAINT!",n);
mLine += 20;
HDC hDC;
PAINTSTRUCT ps;
//BeginPaint的一个重要作用,就是在它返回的DC里,用原来标记的区域制作一个剪裁区域(ClipRegion),
//从而使你的所有重画操作都被限定在这一个区域中。EndPaint之后,重画区域的标记被取消。
//我们自己也需要更新窗口的显示内容,这时候也是通过调用InvalidateRect来做标记。
//不过,很多人在这种情况下习惯于将整个窗口统统Inalidate做标记,方便但是不好。
//除非你需要更新的内容波及到整个窗口,否则应该仅仅把需要改变的那部分Invalidate。
//GetDC不能取消该标记区域,会造成无休止的重绘过程
hDC=BeginPaint(hwnd,&ps);
TextOut(hDC,0,mLine,LPCSTR(szChar),strlen(szChar));
EndPaint(hwnd,&ps);
break;
case WM_LBUTTONDOWN:
HDC hdc;
hdc=GetDC(hwnd);
TextOut(hdc,0,0,LPCSTR("鼠标左键被按下"),strlen("鼠标左键被按下"));
ReleaseDC(hwnd,hdc);
break;
case WM_CLOSE:
if(IDYES==MessageBox(hwnd,LPCSTR("退出该测试程序?"),LPCSTR("weixin"),MB_YESNO))
{
DestroyWindow(hwnd);//发送WM_DESTROY消息
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default: //必须有的,缺省的窗口处理过程
return DefWindowProc(hwnd,uMsg,wParam,lParam);
}
return 0;
}
//UpdateWindow的作用,是使得窗口立即被重绘,而不是去等待那个不确定的消息队列。
CALLBACK的调用方式:_stdcall。编译器默认的是__cdecl,所以在回掉函数前面显示说明。
1、__stdcall调用约定相当于16位动态库中经常使用的PASCAL调用约定。在32位的VC++5.0中PASCAL调用约定不再被支持(实际上它已被定义为__stdcall。除了__pascal 外,__fortran和__syscall也不被支持),取而代之的是__stdcall调用约定。两者实质上是一致的,即函数的参数自右向左通过栈传递,被调用的函数在返回前清理传送参数的内存栈,但不同的是函数名的修饰部分(关于函数名的修饰部分在后面将详细说明)。
_stdcall是Pascal程序的缺省调用方式,通常用于Win32 Api中,函数采用从右到左的 压栈方式,自己在退出时清空堆栈。VC将函数编译后会在函数名前面加上下划线前缀,在函数名后加上"@"和参数的字节数。
2、C调用约定(即用__cdecl关键字说明)按从右至左的顺序压参数入栈,由调用者把参数弹出栈。对于传送参数的内存栈是由调用者来维护的(正因为如此,实现可变参数的函数只能使用该调用约定)。另外,在函数名修饰约定方面也有所不同。
_cdecl是C和C++程序的缺省调用方式。每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用_stdcall函数的大。函数采用从右到左的压栈方式。VC将函数编译后会在函数名前面加上下划线前缀。是MFC缺省调用约定。