[VC++深入详解] 1. Windows程序内部运行机制

本系列(VC++深入详解)为《VC++深入详解》(孙鑫 编著)读书笔记,很多例子都是仿照此书,很多概念也是来自此书,在对其做归纳总结的同时,也加入了自己的一些理解。

一、 最简单的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指定程序窗口的显示方式,例如最大化、最小化、隐藏等。四、 窗口

而现在我喜欢深邃的夜空,包容一切的黑暗和隐忍,留下眼泪也没人看见。

[VC++深入详解] 1. Windows程序内部运行机制

相关文章:

你感兴趣的文章:

标签云: