VNCHOOKS是一个设置全局钩子的动态链接库。

先看入口函数

view plain
  1. BOOL WINAPI DllMain (HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
  2. {
  3. // Find out why we're being called
  4. switch (ul_reason_for_call)
  5. {
  6. case DLL_PROCESS_ATTACH:
  7. _RPT0(_CRT_WARN, "vncHooks : Hook DLL loaded/n");
  8. // Save the instance handle
  9. hInstance = (HINSTANCE)hInst;
  10. // Call the initialisation function
  11. appHookedOK = InitInstance();
  12. // ALWAYS return TRUE to avoid breaking unhookable applications!!!
  13. return TRUE;
  14. case DLL_PROCESS_DETACH:
  15. _RPT0(_CRT_WARN, "vncHooks : Hook DLL unloaded/n");
  16. // Call the exit function
  17. // If the app failed to hook OK, ExitInstance will still operate OK (hopefully...)
  18. ExitInstance();
  19. return TRUE;
  20. default:
  21. return TRUE;
  22. }
  23. }

BOOL WINAPI DllMain (HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)

当VNCHOOKS.dll被加载的时候,首先惯例保留自己的句柄hInst,然后进入

BOOL InitInstance() 函数

view plain
  1. BOOL InitInstance()
  2. {
  3. // Create the global atoms
  4. VNC_POPUPSELN_ATOM = GlobalAddAtom(VNC_POPUPSELN_ATOMNAME);
  5. // 请求一个原子变量
  6. if (VNC_POPUPSELN_ATOM == NULL)
  7. return FALSE;
  8. // Get the module name
  9. char proc_name[_MAX_PATH];
  10. DWORD size;
  11. // Attempt to get the program/module name
  12. if ((size = GetModuleFileName(
  13. GetModuleHandle(NULL),
  14. //If this parameter is NULL, GetModuleHandle returns a handle to the file used to create the calling process (.exe file).
  15. //返回当前进程可执行文件的句柄
  16. (char *) &proc_name,
  17. _MAX_PATH
  18. )) == 0)
  19. return FALSE;
  20. // Get the default system key for the module
  21. //  HKEY_LOCAL_MACHINE/Software/ORL/VNCHooks/Application_Prefs/excute.exe(或者带文件全路径)
  22. //ORL是啥缩写。。
  23. hModuleKey = GetModuleKey(HKEY_LOCAL_MACHINE, proc_name, false, false);
  24. if (hModuleKey != NULL) {
  25. _RPT0(_CRT_WARN, "vncHooks : loading machine prefs/n");
  26. ReadSettings();
  27. RegCloseKey(hModuleKey);
  28. hModuleKey = NULL;
  29. }
  30. // Get the key for the module
  31. hModuleKey = GetModuleKey(HKEY_CURRENT_USER, proc_name, false, false);
  32. //  HKEY_CURRENT_USER/Software/ORL/VNCHooks/Application_Prefs/excute.exe(或者带文件全路径)
  33. // 这样理解,如果某个可执行文件会调用本dll来钩住系统消息,那么可以通过在注册表修改相应配置来控制钩住消息的处理。
  34. if (hModuleKey != NULL) {
  35. _RPT0(_CRT_WARN, "vncHooks : loading user prefs/n");
  36. ReadSettings();
  37. //通常我们可以在进程中通过WriteProfileString这类函数来保存值。
  38. //下次可以通过GetProfileString和::GetProfileInt这类函数来读取值。
  39. // UINT GetProfileInt(
  40. //LPCTSTR lpAppName,
  41. //  LPCTSTR lpKeyName,
  42. //  INT nDefault
  43. //  );
  44. //MSDN中注意(Note):  This function is provided only for compatibility with 16-bit Windows-based applications. Applications should store initialization information in the registry.
  45. //意思是这个函数是为了16位系统的程序准备的,因为在Dos和Win3.x的时代,大部分的应用程序都是采用了 ini 文件(初始化文件)来保存一些配置信息,如设置路径,环境变量等
  46. //在一个变化的环境中,在应用程序安装到系统中后,每个人都会更改.ini文件。而没有人去删除该信息,一个.ini文件的最大尺寸是64KB。
  47. //所以微软建议32位进程直接写入注册表,而不是调用这个函数。
  48. //只有在Windows Server 2003 and Windows XP/2000/NT系统下,对于WIN.INI部分键会在注册表中作一个映射,对于这些内容的访问还是可以调用这个函数的。
  49. //被映射的键在HKEY_LOCAL_MACHINE/Software/Microsoft/Windows NT/CurrentVersion/IniFileMapping/win.ini下列了出来。
  50. //我们看到key的name是WIN.INI键值,value是对应的注册表路径,但路径前面有些符号。
  51. //!表示WriteProfileString会在注册表和WIN.INI分别写入。
  52. //#表示PC上已经装了Windows 3.1,再安装Windows NT后,当一个用户第一次登陆的时候,注册表会把值写入Windows 3.1 .ini
  53. //@表示如果GetProfile注册表中未找到,不会再在WIN.INI中继续查找
  54. //USR:表示路径替换为HKEY_CURRENT_USER/,效果相当于C语言中 #define USR: HKEY_CURRENT_USER/
  55. //SYS:表示路径替换为HKEY_LOCAL_MACHINE/SOFTWARE/
  56. //以上是对MSDN的翻译,并未验证。
  57. //WriteProfileString同样也是先查看IniFileMapping,并不推荐使用。
  58. //所以在WINVNC是直接写入注册表的。
  59. RegCloseKey(hModuleKey);
  60. hModuleKey = NULL;
  61. }
  62. return TRUE;
  63. }

BOOL InitInstance()

首先调用

ATOM GlobalAddAtom(          LPCTSTR lpString

);

申请了一个原子变量,WINDOWS系统维护了一张原子表,可以这么理解,保存了一个字符串数组,每个字符串不能超过127字符。每个字符串对应一个唯一的ID(16位)。

最常用于Dynamic Data Exchange、防止2次启动(在WINVNC中是用命名的MUTEX来防止二次启动的)等。原子变量是采用计数的,每次调用GlobalAddAtom会让计数加1,GlobalDeleteAtom会让计数减1,

直到计数为0系统才会删除。原子在这里的应用将在稍后看到。

接下来是从注册表读取配置。

7个配置项

view plain
  1. BOOL prf_use_GetUpdateRect = FALSE;       // Use the GetUpdateRect paint mode
  2. BOOL prf_use_Timer = TRUE;                          // Use Timer events to trigger updates
  3. BOOL prf_use_KeyPress = TRUE;                       // Use keyboard events
  4. BOOL prf_use_LButtonUp = TRUE;                  // Use left mouse button up events
  5. BOOL prf_use_MButtonUp = FALSE;                 // Use middle mouse button up events
  6. BOOL prf_use_RButtonUp = FALSE;                 // Use right mouse button up events
  7. BOOL prf_use_Deferral = TRUE;                       // Use deferred updates
view plain
  1. HKEY OpenKey(HKEY basekey, const char* path[], bool writable, bool create) {
  2. HKEY key = basekey;
  3. DWORD flags = KEY_READ;
  4. if (writable) flags |= KEY_WRITE;
  5. if (create) flags |= KEY_CREATE_SUB_KEY;
  6. unsigned int i = 0;
  7. while (path[i]) {
  8. HKEY newkey;
  9. DWORD result, dw;
  10. if (create) {
  11. _RPT2(_CRT_WARN, "vncHooks : creating %s from %x/n", path[i], key);
  12. result = RegCreateKeyEx(key, path[i], 0, REG_NONE,
  13. REG_OPTION_NON_VOLATILE, flags, NULL,
  14. &newkey, &dw);
  15. } else {
  16. _RPT2(_CRT_WARN, "vncHooks : opening %s from %x/n", path[i], key);
  17. result = RegOpenKeyEx(key, path[i], 0, flags, &newkey);
  18. }
  19. if (key && (key != basekey)) RegCloseKey(key);
  20. key = newkey;
  21. if (result != ERROR_SUCCESS) {
  22. _RPT2(_CRT_WARN, "vncHooks : failed to open %s(%lu)/n", path[i], result);
  23. return NULL;
  24. } else {
  25. _RPT2(_CRT_WARN, "vncHooks : opened %s (%x)/n", path[i], key);
  26. }
  27. i++;
  28. }
  29. return key;
  30. }

HKEY OpenKey(HKEY basekey, const char* path[], bool writable, bool create)

注册表创(读)键函数。

HKEY basekey,基键。

const char* path[],保存的是基键下的路径,每个元素保存一个键值。

bool writable,键的可写属性。

bool create,true表示创建该键(调用API RegCreateKeyEx),false表示打开该键(调用RegOpenKeyEx)。

该函数会依次读取path[]参数的元素来创键(或者读取键)。

view plain
  1. HKEY GetModuleKey(HKEY basekey, const char* proc_name, bool writable, bool create) {
  2. // Work out the registry key to save this under
  3. if (!sModulePrefs) {
  4. sModulePrefs = (char *) malloc(strlen(proc_name) + 1);
  5. if (sModulePrefs == NULL)
  6. return FALSE;
  7. strcpy(sModulePrefs, proc_name);
  8. }
  9. // Check whether the library's entry exists!
  10. //static const TCHAR szSoftware[] = "Software";
  11. //static const TCHAR szCompany[] = "ORL";
  12. //static const TCHAR szProfile[] = "VNCHooks";
  13. const char* appPath[] = {szSoftware, szCompany, szProfile, 0};
  14. HKEY appKey = OpenKey(basekey, appPath, writable, create);
  15. if (!appKey)
  16. return NULL;
  17. // Attempt to open the registry section for the application
  18. //const char sPrefSegment[] = "Application_Prefs";
  19. //char *sModulePrefs = NULL;                          // Name of the module that created us
  20. const char* modPath[] = {sPrefSegment, sModulePrefs, 0};
  21. HKEY modKey = OpenKey(appKey, modPath, writable, false);
  22. // Software/ORL/VNCHooks/Application_Prefs/...xxx.exe(...表示全路径)
  23. if (!modKey) {
  24. // Cut off the app directory and just use the name
  25. char *file_name = NameFromPath(proc_name);
  26. if (!file_name)
  27. {
  28. RegCloseKey(appKey);
  29. return NULL;
  30. }
  31. // Adjust the moduleprefs name
  32. strcpy(sModulePrefs, file_name);
  33. free(file_name);
  34. // Now get the module key again
  35. //const char sPrefSegment[] = "Application_Prefs";
  36. const char* modPath2[] = {sPrefSegment, sModulePrefs, 0};
  37. modKey = OpenKey(appKey, modPath2, writable, create);
  38. // Software/ORL/VNCHooks/Application_Prefs/vnchooks.dll(如果注册表中没找到全路径,则查找文件名)
  39. }
  40. RegCloseKey(appKey);
  41. return modKey;
  42. }

HKEY GetModuleKey(HKEY basekey, const char* proc_name, bool writable, bool create)

proc_name传入的是全路径的可执行文件名称,函数会尝试全路径名称的读取,如果失败,则会调用NameFromPath去掉路径,再读取或者创建。

view plain
  1. static const TCHAR szSoftware[] = "Software";//注册表原有,系统用来保存已安装的软件
  2. static const TCHAR szCompany[] = "ORL";//需创建
  3. static const TCHAR szProfile[] = "VNCHooks";//需创建
  4. const char sPrefSegment[] = "Application_Prefs";//需创建
  5. char *sModulePrefs = NULL;  //保存可执行文件名

这些静态变量保存了键名,构成了注册表的路径。

GetModuleHandle(NULL),注释说到,参数为NULL,GetModuleHandle返回的是创造进程的磁盘文件(*.exe)的句柄。

这里获取了可执行文件的全路径。

char * NameFromPath(const char *path)

截取path最后一个'/'符号后的字符串。

注意这里用了strdup(从堆分配内存拷贝),所以记得调用free来释放。

BOOL InitInstance()中会尝试在HKEY_LOCAL_MACHINE和HKEY_CURRENT_USER两个主键下读取键,然后通过

view plain
  1. void ReadSettings() {
  2. // Read in the prefs
  3. prf_use_GetUpdateRect = GetProfileInt(
  4. "use_GetUpdateRect",
  5. TRUE
  6. );
  7. //注意这里的GetProfileInt重载了系统的API,在注册表指定路径读取
  8. prf_use_Timer = GetProfileInt(
  9. "use_Timer",
  10. FALSE
  11. );
  12. prf_use_KeyPress = GetProfileInt(
  13. "use_KeyPress",
  14. TRUE
  15. );
  16. prf_use_LButtonUp = GetProfileInt(
  17. "use_LButtonUp",
  18. TRUE
  19. );
  20. prf_use_MButtonUp = GetProfileInt(
  21. "use_MButtonUp",
  22. TRUE
  23. );
  24. prf_use_RButtonUp = GetProfileInt(
  25. "use_RButtonUp",
  26. TRUE
  27. );
  28. prf_use_Deferral = GetProfileInt(
  29. "use_Deferral",
  30. TRUE
  31. );
  32. }

void ReadSettings()来读取配置,这样每一个可执行文件(确切的说应该是名字)都在注册表中拥有自己的键,保存配置。我们可以主动地在注册表中写入全路径的可执行文件的配置,那么在这个路径中的可执行文件调用本库的时候就会读取指定配置了,否则就是所有同名的可执行文件启动的进程共用1个配置。

进程卸载本库的时候(DLL_PROCESS_DETACH)会调用

view plain
  1. BOOL ExitInstance()
  2. {
  3. // Free the created atoms
  4. if (VNC_POPUPSELN_ATOM != NULL)
  5. {
  6. // GlobalDeleteAtom(VNC_POPUPSELN_ATOM);
  7. VNC_POPUPSELN_ATOM = NULL;
  8. }
  9. // Write the module settings to disk
  10. if (sModulePrefs != NULL)
  11. {
  12. // Get the module name
  13. char proc_name[_MAX_PATH];
  14. DWORD size;
  15. // Attempt to get the program/module name
  16. if ((size = GetModuleFileName(
  17. GetModuleHandle(NULL),
  18. (char *) &proc_name,
  19. _MAX_PATH
  20. )) == 0)
  21. return FALSE;
  22. // Get the key for the module
  23. _RPT0(_CRT_WARN, "vncHooks : locating user prefs/n");
  24. hModuleKey = GetModuleKey(HKEY_CURRENT_USER, proc_name, true, true);
  25. if (hModuleKey == NULL)
  26. return FALSE;
  27. _RPT0(_CRT_WARN, "vncHooks : writing user prefs/n");
  28. //在进程释放本库调用时BOOL ExitInstance() 就通过RegSetValueEx函数保存了设置。
  29. //注意这里的GetModuleKey第四个参数为true,表示如果注册表未找到键,则创建。
  30. WriteProfileInt(
  31. "use_GetUpdateRect",
  32. prf_use_GetUpdateRect
  33. );
  34. WriteProfileInt(
  35. "use_Timer",
  36. prf_use_Timer
  37. );
  38. WriteProfileInt(
  39. "use_KeyPress",
  40. prf_use_KeyPress
  41. );
  42. WriteProfileInt(
  43. "use_LButtonUp",
  44. prf_use_LButtonUp
  45. );
  46. WriteProfileInt(
  47. "use_MButtonUp",
  48. prf_use_MButtonUp
  49. );
  50. WriteProfileInt(
  51. "use_RButtonUp",
  52. prf_use_RButtonUp
  53. );
  54. WriteProfileInt(
  55. "use_Deferral",
  56. prf_use_Deferral
  57. );
  58. free(sModulePrefs);
  59. sModulePrefs = NULL;
  60. }
  61. // Close the registry key for this module
  62. if (hModuleKey != NULL) {
  63. RegCloseKey(hModuleKey);
  64. hModuleKey = NULL;
  65. }
  66. return TRUE;
  67. }

BOOL ExitInstance()

它是依次释放资源,并且保存配置到注册表。

view plain
  1. #pragma data_seg(".SharedData")
  2. DWORD vnc_thread_id = 0;
  3. UINT UpdateRectMessage = 0;
  4. UINT CopyRectMessage = 0;
  5. UINT MouseMoveMessage = 0;
  6. HHOOK hCallWndHook = NULL;                          // Handle to the CallWnd hook
  7. HHOOK hGetMsgHook = NULL;                           // Handle to the GetMsg hook
  8. HHOOK hDialogMsgHook = NULL;                        // Handle to the DialogMsg hook
  9. HHOOK hLLKeyboardHook = NULL;                       // Handle to LowLevel kbd hook
  10. HHOOK hLLMouseHook = NULL;                          // Handle to LowLevel mouse hook
  11. #pragma data_seg( )

这里用了一个共享段,用于限制整个系统只有一个钩子线程,既一个线程来接收更新消息。所以该线程ID和钩子句柄,另外还保存3个钩子线程定义的消息ID。

view plain
  1. DllExport BOOL SetHooks(DWORD thread_id, UINT UpdateMsg, UINT CopyMsg, UINT MouseMsg)
  2. {
  3. // Don't add the hook if the thread id is NULL
  4. if (!thread_id)
  5. return FALSE;
  6. // Don't add a hook if there is already one added
  7. if (vnc_thread_id)
  8. return FALSE;
  9. //用于限制整个系统只有一个线程来接收更新消息。
  10. // Add the CallWnd hook
  11. hCallWndHook = SetWindowsHookEx(
  12. WH_CALLWNDPROC,                 // Hook in before msg reaches app
  13. (HOOKPROC) CallWndProc,         // Hook procedure
  14. hInstance,                      // This DLL instance
  15. 0L                              // Hook in to all apps
  16. //                  GetCurrentThreadId()            // DEBUG : HOOK ONLY WinVNC
  17. );
  18. // Add the GetMessage hook
  19. hGetMsgHook = SetWindowsHookEx(
  20. WH_GETMESSAGE,                  // Hook in before msg reaches app
  21. (HOOKPROC) GetMessageProc,          // Hook procedure
  22. hInstance,                      // This DLL instance
  23. 0L                              // Hook in to all apps
  24. //                  GetCurrentThreadId()            // DEBUG : HOOK ONLY WinVNC
  25. );
  26. // Add the GetMessage hook
  27. hDialogMsgHook = SetWindowsHookEx(
  28. WH_SYSMSGFILTER,                // Hook in dialogs, menus and scrollbars
  29. (HOOKPROC) DialogMessageProc,   // Hook procedure
  30. hInstance,                      // This DLL instance
  31. 0L                              // Hook in to all apps
  32. );
  33. // Check that it worked
  34. if ((hCallWndHook != NULL) && (hGetMsgHook != NULL) && (hDialogMsgHook != NULL))
  35. {
  36. vnc_thread_id = thread_id;          // Save the WinVNC thread id
  37. UpdateRectMessage = UpdateMsg;      // Save the message ID to use for rectangle updates
  38. CopyRectMessage = CopyMsg;          // Save the message ID to use for copyrect
  39. MouseMoveMessage = MouseMsg;        // Save the message ID to send when mouse moves
  40. HookMaster = TRUE;                  // Set the HookMaster flag for this instance
  41. return TRUE;
  42. }
  43. else
  44. {
  45. // Stop the keyboard hook
  46. SetKeyboardFilterHook(FALSE);
  47. SetMouseFilterHook(FALSE);
  48. // Kill the main hooks
  49. if (hCallWndHook != NULL)
  50. UnhookWindowsHookEx(hCallWndHook);
  51. if (hGetMsgHook != NULL)
  52. UnhookWindowsHookEx(hGetMsgHook);
  53. if (hDialogMsgHook != NULL)
  54. UnhookWindowsHookEx(hDialogMsgHook);
  55. hCallWndHook = NULL;
  56. hGetMsgHook = NULL;
  57. hDialogMsgHook = NULL;
  58. }
  59. // The hook failed, so return an error code
  60. return FALSE;
  61. }

DllExport BOOL SetHooks(DWORD thread_id, UINT UpdateMsg, UINT CopyMsg, UINT MouseMsg)

这个函数安装了3种类型的钩子

WH_CALLWNDPROC 监视系统或者用户通过调用SendMessage向窗口发送的消息,既创建该窗口的线程的send-message queue。在消息被窗口过程处理前响应。

WH_GETMESSAGE 监视的是posted-message queue,键盘消息与鼠标消息等。

WH_SYSMSGFILTER 监视的所有进程中即将由菜单、滚动条、消息框、对话框处理的消息,并且在用户按下了ALT+TAB 或者ALT+ESC组合键后,检测何时一个不同的窗口将被激活。

三个钩子回调函数都无一例外地调用了

view plain
  1. inline BOOL HookHandle(UINT MessageId, HWND hWnd, WPARAM wParam, LPARAM lParam)
  2. {
  3. // HANDLE DEFERRED UPDATES
  4. // Is this a deferred-update message?
  5. if (MessageId == VNC_DEFERRED_UPDATE)
  6. {
  7. // NOTE : NEVER use the SendDeferred- routines to send updates
  8. //      from here, or you'll get an infinite loop....!
  9. // NB : The format of DEFERRED_UPDATE matches that of UpdateRectMessage,
  10. //      so just send the exact same message data to WinVNC
  11. if (!PostThreadMessage(
  12. vnc_thread_id,
  13. UpdateRectMessage,
  14. wParam,
  15. lParam
  16. ))
  17. vnc_thread_id = 0;
  18. return FALSE;
  19. }
  20. //进程主动发出的更新消息
  21. // *** Could use WM_COPYDATA to send data to WinVNC
  22. /*
  23. if (GetClassLong(hWnd, GCW_ATOM) == 32768)
  24. {
  25. _RPT4(_CRT_WARN, "DBG : popup menu message (hwnd=%d, msg=%d, l=%d, w=%d)/n",
  26. hWnd, MessageId, lParam, wParam);
  27. }
  28. */
  29. // UPDATE-TRIGGERING MESSAGES
  30. // Do something dependent upon message type
  31. switch (MessageId)
  32. {
  33. // Messages indicating only a border repaint.
  34. case WM_NCPAINT:
  35. case WM_NCACTIVATE:
  36. SendDeferredBorderRect(hWnd);
  37. //窗口边界更新
  38. old_cursor = NULL;
  39. break;
  40. // Messages indicating a client area repaint
  41. case WM_CHAR:
  42. case WM_KEYUP:                          // Handle key-presses
  43. if (prf_use_KeyPress)
  44. SendDeferredWindowRect(hWnd);
  45. //键盘输入,焦点窗口全部更新
  46. break;
  47. case WM_LBUTTONUP:                      // Handle LMB clicks
  48. if (prf_use_LButtonUp)
  49. SendDeferredWindowRect(hWnd);
  50. //鼠标左键点击,焦点窗口全部更新
  51. break;
  52. case WM_MBUTTONUP:                      // Handle MMB clicks
  53. if (prf_use_MButtonUp)
  54. SendDeferredWindowRect(hWnd);
  55. //鼠标中键点击,焦点窗口全部更新
  56. break;
  57. case WM_RBUTTONUP:                      // Handle RMB clicks
  58. if (prf_use_RButtonUp)
  59. SendDeferredWindowRect(hWnd);
  60. //鼠标右键点击,焦点窗口全部更新
  61. break;
  62. case WM_MOUSEWHEEL:           // Handle mousewheel events
  63. SendDeferredWindowRect(hWnd);
  64. //鼠标滚轮,焦点窗口全部更新
  65. break;
  66. case WM_TIMER:
  67. if (prf_use_Timer)
  68. SendDeferredWindowRect(hWnd);
  69. //定时器,焦点窗口全部更新
  70. break;
  71. case WM_HSCROLL:
  72. case WM_VSCROLL:
  73. if (((int) LOWORD(wParam) == SB_THUMBTRACK) || ((int) LOWORD(wParam) == SB_ENDSCROLL))
  74. //SB_THUMBTRACK             滚动条滑块被拖动
  75. //SB_THUMBPOSTION         拖动后滚动条滑块被释放
  76. //这是第一种情况,滚动条即时跟新窗口
  77. //在 SB_ENDSCROLL 滚动条通知代码,它指示用户已滚动之后, 释放鼠标按钮。
  78. // 这是第二种情况, 释放鼠标按钮后再更新窗口
  79. SendDeferredWindowRect(hWnd);
  80. //滚动条,焦点窗口全部更新
  81. break;
  82. case 485:  // HACK to handle popup menus
  83. //The 0x1e5 message is defined in private microsoft headers as MN_SELECTITEM.
  84. //  0x01e5 (undocumented) - requests the menu to redraw the item supplied in the message wParam using an internal method (Windows 9x, 2K, XP)
  85. //  This last message (0x01e5) is the most interesting and the most crucial discovery I made on this project.
  86. //Windows sends it to the menu every time you twitch the mouse inside or outside the menu, so that the menu can redraw the specified item.
  87. //At first it might seem like a gross inefficiency since no other window controls behave that way, but if you've got menu animation turned on you'll see why its necessary.
  88. //当鼠标在菜单选项边界移动,会触发。
  89. {
  90. // Get the old popup menu selection value
  91. HANDLE prop = GetProp(hWnd, (LPCTSTR) MAKELONG(VNC_POPUPSELN_ATOM, 0));
  92. //查找窗口资源列表中是否有"VNCHooks.PopUpMenu.Selected"
  93. //GetProp可以通过原子来查找资源句柄
  94. if (prop != (HANDLE) wParam)
  95. //wparam是menu的item偏移量
  96. //保证WINDOW中"VNCHooks.PopUpMenu.Selected"资源永远对应被选择的ITEM。
  97. //如果menu的item选择没有变化的时候是不需要更新窗口的,
  98. //一旦item发生改变,就会把item与"VNCHooks.PopUpMenu.Selected"资源保存的item相比较,如果不同则更新。
  99. {
  100. // It did, so update the menu & the selection value
  101. SendDeferredWindowRect(hWnd);
  102. SetProp(hWnd,
  103. (LPCTSTR) MAKELONG(VNC_POPUPSELN_ATOM, 0),
  104. (HANDLE) wParam);
  105. }
  106. }
  107. break;
  108. // Messages indicating a full window update
  109. case WM_SYSCOLORCHANGE:
  110. case WM_PALETTECHANGED:
  111. case WM_SETTEXT:
  112. case WM_ENABLE:
  113. case BM_SETCHECK:
  114. case BM_SETSTATE:
  115. case EM_SETSEL:
  116. //case WM_MENUSELECT:
  117. SendDeferredWindowRect(hWnd);
  118. break;
  119. // Messages indicating that an area of the window needs updating
  120. // Uses GetUpdateRect to find out which
  121. case WM_PAINT:
  122. if (prf_use_GetUpdateRect)
  123. {
  124. HRGN region;
  125. region = CreateRectRgn(0, 0, 0, 0);
  126. // Get the affected region
  127. if (GetUpdateRgn(hWnd, region, FALSE) != ERROR)
  128. {
  129. int buffsize;
  130. UINT x;
  131. RGNDATA *buff;
  132. POINT TopLeft;
  133. // Get the top-left point of the client area
  134. TopLeft.x = 0;
  135. TopLeft.y = 0;
  136. if (!ClientToScreen(hWnd, &TopLeft))
  137. break;
  138. // Get the size of buffer required
  139. buffsize = GetRegionData(region, 0, 0);
  140. if (buffsize != 0)
  141. {
  142. buff = (RGNDATA *) new BYTE [buffsize];
  143. if (buff == NULL)
  144. break;
  145. // Now get the region data
  146. if(GetRegionData(region, buffsize, buff))
  147. {
  148. for (x=0; x<(buff->rdh.nCount); x++)
  149. //rdh.nCount 指构成区域的矩形数目
  150. {
  151. // Obtain the rectangles from the list
  152. RECT *urect = (RECT *) (((BYTE *) buff) + sizeof(RGNDATAHEADER) + (x * sizeof(RECT)));
  153. SendDeferredUpdateRect(
  154. hWnd,
  155. (SHORT) (TopLeft.x + urect->left),
  156. (SHORT) (TopLeft.y + urect->top),
  157. (SHORT) (TopLeft.x + urect->right),
  158. (SHORT) (TopLeft.y + urect->bottom)
  159. );
  160. }
  161. }
  162. delete [] buff;
  163. }
  164. }
  165. // Now free the region
  166. if (region != NULL)
  167. DeleteObject(region);
  168. }
  169. else
  170. SendDeferredWindowRect(hWnd);
  171. break;
  172. // Messages indicating full repaint of this and a different window
  173. // Send the new position of the window
  174. case WM_WINDOWPOSCHANGING:
  175. if (IsWindowVisible(hWnd))
  176. SendWindowRect(hWnd);
  177. break;
  178. case WM_WINDOWPOSCHANGED:
  179. if (IsWindowVisible(hWnd))
  180. SendDeferredWindowRect(hWnd);
  181. break;
  182. // WinVNC also wants to know about mouse movement
  183. case WM_NCMOUSEMOVE:
  184. case WM_MOUSEMOVE:
  185. // Inform WinVNC that the mouse has moved and pass it the current cursor handle
  186. {
  187. ULONG new_cursor = (ULONG)GetCursor();
  188. if (new_cursor != old_cursor) {
  189. if (!PostThreadMessage(
  190. vnc_thread_id,
  191. MouseMoveMessage,
  192. (ULONG) new_cursor, 0))
  193. vnc_thread_id = 0;
  194. old_cursor=new_cursor;
  195. }
  196. }
  197. break;
  198. // VNCHOOKS PROPERTIES HANDLING WINDOWS
  199. case WM_DESTROY:
  200. RemoveProp(hWnd, (LPCTSTR) MAKELONG(VNC_POPUPSELN_ATOM, 0));
  201. break;
  202. }
  203. return TRUE;
  204. }

const UINT VNC_DEFERRED_UPDATE = RegisterWindowMessage("VNCHooks.Deferred.UpdateMessage");

这里注册了一个系统级别的消息,当prf_use_Deferral为真,监听到的窗口消息不会发送给处理线程,而是发送一个VNC_DEFERRED_UPDATE消息到该窗口的posted-message queue。当下次窗口处理该消息的时候又会被我们钩住。

WINVNC源码分析(四)-vnchooks相关推荐

  1. ABP源码分析四十七:ABP中的异常处理

    ABP源码分析四十七:ABP中的异常处理 参考文章: (1)ABP源码分析四十七:ABP中的异常处理 (2)https://www.cnblogs.com/1zhk/p/5538983.html (3 ...

  2. 【投屏】Scrcpy源码分析四(最终章 - Server篇)

    Scrcpy源码分析系列 [投屏]Scrcpy源码分析一(编译篇) [投屏]Scrcpy源码分析二(Client篇-连接阶段) [投屏]Scrcpy源码分析三(Client篇-投屏阶段) [投屏]Sc ...

  3. gSOAP 源码分析(四)

    gSOAP 源码分析(四) 2012-6-2 邵盛松 前言 本文主要说明gSOAP中对Client的认证分析 gSOAP中包含了HTTP基本认证,NTLM认证等,还可以自定义SOAP Heard实现认 ...

  4. Spring 源码分析(四) ——MVC(二)概述

    随时随地技术实战干货,获取项目源码.学习资料,请关注源代码社区公众号(ydmsq666) from:Spring 源码分析(四) --MVC(二)概述 - 水门-kay的个人页面 - OSCHINA ...

  5. 【转】ABP源码分析四十七:ABP中的异常处理

    ABP 中异常处理的思路是很清晰的.一共五种类型的异常类. AbpInitializationException用于封装ABP初始化过程中出现的异常,只要抛出AbpInitializationExce ...

  6. 【转】ABP源码分析四:Configuration

    核心模块的配置 Configuration是ABP中设计比较巧妙的地方.其通过AbpStartupConfiguration,Castle的依赖注入,Dictionary对象和扩展方法很巧妙的实现了配 ...

  7. 谷歌chrome浏览器的源码分析(四)

    上一次说到需要调用这个OpenURLFromTab函数,那么这个函数是做什么的呢?从名称上可能猜到它是打开网页,但是是从目前TAB页里打开呢?还是新建一个?或者使用每个TAB页一个进程呢?这些疑惑,只 ...

  8. 【转】ABP源码分析四十六:ABP ZERO中的Ldap模块

    通过AD作为用户认证的数据源.整个管理用户认证逻辑就在LdapAuthenticationSource类中实现. LdapSettingProvider:定义LDAP的setting和提供Defaut ...

  9. 【转】ABP源码分析四十五:ABP ZERO中的EntityFramework模块

    AbpZeroDbContext:配置ABP.Zero中定义的entity的Dbset EntityFrameworkModelBuilderExtensions:给PrimitiveProperty ...

  10. 【转】ABP源码分析四十三:ZERO的本地化

    ABP Zero模块扩展了ABP基础框架中的本地化功能,实现了通过数据库对本地化功能进行管理.其通过数据库保存本地化语言及其资源. ApplicationLanguage:代表本地化语言的实体类.一种 ...

最新文章

  1. hadoop fs命令无法使用_Hadoop从入门到入土(三)HDFS集群简单维护及JAVA客户端连接HDFS...
  2. 字符集与编码(九)——GB2312,GBK,GB18030
  3. 客户网站被黑导致CDN加速后打开域名就提示域名纠错
  4. (转) 如何在JavaScript与ActiveX之间传递数据1
  5. Android 屏幕旋转的多种状态
  6. 解决Bootstrap模态窗口Modal中使用Kindeditor或UEditor编辑器 点击关闭弹窗 出现第二次无法加载的问题
  7. Android音视频编辑器架构图与分析
  8. linux基础学习思维导图及文档(17万字)
  9. phpstudy搭建渗透测试环境
  10. 《开端》里的循环,人工智能每天都在进行
  11. web前端学习13-19(HTML常用标签)
  12. SSH远程登录与控制
  13. 网页版Photoshop,
  14. SpringCloud学习笔记(十四)Zuul网关
  15. Notion:笔记协同工具,使用教程
  16. 企业公众号怎么做内容?这四个阶段要做好
  17. 为什么外链建设对于谷歌SEO至关重要?
  18. 聊城大学计算机学院许丽莉,计算机学院
  19. 揭秘:HR的嘴骗人的鬼!这三个陷阱要小心!
  20. 创业思维 - MikeCRM的故事

热门文章

  1. 解决android.support.multide... keeps stopping问题
  2. 2Wire_2700hg系列无线路由器功率增大方法!
  3. php域名绑定模块,ThinkPHP5.1 域名绑定模块
  4. 超好用的ps图表快速生成插件:PS拉框助手 for mac
  5. 4484: [Jsoi2015]最小表示
  6. 华为数通笔记-策略路由
  7. python制作好看的界面_python漂亮界面
  8. Rhino学习教程——1.2
  9. 工作感受月记 201906月
  10. A callback was made on a garbage collected delegate of type...