WINVNC源码分析——vnchooks

Posted 松狮MVP

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WINVNC源码分析——vnchooks相关的知识,希望对你有一定的参考价值。

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.           
  17.         // Call the exit function  
  18.         // If the app failed to hook OK, ExitInstance will still operate OK (hopefully...)  
  19.         ExitInstance();  
  20.         return TRUE;  
  21.     default:  
  22.         return TRUE;  
  23.       
  24.   
 

 

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

 

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

 

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

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

以上是关于WINVNC源码分析——vnchooks的主要内容,如果未能解决你的问题,请参考以下文章

WINVNC分析——源码执行流程

WINVNC分析——源码执行流程

WINVNC源码分析——图像

WINVNC源码分析——图像

WINVNC源码分析——IO之rdr库

WINVNC源码分析——IO之rdr库