以下内容大多由《Windows程序设计》,《VC++深入详解》等书籍以及MSDN,网上一些资料和本人的理解整合而来,以作笔记之用。
---------------------------------------------------------------------------------------------
1.几个常用的术语:
API(Application Programming Interface)应用程序编程接口
SDK(Software Development Kit)软件开发包
2.匈牙利符号表示法(节选)
理解匈牙利符号表示法对windows程序设计有不小的好处,但是个人认为不必强记。
3.MessageBox(消息框)
#include<windows.h>
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR szCmdLine,int iCmdShow)
{
MessageBox(NULL,TEXT("xfatenet"),TEXT("HelloMsg"),0);
return 0;
}
3.1逐个分析所有windows程序入口函数WinMain()的参数:
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR szCmdLine,int nCmdShow)
WINAPI (CALLBACK也是如此):
#define WINAPI _stdcall
_stdcall 区别于 _cdecl
前者(_stdcall)一般用于调用win32 api函数;函数的参数自右向左通过栈传递。在主调用函数中负责压栈,被调用函数在返回前清理传送参数的内存栈。
后者是c/c++函数的默认调用方式。函数的参数自右向左通过栈传递。由主调用函数进行参数压栈,由主调用函数把参数弹出栈。对于传送参数的内存栈是由主调用函数来维护的(正因为如此,实现可变参数的函数只能使用该调用约定)。
HINSTANCE hinstance:
该参数是一个windows为你的应用程序生成的实例句柄。实例是一个用来跟踪资源的指针或数。句柄是实例指针的索引. 通过句柄能找到实例的地址.
hprevinstance:
由名字可以知道是指产生当前实例的应用程序实例,跟踪应用程序以前(prev-)的实例(instance)。
szCmdLine:和int main(int argc,char **argv)函数中的命令行参数相似。
nCmdShow :在启动过程中被传递给应用程序,带有如何打开主应用程序窗口的信息。控制应用程序如何启动。常用的参数值:
SW_HIDE | Hides the window and activates another window.(隐藏一个窗口,激活另一个窗口) |
SW_SHOW | Activates a window and displays it in its current size and position.(激活窗口按照当前尺寸和位置显示它。) |
SW_SHOWNA | Displays a window in its current state. The active window remains active.(以当前状态显示一个窗口,激活的窗口保持激活状态) |
SW_SHOWNOACTIVATE | Displays a window in its most recent size and position. The active window remains active.(以上次的尺寸和位置显示窗口,激活窗口保持激活状态) |
SW_SHOWNORMAL | Activates and displays a window. If the window is minimized or maximized, the system restores it to its original size and position (same as SW_RESTORE).(激活和显示窗口,如果窗口最小化或最大化,则恢复到原始尺寸和位置,应用程序将指定该状态) |
MessageBox的第一参数通常是窗口句柄,第二参数是显示的内容,第三参数是消息框的标题栏的字符串。第四参数是常数组合用于显示消息框的按钮样式。
#define MB_OK—— 0
#define MB_OKCANCEL—— 1
#define MB_ABORTRETRYIGNORE——2
#define MB_YESNOCANCEL——3
#define MB_YESNO——4
#define MB_RETRYCANCEL——5
可以用或运算符比如MB_OK|MB_ICONEXCLAMATION
如果想有声音的话,最简单的方法调用
BOOL WINAPI MessageBeep(
__in UINT uType
);
比如MessageBeep(MB_OK);
4. 创建windows应用程序
创建一个完整的windows应用程序需要:
1. 创建一个windows类
2. 创建一个winProc
3. 用windows注册windows类
4. 用前面创建的windows类创建一个窗口
5. 创建一个能够从事件句柄获得是或向事件句柄传递windows信息的主事件循环。
4.1消息队列和窗口
Windows程序设计是OOP(面向对象),每一个小的可视对象都是一窗口(子窗口或是控件窗口),窗口以消息的形式接受窗口的输入,窗口以消息与其他窗口通讯。Windows给程序发送消息是指windows调用程序中的一个函数,位于windows程序中的函数被称为“窗口过程”。Windows通过窗口过程来给窗口发送消息。窗口过程根据此消息进行处理,然后将控制返回给windows。
Windows程序开始执行后,windows为该程序创建一个消息队列,这个消息队列用来存放该程序可能创建的各个不同窗口的消息。程序中有一段代码,叫做消息循环。用来从队列中取出消息。并且将他们发送到相应的窗口过程。有些消息直接发送给窗口过程,不用放进消息队列中。
4.2 创建一个windows类
控制windows类信息的数据结构有两个:WNDCLASS和WNDCLASSX,
看下WNDCLASSX的结构:
4.2.1.注册windows类
当windows类已经定义并且存放在winclass中,必须将新的类注册,可以使用RegisterClassEx()和RegisterClass()这两个函数实现,前者对应WNDCLASSX,后者对应WNDCLASS。
函数原型:
ATOM RegisterClassEx(
CONST WNDCLASSEX *lpwcx
);
参数是一个指向新类定义的指针。
4.3 创建窗口
创建一个窗口使用CreateWIndowEx()或是CreateWindow()函数,下面来看下CreateWindowEx()的函数原型
4.3.1.显示和更新窗口
创建完窗口后,窗口却不一定可见,如果dwStyle没有设为为WS_VISIBLE,那就要手工显示窗口:
ShowWindow(hwnd,nCmdShow)
然后让windows更新窗口的内容
UpdateWindow();
4.4.事件句柄(event handle)| WinProc(窗口过程)
就是当事情发生时windows从主事件循环调用的回调函数。
每一个windows类都有一个独立的事件句柄,称为windows procedure 简称winproc(窗口过程)。
当运行任务时,窗口和其他应用程序窗口就会产生事件和消息,所有的消息进入一个队列,而该窗口消息发送到该窗口的专用队列中,然后主事件循环检索这些消息,并且将他们发送到该窗口的winproc(窗口过程)中来处理。
窗口过程函数原型:
LRESULT CALLBACK WindowProc(
HWND hwnd, //窗口句柄
UINT uMsg, //消息标识符
//进一步匹配或分类发送到msg参数中的信息
WPARAM wParam,
LPARAM lParam
);
例子:
4.5 主事件循环
主事件循环函数
while(GetMessage(&msg,NULL,0,0))
{
//虚拟加速键翻译器,把虚拟键消息转为字符消息
TranslateMessage(&msg);
//分派消息到窗口过程,由窗口过程来处理消息
DispatchMessage(&msg);
}
MSG结构:
typedef struct tagMSG {
HWND hwnd; //产生消息的窗口
UINT message; //消息标识符
//进一步匹配或分类发送到msg参数中的信息
WPARAM wParam;
LPARAM lParam;
DWORD time; //消息事件发生的时间
POINT pt; //鼠标位置
} MSG;
GetMessage()函数原型
//GetMessage()除了WM_QUIT返回值,其他都返回非零值。
BOOL GetMessage(
LPMSG lpMsg, //GetMessage()从消息队列中取出的消息
HWND hWnd, //窗口句柄,通常设为NULL,接受调用线程的所有窗口的窗口消息
UINT wMsgFilterMin, //要获取的窗口消息的最小值
UINT wMsgFilterMax //要获取的窗口消息的最大值,最小值和最大值设为,则为接受所有消息
);
可以看出,当程序在等待通过GetMessage()传递的消息的同时,主事件循环基本上是锁定的。如果要实现实时的无等候的事件循环,可以用PeekMessage()函数
BOOL PeekMessage(
LPMSG lpMsg,
HWND hWnd,
UINT wMsgFilterMin,
UINT wMsgFilterMax,
//控制如何从消息序列中检索消息
//PM_NOREMOVE:PeekMessage()处理后,消息没有从序列中取出
//PM_REMOVE:PeekMessage()处理后,消息已经从序列中去除
UINT wRemoveMsg
);
例子:
while(true)
{
if(PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
if(msg.message == WM_QUIT)
{
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
假设GetMessage(&msg,NULL,0,0)写成GetMessage(&msg,hwnd,0,0)会出现什么情况?
当点击关闭时,窗口句柄会被销毁,此时GetMessage(&msg,hwnd,0,0)将会返回-1值,由于在C++中,0是假其他都是真,所以条件为真,循环会继续下去,因此会陷入死循环中。打开任务管理器会看到cpu占用率到了100%(双核的应该是50%)
整个windows应用程序的创建 过程:
一个完整的windows应用程序如下: