本篇文章会以 Application Instance 示例程序为例讲到如何在同一个可执行文件或DLL的多个实例间共享静态数据。同时会接触一些消息相关的知识。
每个.exe文件或DLL文件映像由许多段组成。按照惯例,每个标准的段名都以点号开始。例如在编译程序的时候,编译器会将代码放在一个.text的段中。此外编译器还会将未初始化的数据放在.bss段中,将已经初始化的数据放在.data段中。
每个段可以指定属性,比如 可读、可写、可执行、可共享。
这里我们说的可共享就是关闭了写时复制机制,通过这种方法可以让.exe或DLL在多个实例间共享数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
#include <Windows.h> // 整个系统的消息,对应用程序来说应该是独一无二的 // The system-wide window message, unique to the application UINT g_uMsgAppInstCountUpdate = WM_APP + 123; //创建一个Shared段,并存放一个数据 - 注意,这个数据必须是初始化的 #pragma data_seg("Shared") volatile LONG g_lApplicationInstances = 0; #pragma data_seg() // 告诉链接器,让 Shared 段属性为 可读可写可共享 // Tell the linker to make the Shared section readable, writable, and shared. #pragma comment(linker ,"/Section:Shared,RWS") LRESULT CALLBACK WinSunProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ); INT WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lCmdLine, INT nCmdShow ) { //设计窗口类 WNDCLASS wndcls; wndcls.cbClsExtra = 0; wndcls.cbWndExtra = 0; wndcls.hbrBackground = (HBRUSH)GetStockObject(COLOR_WINDOWFRAME); wndcls.hCursor = LoadCursor(NULL, IDC_ARROW); wndcls.hIcon = LoadIcon(NULL, IDI_APPLICATION); wndcls.hInstance = hInstance; wndcls.lpfnWndProc = WinSunProc; wndcls.lpszClassName = TEXT("NormalWindow"); wndcls.lpszMenuName = NULL; wndcls.style = CS_HREDRAW | CS_VREDRAW; //注册窗口类 ATOM nAtom = 0; nAtom = RegisterClass(&wndcls); if(nAtom == 0) { MessageBox(NULL, TEXT("Register Failed"), TEXT("Error"), MB_OK | MB_ICONERROR); return FALSE; } //注册消息 g_uMsgAppInstCountUpdate = RegisterWindowMessage(TEXT("MsgAppInstCountUpdate")); //创建窗口 HWND hwnd; hwnd = CreateWindow(TEXT("NormalWindow"), TEXT("ApplicationInstance"), WS_OVERLAPPEDWINDOW, 300, 300, 600, 400, NULL, NULL, hInstance, NULL); if(hwnd == NULL) { MessageBox(NULL, TEXT("Create Window Failed"), TEXT("Error"), MB_OK | MB_ICONERROR); return FALSE; } //显示窗口 ShowWindow(hwnd, SW_SHOWNORMAL); //刷新窗口 UpdateWindow(hwnd); //消息循环 BOOL bRet; MSG msg; while(bRet = GetMessage(&msg, hwnd, 0, 0)) { if(bRet == -1) { // handle the error and possibly exit return -1; } else { TranslateMessage(&msg); DispatchMessage(&msg); } } return msg.wParam; } LRESULT CALLBACK WinSunProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { //收到我们指定的消息 if(uMsg == g_uMsgAppInstCountUpdate) { RECT rect; //重绘窗口用到 //获取窗口大小 GetClientRect(hwnd, &rect); //要求重绘 InvalidateRect(hwnd, &rect, TRUE); //更新窗口 UpdateWindow(hwnd); return 0; } wchar_t A[30]; switch(uMsg) { case WM_CREATE: // 有一个新的应用程序在运行 // There is another instance of this application running InterlockedExchangeAdd(&g_lApplicationInstances, 1); //发送消息 - 通知更新窗口 PostMessage(HWND_BROADCAST, g_uMsgAppInstCountUpdate, 0, 0); break; case WM_PAINT: ////绘制消息 HDC hDC; PAINTSTRUCT ps; hDC = BeginPaint(hwnd, &ps); wsprintf(A, TEXT("应用程序运行数量:%d"), g_lApplicationInstances); TextOut(hDC, 10, 10, A, lstrlen(A)); EndPaint(hwnd, &ps); break; case WM_CLOSE: ////关闭按钮消息 if(IDYES == MessageBox(hwnd, TEXT("是否真的退出?"), TEXT("QuitMessage"), MB_YESNO)) { DestroyWindow(hwnd); } break; case WM_DESTROY:////当窗口销毁时,会调用WinSunProc // 整个应用程序实例要退出了 // This instance of the application is terminating InterlockedExchangeAdd(&g_lApplicationInstances, -1); // 让其他应用程序更新他们的显示信息 // Have all other instances update their display PostMessage(HWND_BROADCAST, g_uMsgAppInstCountUpdate, 0, 0); PostQuitMessage(0);////发送一个退出消息 break; default: return DefWindowProc(hwnd, uMsg, wParam, lParam); } return 0; } |
代码大致就是这样…
除了WinMain程序套路之外,还有就是 注册消息…发送接收消息。
【Windows核心编程】Application Instance示例程序