关闭

GetMessage 和 PeekMessage

标签: nullwindows工作filterusermfc
1021人阅读 评论(0) 收藏 举报
分类:

装载:http://blog.csdn.net/silvergingko/article/details/6087722

在写Win32应用程序时,消息循环是最基本的框架组成部分之一。而消息循环使用最多的两种形式就是:

  1. // C++ code  
  2. // 第一种消息循环。  
  3. MSG msg;  
  4. while (GetMessage(&msg, NULL, 0, 0))  
  5. {  
  6.     //消息过滤。  
  7.     if (msg.message == WM_XXX)  
  8.     {  
  9.         Filter(&msg);  
  10.         //continue;  
  11.     }  
  12.   
  13.     //解析加速键(快捷键)。  
  14.     if (TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) continue;  
  15.   
  16.     //翻译 WM_KEYXXX 到 WM_CHAR 的消息。  
  17.     TranslateMessage(&msg);  
  18.     //派送消息给窗口处理函数处理。  
  19.     DispatchMessage(&msg);  
  20. }  
  21.   
  22. // 第二种消息循环。  
  23. while (TRUE)  
  24. {  
  25.     if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))  
  26.     {  
  27.         //退出消息循环。  
  28.         if (msg.message == WM_QUIT) break;  
  29.   
  30.         //消息过滤。  
  31.         if (msg.message == WM_XXX)  
  32.         {  
  33.             Filter(&msg);  
  34.             //continue;  
  35.         }  
  36.   
  37.         //解析加速键(快捷键)。  
  38.         if (TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) continue;  
  39.   
  40.         //翻译 WM_KEYXXX 到 WM_CHAR 的消息。  
  41.         TranslateMessage(&msg);  
  42.         //派送消息给窗口处理函数处理。  
  43.         DispatchMessage(&msg);  
  44.     }  
  45.     else  
  46.     {  
  47.         //无消息时的其他处理。  
  48.         OnIdle();  
  49.     }  
  50. }  

 

无论是使用哪一种,运行程序体验一下后,感觉都是一样的,看不出这两种循环方式对程序性能的影响。曾在<琢石成器—Windows环境下32位汇编语言程序设计>一书中,作者罗云杉指出, 当进入 GetMessage 后,Windows 在发现线程消息队列中无消息时,会将该线程置于睡眠状态,而不会通过 GetMessage 将执行权继续交给用户执行,直到线程的消息队列有消息后将线程再次唤醒,GetMessage 返回,执行该线程的代码。其中,作者一针见血的指出,既使通过 Windows 的线程调度机制,界面线程刚刚开始执行它的代码,只要调用了 GetMessage ,发现无消息, Windows 就会剥夺该线程剩余的时间片,置线程于睡眠状态,唤醒其它线程争夺 CPU 。而与 GetMessage 截然相反,PeekMessage 执行时,如果线程的消息队列中无消息,PeekMessage 就会立刻返回,执行后面的代码,直到该线程用完它本次的时间片,不会因为无消息而被剥夺剩余的时间片。

按照这种理论,显然通过 PeekMessage 的消息循环将使用更多的时间片。

 

按照程序设计理论,好的程序应尽可能多使用 CPU ,使 CPU 尽可能保持一直运作的状态, CPU 空闲是对 CPU 资源的浪费。按照这个理论,应该是尽可能使用 GetMessage 进行消息循环, 而不是利用 PeekMessage 进行消息循环。但事实上,这两种循环方式各有优缺点。

当线程的消息队列中无消息时:

对于 GetMessage ,由于它出让了自己的 CPU 时间片,界面线程使用 CPU 的时间可能会大大减少,而其他线程有更多的机会获得对 CPU 的使用,通常一个应用程序中会有一个界面线程伴随着 N 条工作线程,这样工作线程将能处理更多的工作。

对于 PeekMessage ,由于它不会出让自己的时间片,可以处理一些与消息无关但又与界面相关的工作。如上例第 48 行的 OnIdle,在其中可以进行处理,当然 OnIdle 函数理应尽快返回,否则有消息到达队列后,由于 OnIdle 没有返回,无法执行 DispatchMessage ,使得消息无法被窗口处理函数处理,造成界面呆滞,停止响应用户操作的糟糕状况。事实上,在 MFC 中, OnIdle 函数中,就是处理一些根据菜单状态(禁用/启用)、状态栏状态(大写键、插入键等toggle键的状态),进行相应显示的工作。

 

查看 MSDN 中关于 GetMessage 和 PeekMessage 的文档说明,并无明确指出以上提到的这种情况。为了进行验证,写了3个程序,两个 Win32 窗口程序,一个控制台程序。GetMsgApp 使用的是 GetMessage 进行消息循环, PeekMsgApp 使用的是 PeekMessage 进行消息循环。 MsgAppCaller 用来创建窗口程序,睡眠一段时间后,向窗口程序发送一个消息,窗口程序在收到消息后,显示自己已经使用的 CPU 时间(分成内核模式和用户模式时间)。

GetMsgApp:

 

  1. // C++ code  
  2. // GetMsgApp.cpp  
  3.   
  4. #include <Windows.h>  
  5. #include <tchar.h>  
  6. #include <strsafe.h>  
  7. #include "ZString.h"  
  8.   
  9. // 函数前导声明。  
  10. ATOM                MyRegisterClass(HINSTANCE hInstance);  
  11. BOOL                InitInstance(HINSTANCEint);  
  12. LRESULT CALLBACK    WndProc(HWNDUINTWPARAMLPARAM);  
  13. BOOL                SetMsg();  
  14. VOID                GetEllapsedTime();  
  15. VOID                ShowEllapsedTime(HDC hdc);  
  16.   
  17. // 将 FILETIME 类型转换成 __int64 类型的宏。  
  18. #define FILETIME2INT64(ft)      ((ft).dwLowDateTime | /  
  19.     (Int64ShllMod32((ft).dwHighDateTime, 32)))  
  20.   
  21. // 全局变量。  
  22. LPCTSTR     g_pszClass = _T("GetMsgWndCls");  
  23. LPCTSTR     g_pszWindow = _T("GetMsgWnd");  
  24. UINT        g_uMsg = 0;  
  25. HWND        g_hWnd;  
  26. DOUBLE      g_dKernel;  
  27. DOUBLE      g_dUser;  
  28. BOOL        g_bErr = FALSE;  
  29.   
  30. int APIENTRY _tWinMain(HINSTANCE hInstance,  
  31.     HINSTANCE hPrevInstance,  
  32.     LPTSTR    lpCmdLine,  
  33.     int       nCmdShow)  
  34. {  
  35.     UNREFERENCED_PARAMETER(hPrevInstance);  
  36.     UNREFERENCED_PARAMETER(lpCmdLine);  
  37.   
  38.     if (SetMsg() == FALSE)  
  39.     {  
  40.         TCHAR szMessage[64] = { 0 };  
  41.         DWORD dwErr = GetLastError();  
  42.   
  43.         StringCchPrintf(szMessage, _countof(szMessage),  
  44.             _T("ERROR: failed to SetMsg(#%u)"), dwErr);  
  45.         MessageBox(NULL, szMessage, _T("GetMsg App"), MB_OK);  
  46.         return 1;  
  47.     }  
  48.   
  49.     MyRegisterClass(hInstance);  
  50.   
  51.     if (!InitInstance (hInstance, nCmdShow))  
  52.     {  
  53.         return FALSE;  
  54.     }  
  55.   
  56.     MSG msg;  
  57.     while (GetMessage(&msg, NULL, 0, 0))  
  58.     {  
  59.         if (msg.message == g_uMsg)  
  60.         {  
  61.             GetEllapsedTime();  
  62.             ShowWindow(g_hWnd, SW_SHOWNORMAL);  
  63.             continue;  
  64.         }  
  65.         TranslateMessage(&msg);  
  66.         DispatchMessage(&msg);  
  67.     }  
  68.   
  69.     return (int) msg.wParam;  
  70. }  
  71.   
  72. ATOM MyRegisterClass(HINSTANCE hInstance)  
  73. {  
  74.     WNDCLASSEX wcex = { sizeof(WNDCLASSEX) };  
  75.   
  76.     wcex.style          = CS_HREDRAW | CS_VREDRAW;  
  77.     wcex.lpfnWndProc    = WndProc;  
  78.     wcex.hInstance      = hInstance;  
  79.     wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);  
  80.     wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);  
  81.     wcex.lpszClassName  = g_pszClass;  
  82.     return RegisterClassEx(&wcex);  
  83. }  
  84.   
  85. BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)  
  86. {  
  87.     g_hWnd = CreateWindow(g_pszClass, g_pszWindow, WS_OVERLAPPEDWINDOW,  
  88.         CW_USEDEFAULT, 0, 500, 200, NULL, NULL, hInstance, NULL);  
  89.   
  90.     if (!g_hWnd)  
  91.     {  
  92.         return FALSE;  
  93.     }  
  94.   
  95.     ShowWindow(g_hWnd, SW_HIDE);  
  96.     UpdateWindow(g_hWnd);  
  97.   
  98.     return TRUE;  
  99. }  
  100.   
  101. LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)  
  102. {  
  103.     int wmId, wmEvent;  
  104.     PAINTSTRUCT ps;  
  105.     HDC hdc;  
  106.   
  107.     switch (message)  
  108.     {  
  109.     case WM_PAINT:  
  110.         hdc = BeginPaint(hWnd, &ps);  
  111.           
  112.         if (g_dKernel > 0 || g_dUser > 0)  
  113.         {  
  114.             ShowEllapsedTime(hdc);  
  115.         }  
  116.   
  117.         EndPaint(hWnd, &ps);  
  118.         return TRUE;  
  119.     case WM_DESTROY:  
  120.         PostQuitMessage(0);  
  121.         break;  
  122.     default:  
  123.         return DefWindowProc(hWnd, message, wParam, lParam);  
  124.     }  
  125.     return 0;  
  126. }  
  127.   
  128. // 解析通过命令行传递过来的消息值,该值是通过调用 RegisterWindowMessage 获取的。  
  129. BOOL SetMsg()  
  130. {  
  131.     LPTSTR pszCmdLine = GetCommandLine();  
  132.     LPCTSTR pPos = NULL;  
  133.   
  134.     pPos = _tcschr(pszCmdLine, _T('"'));  
  135.     if (pPos != NULL)  
  136.     {  
  137.         pPos = _tcschr(pPos, _T('"'));  
  138.     }  
  139.     pPos = _tcschr(pszCmdLine, _T(' '));  
  140.     if (pPos != NULL)  
  141.     {  
  142.         pPos++;  
  143.     }  
  144.     else  
  145.     {  
  146.         pPos = pszCmdLine;  
  147.     }  
  148.     if (*pPos == _T('/'))  
  149.     {  
  150.         pPos++;  
  151.     }  
  152.       
  153.     //Str2UInt 是在 ZString.cpp 定义的字符串转换函数。  
  154.     return Str2UInt(pPos, &g_uMsg, FALSE);  
  155. }  
  156.   
  157. VOID GetEllapsedTime()  
  158. {  
  159.     FILETIME ftCreate, ftExit, ftKernel, ftUser;  
  160.   
  161.     if (GetProcessTimes(GetCurrentProcess(), &ftCreate, &ftExit, &ftKernel, &ftUser)  
  162.         != NULL)  
  163.     {  
  164.         INT64 i64Kernel, i64User;  
  165.         i64Kernel = FILETIME2INT64(ftKernel);  
  166.         g_dKernel = (DOUBLE)(i64Kernel / 10000.0);  
  167.         i64User = FILETIME2INT64(ftUser);  
  168.         g_dUser = (DOUBLE)(i64User / 10000.0);  
  169.     }  
  170.     else  
  171.     {  
  172.         g_bErr = TRUE;  
  173.     }  
  174. }  
  175.   
  176. VOID ShowEllapsedTime(HDC hdc)  
  177. {  
  178.     TCHAR sz[64] = { 0 };  
  179.       
  180.     if (g_bErr == FALSE)  
  181.     {  
  182.         StringCchPrintf(sz, _countof(sz),  
  183.             _T("kernel time-->%lfms && user time-->%lfms"),  
  184.             g_dKernel, g_dUser);  
  185.     }  
  186.     else  
  187.     {  
  188.         StringCchPrintf(sz, _countof(sz),  
  189.         _T("ERROR: failed to GetProcessTimes(#%u)"), GetLastError());  
  190.     }  
  191.   
  192.     TextOut(hdc, 10, 10, sz, _tcslen(sz));  
  193. }  
  194. // end of GetMsgApp.cpp  
 

 

PeekMsgApp:

  1. // C++ code  
  2. // PeekMsgApp.cpp  
  3.   
  4. #include <Windows.h>  
  5. #include <tchar.h>  
  6. #include <strsafe.h>  
  7. #include "ZString.h"  
  8.   
  9. // 函数前导声明。  
  10. ATOM                MyRegisterClass(HINSTANCE hInstance);  
  11. BOOL                InitInstance(HINSTANCEint);  
  12. LRESULT CALLBACK    WndProc(HWNDUINTWPARAMLPARAM);  
  13. BOOL                SetMsg();  
  14. VOID                GetEllapsedTime();  
  15. VOID                ShowEllapsedTime(HDC hdc);  
  16.   
  17. // 将 FILETIME 类型转换成 __int64 类型的宏。  
  18. #define FILETIME2INT64(ft)      ((ft).dwLowDateTime | /  
  19.     (Int64ShllMod32((ft).dwHighDateTime, 32)))  
  20.   
  21. // 全局变量。  
  22. LPCTSTR     g_pszClass = _T("PeekMsgWndCls");  
  23. LPCTSTR     g_pszWindow = _T("PeekMsgWnd");  
  24. UINT        g_uMsg = 0;  
  25. HWND        g_hWnd;  
  26. DOUBLE      g_dKernel;  
  27. DOUBLE      g_dUser;  
  28. BOOL        g_bErr = FALSE;  
  29.   
  30. int APIENTRY _tWinMain(HINSTANCE hInstance,  
  31.     HINSTANCE hPrevInstance,  
  32.     LPTSTR    lpCmdLine,  
  33.     int       nCmdShow)  
  34. {  
  35.     UNREFERENCED_PARAMETER(hPrevInstance);  
  36.     UNREFERENCED_PARAMETER(lpCmdLine);  
  37.   
  38.     if (SetMsg() == FALSE)  
  39.     {  
  40.         TCHAR szMessage[64] = { 0 };  
  41.         DWORD dwErr = GetLastError();  
  42.   
  43.         StringCchPrintf(szMessage, _countof(szMessage),  
  44.             _T("ERROR: failed to SetMsg(#%u)"), dwErr);  
  45.         MessageBox(NULL, szMessage, _T("PeekMsg App"), MB_OK);  
  46.         return 1;  
  47.     }  
  48.   
  49.     MyRegisterClass(hInstance);  
  50.   
  51.     if (!InitInstance (hInstance, nCmdShow))  
  52.     {  
  53.         return FALSE;  
  54.     }  
  55.   
  56.     MSG msg;  
  57.     while (TRUE)  
  58.     {  
  59.         if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))  
  60.         {  
  61.             if (msg.message == WM_QUIT) break;  
  62.             if (msg.message == g_uMsg)  
  63.             {  
  64.                 GetEllapsedTime();  
  65.                 ShowWindow(g_hWnd, SW_SHOWNORMAL);  
  66.                 continue;  
  67.             }  
  68.             TranslateMessage(&msg);  
  69.             DispatchMessage(&msg);  
  70.         }  
  71.     }  
  72.   
  73.     return (int) msg.wParam;  
  74. }  
  75.   
  76. ATOM MyRegisterClass(HINSTANCE hInstance)  
  77. {  
  78.     WNDCLASSEX wcex = { sizeof(WNDCLASSEX) };  
  79.   
  80.     wcex.style          = CS_HREDRAW | CS_VREDRAW;  
  81.     wcex.lpfnWndProc    = WndProc;  
  82.     wcex.hInstance      = hInstance;  
  83.     wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);  
  84.     wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);  
  85.     wcex.lpszClassName  = g_pszClass;  
  86.     return RegisterClassEx(&wcex);  
  87. }  
  88.   
  89. BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)  
  90. {  
  91.     g_hWnd = CreateWindow(g_pszClass, g_pszWindow, WS_OVERLAPPEDWINDOW,  
  92.         CW_USEDEFAULT, 0, 500, 200, NULL, NULL, hInstance, NULL);  
  93.   
  94.     if (!g_hWnd)  
  95.     {  
  96.         return FALSE;  
  97.     }  
  98.   
  99.     ShowWindow(g_hWnd, SW_HIDE);  
  100.     UpdateWindow(g_hWnd);  
  101.   
  102.     return TRUE;  
  103. }  
  104.   
  105. LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)  
  106. {  
  107.     PAINTSTRUCT ps;  
  108.     HDC hdc;  
  109.   
  110.     switch (message)  
  111.     {  
  112.     case WM_PAINT:  
  113.         hdc = BeginPaint(hWnd, &ps);  
  114.   
  115.         if (g_dKernel > 0 || g_dUser > 0)  
  116.         {  
  117.             ShowEllapsedTime(hdc);  
  118.         }  
  119.   
  120.         EndPaint(hWnd, &ps);  
  121.         return TRUE;  
  122.     case WM_DESTROY:  
  123.         PostQuitMessage(0);  
  124.         break;  
  125.     default:  
  126.         return DefWindowProc(hWnd, message, wParam, lParam);  
  127.     }  
  128.     return 0;  
  129. }  
  130.   
  131. // 解析通过命令行传递过来的消息值,该值是通过调用 RegisterWindowMessage 获取的。  
  132. BOOL SetMsg()  
  133. {  
  134.     LPTSTR pszCmdLine = GetCommandLine();  
  135.     LPCTSTR pPos = NULL;  
  136.   
  137.     pPos = _tcschr(pszCmdLine, _T('"'));  
  138.     if (pPos != NULL)  
  139.     {  
  140.         pPos = _tcschr(pPos, _T('"'));  
  141.     }  
  142.     pPos = _tcschr(pszCmdLine, _T(' '));  
  143.     if (pPos != NULL)  
  144.     {  
  145.         pPos++;  
  146.     }  
  147.     else  
  148.     {  
  149.         pPos = pszCmdLine;  
  150.     }  
  151.     if (*pPos == _T('/'))  
  152.     {  
  153.         pPos++;  
  154.     }  
  155.   
  156.     //Str2UInt 是在 ZString.cpp 定义的字符串转换函数。  
  157.     return Str2UInt(pPos, &g_uMsg, FALSE);  
  158. }  
  159.   
  160. VOID GetEllapsedTime()  
  161. {  
  162.     FILETIME ftCreate, ftExit, ftKernel, ftUser;  
  163.   
  164.     if (GetProcessTimes(GetCurrentProcess(), &ftCreate, &ftExit, &ftKernel, &ftUser)  
  165.         != NULL)  
  166.     {  
  167.         INT64 i64Kernel, i64User;  
  168.         i64Kernel = FILETIME2INT64(ftKernel);  
  169.         g_dKernel = (DOUBLE)(i64Kernel / 10000.0);  
  170.         i64User = FILETIME2INT64(ftUser);  
  171.         g_dUser = (DOUBLE)(i64User / 10000.0);  
  172.     }  
  173.     else  
  174.     {  
  175.         g_bErr = TRUE;  
  176.     }  
  177. }  
  178.   
  179. VOID ShowEllapsedTime(HDC hdc)  
  180. {  
  181.     TCHAR sz[64] = { 0 };  
  182.   
  183.     if (g_bErr == FALSE)  
  184.     {  
  185.         StringCchPrintf(sz, _countof(sz),  
  186.             _T("kernel time-->%lfms && user time-->%lfms"),  
  187.             g_dKernel, g_dUser);  
  188.     }  
  189.     else  
  190.     {  
  191.         StringCchPrintf(sz, _countof(sz),  
  192.             _T("ERROR: failed to GetProcessTimes(#%u)"), GetLastError());  
  193.     }  
  194.   
  195.     TextOut(hdc, 10, 10, sz, _tcslen(sz));  
  196. }  
  197. // end of PeekMsgApp.cpp  

 

MsgAppCaller:

  1. // C++ code  
  2. // MsgAppCaller.cpp  
  3. #include <stdio.h>  
  4. #include <tchar.h>  
  5. #include <Windows.h>  
  6. #include <strsafe.h>  
  7.   
  8. // 函数前导声明。  
  9. VOID TestApp(LPTSTRDWORD);  
  10.   
  11. // 全局变量。  
  12. UINT    g_uMsg;  
  13.   
  14. int _tmain(int argc, _TCHAR* argv[])  
  15. {  
  16.     int nRet = 0;  
  17.     DWORD dwTime = 0;  
  18.     STARTUPINFO si = { sizeof(si) };  
  19.     PROCESS_INFORMATION pi;  
  20.     TCHAR szGetMsgApp[64] = { 0 };  
  21.     TCHAR szPeekMsgApp[64] = { 0 };  
  22.   
  23.     g_uMsg = RegisterWindowMessage(_T("WM_MsgAppCaller"));  
  24.     //假定 GetMsgApp.exe 和 PeekMsgApp.exe 在当前目录中。  
  25.     StringCchPrintf(szGetMsgApp, _countof(szGetMsgApp), _T("GetMsgApp /%u"),  
  26.         g_uMsg);  
  27.     StringCchPrintf(szPeekMsgApp, _countof(szPeekMsgApp), _T("PeekMsgApp /%u"),  
  28.         g_uMsg);  
  29.   
  30.     dwTime = 1000;//睡眠1秒钟。  
  31.   
  32.     TestApp(szGetMsgApp, dwTime);  
  33.     TestApp(szPeekMsgApp, dwTime);  
  34.   
  35.     dwTime += 5000;//睡眠6秒钟。  
  36.   
  37.     TestApp(szGetMsgApp, dwTime);  
  38.     TestApp(szPeekMsgApp, dwTime);  
  39.   
  40.     return 0;  
  41. }  
  42.   
  43. VOID TestApp(LPTSTR pszCmdLine, DWORD dwTime)  
  44. {  
  45.     STARTUPINFO si = { sizeof(si) };  
  46.     PROCESS_INFORMATION pi;  
  47.   
  48.     if (CreateProcess(NULL, pszCmdLine, NULL, NULL, FALSE, 0, FALSE, FALSE,  
  49.         &si, &pi))  
  50.     {  
  51.         Sleep(dwTime);  
  52.         BOOL fRet = PostThreadMessage(pi.dwThreadId, g_uMsg, 0, 0);  
  53.         if (fRet == FALSE)  
  54.         {  
  55.             _tprintf(_T("ERROR: failed to call PostThreadMessage(#%u)/r/n"),  
  56.                 GetLastError());  
  57.         }  
  58.         CloseHandle(pi.hThread);  
  59.         CloseHandle(pi.hProcess);  
  60.     }  
  61.     else  
  62.     {  
  63.         _tprintf(_T("ERROR: failed to call CreateProcess(#%u)/r/n"), GetLastError());  
  64.     }  
  65. }  
  66. // end of MsgAppCaller.cpp  

 

MsgAppCaller 运行后的结果:

 

从上图结果中观察 user time:

第一次 MsgAppCaller睡眠了一秒钟,然后 GetMsgWnd 显示一共使用的 CPU 时间大约为 15 毫秒,而第二次 MsgAppCaller 睡眠了六秒钟, GetMsgWnd 显示的 CPU 使用时间还是为 15 毫秒!由此可知,当界面线程没有消息时,由于 Windows 将它置于睡眠状态,因此它的 CPU 时间与该界面线程生存时间不成正比。

第一次 MsgAppCaller睡眠了一秒钟,然后 PeekMsgWnd 显示一共使用的 CPU 时间大约为 921 毫秒,将近1秒!而第二次 MsgAppCaller 睡眠了六秒钟, PeekMsgWnd 显示的 CPU 使用时间增长到约5秒!因此,使用PeekMessage ,界面线程即使在没有消息的情况下仍然占用着 CPU 在运作。

 

关于程序设计,有几点说明下:

1、获取线程使用 CPU 的 API 是 GetThreadTimes 。由于 GetMsgApp 和 PeekMsgApp 进程中只有一个线程且就是界面线程,因此调用 GetProcessTimes 来进行性能计算,并无多大偏差。

2、两个窗口程序的窗口初始状态都是 WM_HIDE ,这是为了避免测试时,用户的干扰,而将窗口故意隐藏了,只有当窗口收到了 MsgAppCaller 发来的消息时,窗口才显示出来。

3、由于 MsgAppCaller 使用 PostThreadMessage 函数,因此窗口程序如果在窗口处理函数 WndProc 中是无法获得该消息的,对该消息的捕获需要放在消息循环中进行。

4、窗口程序中使用了一个自定义函数 Str2UInt ,该函数负责将命令行传过来的字符串形式的窗口消息解析成UINT类型。Str2UInt 及其他一些函数在文件 ZString.h 和 ZString.cpp 中进行声明和定义。

 

ZString.h:

  1. // C++ code  
  2. // ZString.h  
  3.   
  4. #pragma once  
  5. #include <Windows.h>  
  6. #include <tchar.h>  
  7.   
  8. //函数声明。  
  9. BOOL WINAPI Str2IntW(LPCWSTR pszNum, PINT pNum, BOOL bSpaceLead);  
  10. BOOL WINAPI Str2IntA(LPCSTR pszNum, PINT pNum, BOOL bSpaceLead);  
  11. BOOL WINAPI Str2UIntW(LPCWSTR pszNum, PUINT pNum, BOOL bSpaceLead);  
  12. BOOL WINAPI Str2IntA(LPCSTR pszNum, PUINT pNum, BOOL bSpaceLead);  
  13.   
  14. //条件编译。  
  15. #if defined(UNICODE) || defined(_UNICODE)  
  16. #define Str2Int Str2IntW  
  17. #else  
  18. #define Str2Int Str2IntA  
  19. #endif  
  20.   
  21. #if defined(UNICODE) || defined(_UNICODE)  
  22. #define Str2UInt Str2UIntW  
  23. #else  
  24. #define Str2UInt Str2UIntA  
  25. #endif  
  26. // end of ZString.h  

 

ZString.cpp:

  1. // C++ code  
  2. // ZString.cpp  
  3.   
  4. #include "ZString.h"  
  5.   
  6. /* 
  7.   函数签名:BOOL WINAPI Str2IntW(LPCWSTR pszNum, PINT pNum, BOOL bSpaceLead) 
  8.   函数功能:字符串转换成INT型整数的函数。 
  9.   参数: 
  10.   pszNum -- 待解析的字符串。 
  11.   pNum  -- 存储解析后的INT型整数的指针。 
  12.   bSpaceLead -- 是否有前导空格。 
  13.   返回值:成功返回 TRUE ,失败返回 FALSE 并设置错误码。 
  14. */  
  15. BOOL WINAPI Str2IntW(LPCWSTR pszNum, PINT pNum, BOOL bSpaceLead)  
  16. {  
  17.     __int64 num = 0;  
  18.     INT sign = 1;  
  19.     SIZE_T len = 0;  
  20.     BOOL bRet = FALSE;  
  21.     WCHAR ch = 0;  
  22.   
  23.     if (pszNum == NULL || pNum == NULL)  
  24.     {  
  25.         SetLastError(ERROR_INVALID_PARAMETER);  
  26.         return bRet;  
  27.     }  
  28.   
  29.     //跳过前导空格。  
  30.     if (bSpaceLead == TRUE)  
  31.     {  
  32.         while (*pszNum == _T(' ') || *pszNum == _T('/t'))  
  33.             pszNum++;  
  34.     }  
  35.   
  36.     //跳过符号位。  
  37.     ch = *pszNum;  
  38.     if (ch == _T('-'))  
  39.     {  
  40.         sign = -1;  
  41.         pszNum++;  
  42.     } else if (ch == _T('+'))  
  43.     {  
  44.         pszNum++;  
  45.     }  
  46.   
  47.     //非数字字符。  
  48.     if (*pszNum < _T('0') || *pszNum > _T('9'))  
  49.     {  
  50.         SetLastError(ERROR_BAD_FORMAT);  
  51.         return bRet;  
  52.     }  
  53.   
  54.     //第一个字符是数字字符"0"。  
  55.     if (*pszNum == _T('/0'))  
  56.     {  
  57.         if (pszNum[1] == _T('/0'))  
  58.         {  
  59.             *pNum = 0;  
  60.             return TRUE;  
  61.         }  
  62.         else  
  63.         {  
  64.             SetLastError(ERROR_BAD_FORMAT);  
  65.             return bRet;  
  66.         }  
  67.     }  
  68.   
  69.     while (TRUE)  
  70.     {  
  71.         ch = *pszNum++;  
  72.   
  73.         //数字字符。  
  74.         if (ch >= _T('0') && ch <= _T('9'))  
  75.         {  
  76.             len++;  
  77.   
  78.             //int型整数最多十位。  
  79.             if (len > 10)  
  80.             {  
  81.                 SetLastError(ERROR_INVALID_DATA);  
  82.                 break;  
  83.             }  
  84.   
  85.             num = num * 10 + (ch - _T('0'));  
  86.   
  87.             continue;  
  88.         }  
  89.         //字符串终止符。  
  90.         else if (ch == _T('/0'))  
  91.         {  
  92.             num = sign * num;  
  93.             if (num <= INT_MAX && num >= INT_MIN)  
  94.             {  
  95.                 *pNum = (INT)num;  
  96.                 bRet = TRUE;  
  97.                 break;  
  98.             }  
  99.             //溢出。  
  100.             else  
  101.             {  
  102.                 SetLastError(ERROR_INVALID_DATA);  
  103.                 break;  
  104.             }  
  105.         }  
  106.         //非数字字符。  
  107.         else  
  108.         {  
  109.             SetLastError(ERROR_BAD_FORMAT);  
  110.             break;  
  111.         }  
  112.     }  
  113.   
  114.     return bRet;  
  115. }  
  116.   
  117. /* 
  118.   函数签名:BOOL WINAPI Str2IntA(LPCSTR pszNum, PINT pNum, BOOL bSpaceLead) 
  119.   函数功能:Str2IntW 的 Ansi版本,见 Str2IntW 。 
  120. */  
  121. BOOL WINAPI Str2IntA(LPCSTR pszNum, PINT pNum, BOOL bSpaceLead)  
  122. {  
  123.     //int类型的数字最多是10位,大于10的最小2次幂是16。  
  124.     WCHAR szNum[16] = { 0 };  
  125.     INT nRet = 0;  
  126.     SIZE_T cbLen = 0;  
  127.   
  128.     if (pszNum == NULL || pNum == NULL)  
  129.     {  
  130.         SetLastError(ERROR_INVALID_PARAMETER);  
  131.         return FALSE;  
  132.     }  
  133.   
  134.     //跳过前导空格。  
  135.     if (bSpaceLead == TRUE)  
  136.     {  
  137.         while (*pszNum == _T(' ') || *pszNum == _T('/t'))  
  138.             pszNum++;  
  139.     }  
  140.   
  141.     cbLen = strnlen_s(pszNum, 16);  
  142.   
  143.     //过滤较长的恶意字符串。  
  144.     if (cbLen == 16)  
  145.     {  
  146.         SetLastError(ERROR_BAD_FORMAT);  
  147.         return FALSE;  
  148.     }  
  149.   
  150.     nRet = MultiByteToWideChar(CP_ACP, 0, pszNum, -1, szNum, 16);  
  151.     if (nRet == 0)  
  152.     {  
  153.         return FALSE;  
  154.     }  
  155.   
  156.     return Str2IntW(szNum, pNum, bSpaceLead);  
  157. }  
  158.   
  159. /* 
  160.   函数签名:BOOL WINAPI Str2UIntW(LPCWSTR pszNum, PUINT pNum, BOOL bSpaceLead) 
  161.   函数功能:字符串转换成UINT型整数的函数。 
  162.   参数: 
  163.   pszNum -- 待解析的字符串。 
  164.   pNum  -- 存储解析后的INT型整数的指针。 
  165.   bSpaceLead -- 是否有前导空格。 
  166.   返回值:成功返回 TRUE,失败返回 FALSE 并设置错误码。 
  167. */  
  168. BOOL WINAPI Str2UIntW(LPCWSTR pszNum, PUINT pNum, BOOL bSpaceLead)  
  169. {  
  170.     __int64 num = 0;  
  171.     SIZE_T len = 0;  
  172.     BOOL bRet = FALSE;  
  173.     WCHAR ch = 0;  
  174.   
  175.     if (pszNum == NULL || pNum == NULL)  
  176.     {  
  177.         SetLastError(ERROR_INVALID_PARAMETER);  
  178.         return bRet;  
  179.     }  
  180.   
  181.     //跳过前导空格。  
  182.     if (bSpaceLead == TRUE)  
  183.     {  
  184.         while (*pszNum == _T(' ') || *pszNum == _T('/t'))  
  185.             pszNum++;  
  186.     }  
  187.   
  188.     //非数字字符。  
  189.     if (*pszNum < _T('0') || *pszNum > _T('9'))  
  190.     {