Windows是一个多任务的图形用户界面操作系统,学习和熟练掌握Windows应用程序的开发,首先需要了解Windows系统特点,理解Windows平台下应用程序的运行机制。
Microsoft的Windows系统是为PC机开发的GUI(Graphical User Interface)操作系统。它是一个多任务的操作系统。Windows应用程序具有统一的窗口和菜单界面。
由于Windows应用程序的窗口和菜单界面是统一的,所以对用户来说,Windows应用程序比传统的命令行式的应用系统更易于学习和使用。
#include
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,PSTR szCmdLine,int iCmdShow )
{
MessageBox(NULL, TEXT("你好, 欢迎来到VC之路"),TEXT("欢迎"),0);
return 0;
}
C语言编程必须有且只有一个主函数main()。Windows程序则有一个主函数称为WinMain(), 该函数为Windows应用程序的入口点,它的名字一定要是WinMain。
第一个参数:应用程序的当前实例句柄。
第二个参数:应用程序的前一个实例句柄,别管它,对于Win32位而言,它一般是NULL.
第三个参数:指向任何传给程序的命令行参数。PSTR代表"指向字符串的指针"。
第四个参数:它告诉应用程序如何初始化窗口,如最大化,最小化等状态。
WinMain()所起的作用:初始化,展示,销毁应用程序等。
MessageBox(),是一个很常用的API。用于以对话框的形式来输出信息。
#include
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //窗口函数声明
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
HWND hwnd; //窗口类句柄
MSG Msg; //消息结构变量
WNDCLASS wndclass; //窗口类结构变量
char lpszClassName[]="窗口"; //窗口类名
char lpszTitle[]="这是一个基本的Windows程序"; //标题栏
//定义窗口类的属性
wndclass.style=CS_HREDRAW|CS_VREDRAW; //改变窗口大小则重画
wndclass.lpfnWndProc=WndProc; //窗口函数为WndProc
wndclass.cbClsExtra=0; //窗口类无扩展
wndclass.cbWndExtra=0; //窗口示例无扩展
wndclass.hInstance=hInstance; //注册窗口类实例句柄
wndclass.hIcon=LoadIcon(NULL, IDI_APPLICATION); //应用图标
wndclass.hCursor=LoadCursor(NULL, IDC_ARROW); //箭头光标
wndclass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH); //白色背景
wndclass.lpszMenuName=NULL; //无菜单
wndclass.lpszClassName=lpszClassName; //窗口类名为“窗口”
if(!RegisterClass(&wndclass)) //注册窗口类
return FALSE;
//创建窗口
hwnd=CreateWindow( lpszClassName, //窗口类名
lpszTitle, //窗口名
WS_OVERLAPPEDWINDOW, //重叠式窗口
CW_USEDEFAULT, //左上角屏幕坐标默认值
CW_USEDEFAULT,
CW_USEDEFAULT, //窗口宽度和高度默认值
CW_USEDEFAULT,
NULL, //无父窗口
NULL, //无主菜单
hInstance, //创建此窗口的实例句柄
NULL); //无创建参数
ShowWindow(hwnd, nShowCmd); //显示窗口
UpdateWindow(hwnd); //更新窗口客户区
while(GetMessage(&Msg, NULL, 0, 0)) //消息循环
{
TranslateMessage(&Msg); //消息转换
DispatchMessage(&Msg); //派送消息到窗口函数
}
return Msg.wParam; //返回退出值
}
//窗口函数
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
//根据消息值转向不同的处理
switch(message)
{
case WM_PAINT: //重绘窗口客户区消息处理
HDC hdc; //设备描述表句柄
PAINTSTRUCT ps; //绘图信息结构变量
hdc = BeginPaint(hwnd,&ps); //获取要重绘设备描述表句柄
TextOut(hdc,10,10,"这是一个窗口",12); //输出文本
EndPaint(hwnd,&ps); //结束要绘制的窗口
break;
case WM_DESTROY: //撤销窗口消息处理
PostQuitMessage(0); //产生退出消息WM_QUIT
break;
default: //默认窗口函数
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}
如果出现
error C2440: “=”: 无法从“char [5]”转换为“LPCWSTR”
一般出现这种BUG 的,是由于字符集的问题。在vs2010中有两个字符集,Unicode 和 Muti-bye。
LPCTSTR在Multi-byte Character方式下与const char等价,在Unicode方式下与const tchar等价。所以在Unicode方式下对其赋值像下面这样:
LPCTSTR location = _T(“Software//”); //加_T进行转化。
Bug修改方法:
中文版:
项目->项目属性->配置属性->常规->项目默认值->字符集
注意:
原来是Unicode (安装时的默认值)
改为使用多字符集 。
① 注册窗口类
第一步:定义一个窗口类结构数据变量
第二步:给相关的注册项赋值
第三步:注册这个窗口类
ATOM RegisterClass( CONST WNDCLASS *lpWndClass);// 指向窗口类结构数据的指针
函数的返回类型是ATOM,这是一个Windows定义的数据类型,相当于C语言中的unsigned short。如果注册成功,将返回一个唯一的类标识值。否则返回0。
②建立和显示窗口
HWND CreateWindow(
LPCTSTR lpClassName, // 已经注册了的窗口类的名称
LPCTSTR lpWindowName, // 窗口名称,标题栏上显示的
DWORD dwStyle, // 窗口的样式
int x, // 窗口左上角的横坐标
int y, // 窗口左上角的纵坐标
int nWidth, // 窗口的宽度
int nHeight, // 窗口的高度
HWND hWndParent, // 父窗口
HMENU hMenu, // 菜单或子窗口标识
HANDLE hInstance, // 模块实例句柄
LPVOID lpParam // 创建窗口时的参数
);
BOOL ShowWindow(
HWND hWnd, // 窗口句柄
int nCmdShow // 执行显示的状态
);
函数GetMessage
BOOL GetMessage(
LPMSG lpMsg, // 接收消息的变量地址
HWND hWnd, // 接收消息的窗口
UINT wMsgFilterMin, // 消息的下限
UINT wMsgFilterMax // 消息的上限
);
GetMessage函数负责将消息接收。当接收到的消息是WM_QUIT时,返回0,结束消息循环。
函数TranslateMessage
BOOL TranslateMessage(CONST MSG *lpMsg //消息结构变量的地址
);
说明:负责将虚拟键消息转换成字符消息。值得注意的是该函数并不是修改参数lpMsg 所指定的消息,而是视情况产生一个新的消息。
函数DispatchMessage
LONG DispatchMessage(CONST MSG *lpmsg //消息结构变量的地址
);
说明:该函数负责进一步将消息传递的指定窗口(子窗口或对象)的窗口函数。
窗口函数的格式
LRESULT CALLBACK WindowProc(
HWND hwnd, // 窗口句柄
UINT uMsg, // 消息标识
WPARAM wParam, // 附加参数1
LPARAM lParam // 附加参数2
);
函数返回值取决于所处理的消息。
窗口函数将消息通过参数message带入,我们的任务就是处理这些消息。用switch-case分支结构分拣这些消息,挑选出我们需要处理的消息,写出相应的处理代码,其余的交给函数DefWindowProc()。
函数DefWindowProc
LRESULT DefWindowProc(
HWND hwnd, // 窗口句柄
UINT uMsg, // 消息标识
WPARAM wParam, // 附加参数1
LPARAM lParam // 附加参数2
);
处理消息的原则是凡是窗口函数不处理或处理不了的消息,都必须传送到DefWindowProc,其返回值也是取决于所处理的消息。
消息循环中函数DispatchMessage的返回值,就是来自窗口函数的返回值。
为了解决这些问题,微软使用typedef关键字,为很多常用的C类型均定义了别名,这样一来,要解决源代码移植问题,只需在目标平台上定义相同的一套类型别名,即可解决大部分问题。
typedef struct tagMSG {
HWND hwnd; //消息发向的窗口句柄
UINT message; //消息标识符
WPARAM wParam;
//16位的消息参数,值因消息而异
LPARAM lParam;
//32位的消息参数,值因消息而异
DWORD time; //消息放入消息队列的时间
POINT pt; //消息放入消息队列的鼠标位置
} MSG;
说明:WPARAM含义和数值因消息不同而不同
比如:键盘消息和鼠标消息所对应的WPARAM消息所代表的消息是不同的。
hwnd是一个窗口句柄,用于标识消息发向的窗口。
time用于保存消息放进消息队列的时间。
pt用于保存将消息放入消息队列时的鼠标位置。
message是消息标识符,它是一个32位的无符号整数。Windows中的每条消息都有一个对应的消息标识符,这些标识符是在windows.h中定义的。
控件通知消息的格式有三种:
(1)仿窗口消息的格式,如滚动控件消息WM_HSCROLL。
(2)仿命令消息的格式,如用户修改了编辑控件中的文本后,编辑控件向父窗口发送的WM_COMMAND通知消息,该消息包含了控件通知消息EN_CHANGE。
(3)单独控件消息的格式,如消息WM_NOTIFY。
主窗口显示出来了,WinMain就开始处理消息了,怎么做的呢?
Windows为每个正在运行的应用程序都保持一个消息队列。
当你按下鼠标或者键盘时,Windows并不是把这个输入事件直接送给应用程序,而是将输入的事件先翻译成一个消息,然后把这个消息放入到这个应用程序的消息队列中去。
应用程序又是怎么来接收这个消息呢?这就要说到消息循环了。
应用程序的WinMain函数通过执行一段代码从它的消息队列中来检索Windows送往它的消息。然后WinMain就把这些消息分配给相应的窗口函数以便处理它们,这段代码是一段循环代码,故称为“消息循环”。
MSG Msg; //定义消息名
while (GetMessage (&Msg, NULL, 0, 0))
{
TranslateMessage (&Msg) ; //翻译消息
DispatchMessage (&Msg) ; //分发消息
}
return msg.wParam ;
窗口函数 WndProc()
在Windows中,应用程序通过要求Windows完成指定操作,而承担这项通信任务的API函数就是Windows的相应窗口函数WndProc。
在DOS里,程序能直接控制事件的发生顺序;而在Windows里,应用程序不直接调用任何窗口函数,而是等待Windows调用窗口函数,请求完成任务或返回信息。
回调函数
为保证Windows调用这个窗口函数,这个函数必须先向Windows登记,然后在Windows实施相应操作时回调,所以窗口函数又称为回调函数。
WndProc是一个主回调函数,Windows应用程序至少有一个回调函数。下面语句即向Windows登记了窗口函数:
wndclass.lpfnWndProc=WndProc;
// lpfnWndProc是指向窗口过程函数的指针