[VC++深入详解] 1. Windows程序内部运行机制
2013-11-18 22:08
1136 查看
本系列(VC++深入详解)为《VC++深入详解》(孙鑫 编著)读书笔记,很多例子都是仿照此书,很多概念也是来自此书,在对其做归纳总结的同时,也加入了自己的一些理解。
一、 最简单的Windows程序框架概览
下面我来层层剖析这段代码。
二、 头文件
windows.h这个文件包含了Windows API的声明,比如代码中的CreateWindow,PostQuitMessage等等,都是Windows API,是操作系统提供给程序员的函数接口。
三、 程序入口
Windows程序的入口函数是WinMain,其函数原型如下:
2. 至于_In_,它也是一个宏,它的定义比较复杂,我目前还不理解。它的定义位于sal.h:
3. hInstance是一个程序实例句柄(Handle of instance),它代表当前程序的实例。
4. hPrevInstance也是一个程序实例句柄,它代表之前一个相同程序的实例。比如A.exe是一个Windows程序,第一次打开它时,hPrevInstance为NULL,然后不关掉它,再打开,新进程的hPrevInstance就是第一个进程的hInstance。
5. lpCmdLine是传递给该程序的命令行参数,类型是LPSTR(Long pointer of string),相当于标准C程序中的argv。
6. nCmdShow指定程序窗口的显示方式,例如最大化、最小化、隐藏等。
四、 窗口
1. WNDCLASS是窗口类,其定义如下:
style是窗口的样式
lpfnWndProc是窗口过程,所谓窗口过程(Windows process)就是指窗口接收到消息时的处理函数,这个函数由操作系统调用。
cbClsExtra表示类附加内存的大小,一般为0;cbWndExtra表示窗口附加内存的大小,一般为0。
hInstance表示该窗口过程所属的窗口的句柄。
hIcon表示窗口图标的句柄(Handle of icon),可以使用LoadIcon获得一个窗口图标的句柄。
lpszMenuName是菜单的名称,没有菜单的话就为NULL。
wnd.lpszClassName是窗口类的名称,用于CreateWindow。
2. 当窗口类初始化完毕时,需要进行注册,调用RegisterClass。
3. 然后就可以显示窗口了。
CreateWindow函数的原型如下:
lpClassName是需要创建的窗体类的名称,必须是注册过的。lpWindowsName是窗口标题栏的内容。
值得注意的是dwStyle,它表示窗口的样式,我在这里取值是WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX,其定义如下,位于WinUser.h:
可以看出每个基本的宏(非Common Window Styles)都只有一个二进制位为1,所以需要多种特性的时候可以进行位或操作。WS_OVERLAPPEDWINDOW 是一个封装的宏,封装了6种特性,在这里我不需要其中的WS_MAXIMIZEBOX特性(支持最大化),所以我把WS_OVERLAPPEDWINDOW和~WS_MAXIMIZEBOX做了位与操作。
ShowWindow的原型如下:
nCmdShow表示显示参数(最小化显示、最大化显示、隐藏显示、正常显示等)。
值得注意的是它返回类型为BOOL,它并不是C++内置类型bool,而是int:
UpdateWindow函数负责立即向窗口发送一个WM_PAINT消息。
五、
未完待续
---------------------------------------
2014.5.4更新
由于工作原因,此文不再更新
一、 最简单的Windows程序框架概览
#include <windows.h> #include <stdio.h> LRESULT CALLBACK WndProc( _In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam ); int WINAPI WinMain( _In_ HINSTANCE hInstance, _In_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow ) { WNDCLASS wnd; wnd.cbClsExtra = 0; wnd.cbWndExtra = 0; wnd.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH); wnd.hCursor = LoadCursor(NULL, IDC_HAND); wnd.hIcon = LoadIcon(NULL, IDI_INFORMATION); wnd.hInstance = hInstance; wnd.lpfnWndProc = WndProc; wnd.lpszClassName = "uranux"; wnd.lpszMenuName = NULL; wnd.style = CS_VREDRAW | CS_HREDRAW; RegisterClass(&wnd); HWND hWnd = CreateWindow(wnd.lpszClassName, "Hello Uranux!", WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX, 100, 100, 500, 400, NULL, NULL, hInstance, NULL); ShowWindow(hWnd, SW_SHOWNORMAL); UpdateWindow(hWnd); MSG msg; BOOL bRet; while (bRet = GetMessage(&msg, hWnd, 0, 0)) { if (bRet == -1) { DWORD dwErr = GetLastError(); char info[10]; sprintf_s(info, "%d", dwErr); MessageBox(hWnd, info, "Error", 0); return -1; } else { TranslateMessage(&msg); DispatchMessage(&msg); } } return 0; } LRESULT CALLBACK WndProc( _In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam ) { HDC hDC; PAINTSTRUCT ps; static int charCount = 0; int x, y; switch (uMsg) { case WM_CHAR: hDC = GetDC(hwnd); TextOut(hDC, 100 + charCount * 8, 100, (LPCSTR)&wParam, 1); charCount++; ReleaseDC(hwnd, hDC); break; case WM_LBUTTONDOWN: case WM_MBUTTONDOWN: case WM_RBUTTONDOWN: hDC = GetDC(hwnd); char info[100]; char keyType[10]; switch (wParam) { case MK_LBUTTON: strcpy_s(keyType, "Left"); break; case MK_MBUTTON: strcpy_s(keyType, "Middle"); break; case MK_RBUTTON: strcpy_s(keyType, "Right"); break; default: break; } x = lParam & 0x0000FFFF; y = lParam >> 16; sprintf_s(info, "%s Key down, At (%d, %d)", keyType, x, y); TextOut(hDC, 200, 0, info, strlen(info)); ReleaseDC(hwnd, hDC); break; case WM_PAINT: hDC = BeginPaint(hwnd, &ps); TextOut(hDC, 0, 0, "Hi, I'm Uranux!", strlen("Hi, I'm Uranux!")); EndPaint(hwnd, &ps); break; case WM_CLOSE: if (IDYES == MessageBox(hwnd, "Are you sure?", "Quit", MB_YESNO)) { DestroyWindow(hwnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hwnd, uMsg, wParam, lParam); break; } return NULL; }
下面我来层层剖析这段代码。
二、 头文件
windows.h这个文件包含了Windows API的声明,比如代码中的CreateWindow,PostQuitMessage等等,都是Windows API,是操作系统提供给程序员的函数接口。
三、 程序入口
Windows程序的入口函数是WinMain,其函数原型如下:
int WINAPI WinMain( _In_ HINSTANCE hInstance, _In_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow )1. WINAPI是一个宏,值是__stdcall,它是一种函数调用约定,约定函数参数从右向左入站,由调用方清栈,所以该调用约定不能修饰变参函数。
2. 至于_In_,它也是一个宏,它的定义比较复杂,我目前还不理解。它的定义位于sal.h:
// Input parameters -------------------------- // _In_ - Annotations for parameters where data is passed into the function, but not modified. // _In_ by itself can be used with non-pointer types (although it is redundant). // e.g. void SetPoint( _In_ const POINT* pPT ); #define _In_ _SAL2_Source_(_In_, (), _Pre1_impl_(__notnull_impl_notref) _Pre_valid_impl_ _Deref_pre1_impl_(__readaccess_impl_notref))其中_SAL2_Source_也是一个宏,位于sal.h:
#define _SAL2_Source_(Name, args, annotes) _SA_annotes3(SAL_name, #Name, "", "2") _Group_(annotes _SAL_nop_impl_)其中_SA_annotes3也是一个宏,位于sal.h:
#define _SA_annotes3(n,pp1,pp2,pp3) __declspec(#n "(" _SA_SPECSTRIZE(pp1) "," _SA_SPECSTRIZE(pp2) "," _SA_SPECSTRIZE(pp3) ")")此处不再展开分析,后续会专门开篇说明。值得注意的是__declspec,它是另外一种函数调用约定,用于dll函数的导入导出,通常与extern "C"联合使用。
3. hInstance是一个程序实例句柄(Handle of instance),它代表当前程序的实例。
4. hPrevInstance也是一个程序实例句柄,它代表之前一个相同程序的实例。比如A.exe是一个Windows程序,第一次打开它时,hPrevInstance为NULL,然后不关掉它,再打开,新进程的hPrevInstance就是第一个进程的hInstance。
5. lpCmdLine是传递给该程序的命令行参数,类型是LPSTR(Long pointer of string),相当于标准C程序中的argv。
6. nCmdShow指定程序窗口的显示方式,例如最大化、最小化、隐藏等。
四、 窗口
1. WNDCLASS是窗口类,其定义如下:
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;要创建窗口,首先要定义一个窗口类,并完成其各个成员的初始化。
WNDCLASS wnd;
style是窗口的样式
wnd.style = CS_VREDRAW | CS_HREDRAW;CS_VREDRAW和CS_HREDRAW分别表示垂直重绘(Vertical redraw)和水平重绘(Horizontal redraw),CS表示Class style。
lpfnWndProc是窗口过程,所谓窗口过程(Windows process)就是指窗口接收到消息时的处理函数,这个函数由操作系统调用。
wnd.lpfnWndProc = WndProc;其类型为:
typedef LRESULT (CALLBACK* WNDPROC)(HWND, UINT, WPARAM, LPARAM);其中CALLBACK是一个宏,表示__stdcall。
cbClsExtra表示类附加内存的大小,一般为0;cbWndExtra表示窗口附加内存的大小,一般为0。
wnd.cbClsExtra = 0; wnd.cbWndExtra = 0;
hInstance表示该窗口过程所属的窗口的句柄。
wnd.hInstance = hInstance;
hIcon表示窗口图标的句柄(Handle of icon),可以使用LoadIcon获得一个窗口图标的句柄。
wnd.hIcon = LoadIcon(NULL, IDI_INFORMATION);hCursor表示窗口光标的句柄(Handle of cursor),可以使用LoadCursor获得一个窗口光标的句柄。
wnd.hCursor = LoadCursor(NULL, IDC_HAND);hbrBackground指定窗口类的背景画刷句柄(Handle of brush)。
wnd.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);GetStockObject的返回类型是HGDIOBJ,而hbrBackground的类型是HBRUSH,所以需要强制类型转换。
lpszMenuName是菜单的名称,没有菜单的话就为NULL。
wnd.lpszMenuName = NULL;
wnd.lpszClassName是窗口类的名称,用于CreateWindow。
wnd.lpszClassName = "uranux";
2. 当窗口类初始化完毕时,需要进行注册,调用RegisterClass。
RegisterClass(&wnd);
3. 然后就可以显示窗口了。
HWND hWnd = CreateWindow(wnd.lpszClassName, "Hello Uranux!", WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX, 100, 100, 500, 400, NULL, NULL, hInstance, NULL); ShowWindow(hWnd, SW_SHOWNORMAL); UpdateWindow(hWnd);
CreateWindow函数的原型如下:
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 );
lpClassName是需要创建的窗体类的名称,必须是注册过的。lpWindowsName是窗口标题栏的内容。
值得注意的是dwStyle,它表示窗口的样式,我在这里取值是WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX,其定义如下,位于WinUser.h:
/* * Window Styles */ #define WS_OVERLAPPED 0x00000000L #define WS_POPUP 0x80000000L #define WS_CHILD 0x40000000L #define WS_MINIMIZE 0x20000000L #define WS_VISIBLE 0x10000000L #define WS_DISABLED 0x08000000L #define WS_CLIPSIBLINGS 0x04000000L #define WS_CLIPCHILDREN 0x02000000L #define WS_MAXIMIZE 0x01000000L #define WS_CAPTION 0x00C00000L /* WS_BORDER | WS_DLGFRAME */ #define WS_BORDER 0x00800000L #define WS_DLGFRAME 0x00400000L #define WS_VSCROLL 0x00200000L #define WS_HSCROLL 0x00100000L #define WS_SYSMENU 0x00080000L #define WS_THICKFRAME 0x00040000L #define WS_GROUP 0x00020000L #define WS_TABSTOP 0x00010000L #define WS_MINIMIZEBOX 0x00020000L #define WS_MAXIMIZEBOX 0x00010000L #define WS_TILED WS_OVERLAPPED #define WS_ICONIC WS_MINIMIZE #define WS_SIZEBOX WS_THICKFRAME #define WS_TILEDWINDOW WS_OVERLAPPEDWINDOW /* * Common Window Styles */ #define WS_OVERLAPPEDWINDOW (WS_OVERLAPPED | \ WS_CAPTION | \ WS_SYSMENU | \ WS_THICKFRAME | \ WS_MINIMIZEBOX | \ WS_MAXIMIZEBOX) #define WS_POPUPWINDOW (WS_POPUP | \ WS_BORDER | \ WS_SYSMENU) #define WS_CHILDWINDOW (WS_CHILD)
可以看出每个基本的宏(非Common Window Styles)都只有一个二进制位为1,所以需要多种特性的时候可以进行位或操作。WS_OVERLAPPEDWINDOW 是一个封装的宏,封装了6种特性,在这里我不需要其中的WS_MAXIMIZEBOX特性(支持最大化),所以我把WS_OVERLAPPEDWINDOW和~WS_MAXIMIZEBOX做了位与操作。
ShowWindow的原型如下:
BOOL WINAPI ShowWindow( _In_ HWND hWnd, _In_ int nCmdShow );
nCmdShow表示显示参数(最小化显示、最大化显示、隐藏显示、正常显示等)。
值得注意的是它返回类型为BOOL,它并不是C++内置类型bool,而是int:
typedef int BOOL;位与minwindef.h。
UpdateWindow函数负责立即向窗口发送一个WM_PAINT消息。
五、
未完待续
---------------------------------------
2014.5.4更新
由于工作原因,此文不再更新
相关文章推荐
- 孙鑫VC++深入详解(1):windows程序内部运行机制
- 《VC++深入详解》读书笔记 第一章:Windows程序内部运行机制
- VC++深入详解(孙鑫)(第一章 Windows 程序内部运行机制)学习笔记
- 《VC++深入详解》学习笔记 第一章 Windows程序内部运行机制
- 《VC++深入详解》学习笔记[1]——第1章 Windows程序内部运行机制
- VC++深入详解学习笔记之Windows程序内部运行机制
- Windows程序内部运行机制
- 深入浅出话VC++(1)——Windows程序内部运行机制
- 孙鑫VC++讲座笔记-(1)Windows程序内部运行机制
- 再理解Windows程序内部运行机制
- 孙鑫VC++讲座笔记-(1)Windows程序内部运行机制
- 【vc】1_Windows程序内部运行机制
- Windows程序内部运行机制简易分析
- 《VC++深入详解》读书笔记——1.Windows程序内部运行机制
- 孙鑫VC++讲座笔记-(1)Windows程序内部运行机制
- 孙鑫VC++讲座笔记-(1) Windows程序内部运行机制
- 第一章,Windows程序内部运行机制
- 孙鑫-MFC笔记一--Windows程序内部运行机制
- Windows 程序内部运行机制
- Windows程序内部运行机制