N多年没有写过 Window 程序了。为了研究 WebRTC 源码,这两天从新学习一下。还记得上大学的时候看过 《Windows95 程式设计》台湾版,对那本书印象极为深入。一是当时国内确实没有一本写的那么深刻的书籍,二是那本书翻译的特别好,让人一看就特别明白。10多年过多了,当时的情景还记忆犹新,也可见那本书写的有多好了。git
Windows开发有不少知识点,窗口啊,句柄啊,消息啊,重绘啊,baba .....,但一个 Windows 程序的核心就是一个消息处理机制。github
Windows程序是消息为驱动的,因此它的核心就是消息的传递与处理。如鼠标消息、键盘消息,Timer消息,窗口的建立与消毁等等。那么,Windows程序是在哪儿处理消息呢?是否掌握了它,就控制了Windows程序的核心呢?答案是确定的,它就是 WndProc 函数。全部的消息都要通过这个函数处理。bash
Windows 程序有两种消息,一种是队列消息,它经过 DispatchMessage 函数分发给 WndProc 函数,像鼠标消息、键盘消息,Timer消息都是这类消息。另外一种是非队列消息,它是系统函数直接发送给 WndProc 函数的,像窗口的建立与消毁消息,WM_COMMON消息等等都是非队列消息。函数
一个最简单的 Windows 程序都包括哪些内容呢?下面咱们详细介绍一下:学习
咱们都知道不管是Windows程序,仍是Linux程序,也不管是C/C++,仍是 Java语言,它们都有一个 main 函数。更准确点说应该叫“程序入口点”。ui
咱们写程序时,通常都以 main 开头,编译器在编译该程序时,会将 main 函数地址写入到可执行文件的文件头中,这就是“程序入口点”了。spa
在执行程序时,操做系统首先经过程序加载器将要运行的程序加载到内存中,而后从新计算符号地址表。一切准备就绪后,才跳到程序入口点,将一条条指令送入CPU流水线开始执行程序。这就是程序的运行的基本流程。操作系统
所以,咱们能够知道出每一个程序都有一个入口点。可是否不必定以 main 为开头呢? 其实,只要编译器能识别出入口点就能够,没必要非要以 main 为标志。对于 Windows 程序就是这样,它就不使用 main 而换做了 WinMain 做为程序入口点。格式以下:命令行
int CALLBACK WinMain(
_In_ HINSTANCE hInstance,
_In_ HINSTANCE hPrevInstance,
_In_ LPSTR lpCmdLine,
_In_ int nCmdShow
);
复制代码
上面一节我也介绍了 WndProc 是 Windows 程序的消息中心。全部的消息都要在这个函数中处理。如 窗口建立时发送的 WM_CREATE 消息,若是咱们不处理它,Windows 操做系统就不会显示建立的窗口。翻译
但 Windows 中有那么多消息,咱们每一个都处理企不是要累死人?因此 Windows 很贴心的提供了一个API,就是 DefWindowProc 函数。在该函数中对全部的 Windows 消息都作了默认处理。若是咱们很懒的话,能够将全部消息都交由它来处理就行了。
有没有坐过山车的感脚?开始以为很苦闷,忽然又拨云见日了。嘿嘿!
LRESULT CALLBACK WndProc(
_In_ HWND hwnd,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
) {
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
复制代码
咱们在建立窗口以前要注册一个窗口类,它是干啥用的呢?就是告诉操做系统,我要建立个什么样子的窗口,是啥背景色,鼠标是啥样子的,程序叫啥名子等等。
有了这个窗口类,咱们就能够建立不少这样子的窗口了,这样是否是以为很方便呢?固然,若是只建立一个貌似也就没啥子优点!
除了上面那些,它其实最最重要的做用是指定 WndProc 函数,也就是 Window 程序的 "消息中心"。消息中心是谁,彻底是由 RegisterClass 说了算。它说 WndProc 就是 WndProc,它说 WindowProc 就是 WindowProc。
它长的像下面这个样子:
// 类名
WCHAR* cls_Name = L"My Class";
// 设计窗口类
WNDCLASS wc = { };
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = cls_Name;
wc.hInstance = hInstance;
// 注册窗口类
RegisterClass(&wc);
复制代码
建立窗口就比较简单了,高多少,宽多少,透明的仍是非透明的,可显示仍是不可显示,标题栏上要写啥字等等,这些都是它说了算。形式以下:
// 建立窗口
HWND hwnd = CreateWindow(
cls_Name, //类名,要和刚才注册的一致
L"个人应用程序", //窗口标题文字
WS_OVERLAPPEDWINDOW, //窗口外观样式
38, //窗口相对于父级的X坐标
20, //窗口相对于父级的Y坐标
480, //窗口的宽度
250, //窗口的高度
NULL, //没有父窗口,为NULL
NULL, //没有菜单,为NULL
hInstance, //当前应用程序的实例句柄
NULL); //没有附加数据,为NULL
复制代码
窗口建立完了,还要主动调函数让它显示出来,不然它是不会出来干活的。形式以下:
// 显示窗口
ShowWindow(hwnd, SW_SHOW);
复制代码
这部分工做是在 WinMain 函数中要作的事儿。在 WinMain 中写一个循环,不停的从系统消息队列中取消息。
若是此时没有消息,则该线被程阻塞,并将CPU资源释放;若是有消息,须要判断是否是退出消息?若是不是,使用 DispatchMessage 将该消息分配出去。若是是退出消息,则退出消息循环,程序结束。代码以下:
void WinMan(...){
...
// 消息循环
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
复制代码
以上就是一个最简单的窗口 Window 程序。了解了上在的知识,你们是否是以为不用 MFC 本身写个 Windows 程序也不是很难了?
int CALLBACK WinMain(
_In_ HINSTANCE hInstance, //句柄
_In_ HINSTANCE hPrevInstance, //老是 NULL
_In_ LPSTR lpCmdLine, //在命令行启动程序时的命令
_In_ int nCmdShow //程序启动时的显示方式
);
复制代码
typedef struct tagWNDCLASS {
UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HINSTANCE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCTSTR lpszMenuName;
LPCTSTR lpszClassName;
} WNDCLASS, *PWNDCLASS;
ATOM WINAPI RegisterClass(
_In_ const WNDCLASS *lpWndClass
);
复制代码
HWND WINAPI CreateWindow(
_In_opt_ LPCTSTR lpClassName,
_In_opt_ LPCTSTR lpWindowName,
_In_ DWORD dwStyle,
_In_ int x,
_In_ int y,
_In_ int nWidth,
_In_ int nHeight,
_In_opt_ HWND hWndParent,
_In_opt_ HMENU hMenu,
_In_opt_ HINSTANCE hInstance,
_In_opt_ LPVOID lpParam
);
复制代码
本文首先介绍了一个Windows程序程序是由消息驱动的,它的核心是注册窗口类API RegisterClass 中指定的 WinProc 函数。WinProc是Windows消息处理中心,全部的消息都要交由它来处理。而后对一个最简单的 Windows程序作了剖析,指出经过 6 大步能够建立出一个最简单的 Windows程序,它们分别是:
至此,一个Windows程序窗口已经展示在你面前了。后面就能够往里不断的增长内容了。
但愿本文能对你有所帮助,谢谢!github地址