VNCHOOKS是一个设置全局钩子的动态链接库。
先看入口函数
view plain
BOOL WINAPI DllMain (HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
{
// Find out why we're being called
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
_RPT0(_CRT_WARN, "vncHooks : Hook DLL loaded/n");
// Save the instance handle
hInstance = (HINSTANCE)hInst;
// Call the initialisation function
appHookedOK = InitInstance();
// ALWAYS return TRUE to avoid breaking unhookable applications!!!
return TRUE;
case DLL_PROCESS_DETACH:
_RPT0(_CRT_WARN, "vncHooks : Hook DLL unloaded/n");
// Call the exit function
// If the app failed to hook OK, ExitInstance will still operate OK (hopefully...)
ExitInstance();
return TRUE;
default:
return TRUE;
}
}
BOOL WINAPI DllMain (HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
当VNCHOOKS.dll被加载的时候,首先惯例保留自己的句柄hInst,然后进入
BOOL InitInstance() 函数
view plain
BOOL InitInstance()
{
// Create the global atoms
VNC_POPUPSELN_ATOM = GlobalAddAtom(VNC_POPUPSELN_ATOMNAME);
// 请求一个原子变量
if (VNC_POPUPSELN_ATOM == NULL)
return FALSE;
// Get the module name
char proc_name[_MAX_PATH];
DWORD size;
// Attempt to get the program/module name
if ((size = GetModuleFileName(
GetModuleHandle(NULL),
//If this parameter is NULL, GetModuleHandle returns a handle to the file used to create the calling process (.exe file).
//返回当前进程可执行文件的句柄
(char *) &proc_name,
_MAX_PATH
)) == 0)
return FALSE;
// Get the default system key for the module
// HKEY_LOCAL_MACHINE/Software/ORL/VNCHooks/Application_Prefs/excute.exe(或者带文件全路径)
//ORL是啥缩写。。
hModuleKey = GetModuleKey(HKEY_LOCAL_MACHINE, proc_name, false, false);
if (hModuleKey != NULL) {
_RPT0(_CRT_WARN, "vncHooks : loading machine prefs/n");
ReadSettings();
RegCloseKey(hModuleKey);
hModuleKey = NULL;
}
// Get the key for the module
hModuleKey = GetModuleKey(HKEY_CURRENT_USER, proc_name, false, false);
// HKEY_CURRENT_USER/Software/ORL/VNCHooks/Application_Prefs/excute.exe(或者带文件全路径)
// 这样理解,如果某个可执行文件会调用本dll来钩住系统消息,那么可以通过在注册表修改相应配置来控制钩住消息的处理。
if (hModuleKey != NULL) {
_RPT0(_CRT_WARN, "vncHooks : loading user prefs/n");
ReadSettings();
//通常我们可以在进程中通过WriteProfileString这类函数来保存值。
//下次可以通过GetProfileString和::GetProfileInt这类函数来读取值。
// UINT GetProfileInt(
//LPCTSTR lpAppName,
// LPCTSTR lpKeyName,
// INT nDefault
// );
//MSDN中注意(Note): This function is provided only for compatibility with 16-bit Windows-based applications. Applications should store initialization information in the registry.
//意思是这个函数是为了16位系统的程序准备的,因为在Dos和Win3.x的时代,大部分的应用程序都是采用了 ini 文件(初始化文件)来保存一些配置信息,如设置路径,环境变量等
//在一个变化的环境中,在应用程序安装到系统中后,每个人都会更改.ini文件。而没有人去删除该信息,一个.ini文件的最大尺寸是64KB。
//所以微软建议32位进程直接写入注册表,而不是调用这个函数。
//只有在Windows Server 2003 and Windows XP/2000/NT系统下,对于WIN.INI部分键会在注册表中作一个映射,对于这些内容的访问还是可以调用这个函数的。
//被映射的键在HKEY_LOCAL_MACHINE/Software/Microsoft/Windows NT/CurrentVersion/IniFileMapping/win.ini下列了出来。
//我们看到key的name是WIN.INI键值,value是对应的注册表路径,但路径前面有些符号。
//!表示WriteProfileString会在注册表和WIN.INI分别写入。
//#表示PC上已经装了Windows 3.1,再安装Windows NT后,当一个用户第一次登陆的时候,注册表会把值写入Windows 3.1 .ini
//@表示如果GetProfile注册表中未找到,不会再在WIN.INI中继续查找
//USR:表示路径替换为HKEY_CURRENT_USER/,效果相当于C语言中 #define USR: HKEY_CURRENT_USER/
//SYS:表示路径替换为HKEY_LOCAL_MACHINE/SOFTWARE/
//以上是对MSDN的翻译,并未验证。
//WriteProfileString同样也是先查看IniFileMapping,并不推荐使用。
//所以在WINVNC是直接写入注册表的。
RegCloseKey(hModuleKey);
hModuleKey = NULL;
}
return TRUE;
}
BOOL InitInstance()
首先调用
ATOM GlobalAddAtom( LPCTSTR lpString
);
申请了一个原子变量,WINDOWS系统维护了一张原子表,可以这么理解,保存了一个字符串数组,每个字符串不能超过127字符。每个字符串对应一个唯一的ID(16位)。
最常用于Dynamic Data Exchange、防止2次启动(在WINVNC中是用命名的MUTEX来防止二次启动的)等。原子变量是采用计数的,每次调用GlobalAddAtom会让计数加1,GlobalDeleteAtom会让计数减1,
直到计数为0系统才会删除。原子在这里的应用将在稍后看到。
接下来是从注册表读取配置。
7个配置项
view plain
BOOL prf_use_GetUpdateRect = FALSE; // Use the GetUpdateRect paint mode
BOOL prf_use_Timer = TRUE; // Use Timer events to trigger updates
BOOL prf_use_KeyPress = TRUE; // Use keyboard events
BOOL prf_use_LButtonUp = TRUE; // Use left mouse button up events
BOOL prf_use_MButtonUp = FALSE; // Use middle mouse button up events
BOOL prf_use_RButtonUp = FALSE; // Use right mouse button up events
BOOL prf_use_Deferral = TRUE; // Use deferred updates
view plain
HKEY OpenKey(HKEY basekey, const char* path[], bool writable, bool create) {
HKEY key = basekey;
DWORD flags = KEY_READ;
if (writable) flags |= KEY_WRITE;
if (create) flags |= KEY_CREATE_SUB_KEY;
unsigned int i = 0;
while (path[i]) {
HKEY newkey;
DWORD result, dw;
if (create) {
_RPT2(_CRT_WARN, "vncHooks : creating %s from %x/n", path[i], key);
result = RegCreateKeyEx(key, path[i], 0, REG_NONE,
REG_OPTION_NON_VOLATILE, flags, NULL,
&newkey, &dw);
} else {
_RPT2(_CRT_WARN, "vncHooks : opening %s from %x/n", path[i], key);
result = RegOpenKeyEx(key, path[i], 0, flags, &newkey);
}
if (key && (key != basekey)) RegCloseKey(key);
key = newkey;
if (result != ERROR_SUCCESS) {
_RPT2(_CRT_WARN, "vncHooks : failed to open %s(%lu)/n", path[i], result);
return NULL;
} else {
_RPT2(_CRT_WARN, "vncHooks : opened %s (%x)/n", path[i], key);
}
i++;
}
return key;
}
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
HKEY GetModuleKey(HKEY basekey, const char* proc_name, bool writable, bool create) {
// Work out the registry key to save this under
if (!sModulePrefs) {
sModulePrefs = (char *) malloc(strlen(proc_name) + 1);
if (sModulePrefs == NULL)
return FALSE;
strcpy(sModulePrefs, proc_name);
}
// Check whether the library's entry exists!
//static const TCHAR szSoftware[] = "Software";
//static const TCHAR szCompany[] = "ORL";
//static const TCHAR szProfile[] = "VNCHooks";
const char* appPath[] = {szSoftware, szCompany, szProfile, 0};
HKEY appKey = OpenKey(basekey, appPath, writable, create);
if (!appKey)
return NULL;
// Attempt to open the registry section for the application
//const char sPrefSegment[] = "Application_Prefs";
//char *sModulePrefs = NULL; // Name of the module that created us
const char* modPath[] = {sPrefSegment, sModulePrefs, 0};
HKEY modKey = OpenKey(appKey, modPath, writable, false);
// Software/ORL/VNCHooks/Application_Prefs/...xxx.exe(...表示全路径)
if (!modKey) {
// Cut off the app directory and just use the name
char *file_name = NameFromPath(proc_name);
if (!file_name)
{
RegCloseKey(appKey);
return NULL;
}
// Adjust the moduleprefs name
strcpy(sModulePrefs, file_name);
free(file_name);
// Now get the module key again
//const char sPrefSegment[] = "Application_Prefs";
const char* modPath2[] = {sPrefSegment, sModulePrefs, 0};
modKey = OpenKey(appKey, modPath2, writable, create);
// Software/ORL/VNCHooks/Application_Prefs/vnchooks.dll(如果注册表中没找到全路径,则查找文件名)
}
RegCloseKey(appKey);
return modKey;
}
HKEY GetModuleKey(HKEY basekey, const char* proc_name, bool writable, bool create)
proc_name传入的是全路径的可执行文件名称,函数会尝试全路径名称的读取,如果失败,则会调用NameFromPath去掉路径,再读取或者创建。
view plain
static const TCHAR szSoftware[] = "Software";//注册表原有,系统用来保存已安装的软件
static const TCHAR szCompany[] = "ORL";//需创建
static const TCHAR szProfile[] = "VNCHooks";//需创建
const char sPrefSegment[] = "Application_Prefs";//需创建
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
void ReadSettings() {
// Read in the prefs
prf_use_GetUpdateRect = GetProfileInt(
"use_GetUpdateRect",
TRUE
);
//注意这里的GetProfileInt重载了系统的API,在注册表指定路径读取
prf_use_Timer = GetProfileInt(
"use_Timer",
FALSE
);
prf_use_KeyPress = GetProfileInt(
"use_KeyPress",
TRUE
);
prf_use_LButtonUp = GetProfileInt(
"use_LButtonUp",
TRUE
);
prf_use_MButtonUp = GetProfileInt(
"use_MButtonUp",
TRUE
);
prf_use_RButtonUp = GetProfileInt(
"use_RButtonUp",
TRUE
);
prf_use_Deferral = GetProfileInt(
"use_Deferral",
TRUE
);
}
void ReadSettings()来读取配置,这样每一个可执行文件(确切的说应该是名字)都在注册表中拥有自己的键,保存配置。我们可以主动地在注册表中写入全路径的可执行文件的配置,那么在这个路径中的可执行文件调用本库的时候就会读取指定配置了,否则就是所有同名的可执行文件启动的进程共用1个配置。
进程卸载本库的时候(DLL_PROCESS_DETACH)会调用
view plain
BOOL ExitInstance()
{
// Free the created atoms
if (VNC_POPUPSELN_ATOM != NULL)
{
// GlobalDeleteAtom(VNC_POPUPSELN_ATOM);
VNC_POPUPSELN_ATOM = NULL;
}
// Write the module settings to disk
if (sModulePrefs != NULL)
{
// Get the module name
char proc_name[_MAX_PATH];
DWORD size;
// Attempt to get the program/module name
if ((size = GetModuleFileName(
GetModuleHandle(NULL),
(char *) &proc_name,
_MAX_PATH
)) == 0)
return FALSE;
// Get the key for the module
_RPT0(_CRT_WARN, "vncHooks : locating user prefs/n");
hModuleKey = GetModuleKey(HKEY_CURRENT_USER, proc_name, true, true);
if (hModuleKey == NULL)
return FALSE;
_RPT0(_CRT_WARN, "vncHooks : writing user prefs/n");
//在进程释放本库调用时BOOL ExitInstance() 就通过RegSetValueEx函数保存了设置。
//注意这里的GetModuleKey第四个参数为true,表示如果注册表未找到键,则创建。
WriteProfileInt(
"use_GetUpdateRect",
prf_use_GetUpdateRect
);
WriteProfileInt(
"use_Timer",
prf_use_Timer
);
WriteProfileInt(
"use_KeyPress",
prf_use_KeyPress
);
WriteProfileInt(
"use_LButtonUp",
prf_use_LButtonUp
);
WriteProfileInt(
"use_MButtonUp",
prf_use_MButtonUp
);
WriteProfileInt(
"use_RButtonUp",
prf_use_RButtonUp
);
WriteProfileInt(
"use_Deferral",
prf_use_Deferral
);
free(sModulePrefs);
sModulePrefs = NULL;
}
// Close the registry key for this module
if (hModuleKey != NULL) {
RegCloseKey(hModuleKey);
hModuleKey = NULL;
}
return TRUE;
}
BOOL ExitInstance()
它是依次释放资源,并且保存配置到注册表。
view plain
#pragma data_seg(".SharedData")
DWORD vnc_thread_id = 0;
UINT UpdateRectMessage = 0;
UINT CopyRectMessage = 0;
UINT MouseMoveMessage = 0;
HHOOK hCallWndHook = NULL; // Handle to the CallWnd hook
HHOOK hGetMsgHook = NULL; // Handle to the GetMsg hook
HHOOK hDialogMsgHook = NULL; // Handle to the DialogMsg hook
HHOOK hLLKeyboardHook = NULL; // Handle to LowLevel kbd hook
HHOOK hLLMouseHook = NULL; // Handle to LowLevel mouse hook
#pragma data_seg( )
这里用了一个共享段,用于限制整个系统只有一个钩子线程,既一个线程来接收更新消息。所以该线程ID和钩子句柄,另外还保存3个钩子线程定义的消息ID。
view plain
DllExport BOOL SetHooks(DWORD thread_id, UINT UpdateMsg, UINT CopyMsg, UINT MouseMsg)
{
// Don't add the hook if the thread id is NULL
if (!thread_id)
return FALSE;
// Don't add a hook if there is already one added
if (vnc_thread_id)
return FALSE;
//用于限制整个系统只有一个线程来接收更新消息。
// Add the CallWnd hook
hCallWndHook = SetWindowsHookEx(
WH_CALLWNDPROC, // Hook in before msg reaches app
(HOOKPROC) CallWndProc, // Hook procedure
hInstance, // This DLL instance
0L // Hook in to all apps
// GetCurrentThreadId() // DEBUG : HOOK ONLY WinVNC
);
// Add the GetMessage hook
hGetMsgHook = SetWindowsHookEx(
WH_GETMESSAGE, // Hook in before msg reaches app
(HOOKPROC) GetMessageProc, // Hook procedure
hInstance, // This DLL instance
0L // Hook in to all apps
// GetCurrentThreadId() // DEBUG : HOOK ONLY WinVNC
);
// Add the GetMessage hook
hDialogMsgHook = SetWindowsHookEx(
WH_SYSMSGFILTER, // Hook in dialogs, menus and scrollbars
(HOOKPROC) DialogMessageProc, // Hook procedure
hInstance, // This DLL instance
0L // Hook in to all apps
);
// Check that it worked
if ((hCallWndHook != NULL) && (hGetMsgHook != NULL) && (hDialogMsgHook != NULL))
{
vnc_thread_id = thread_id; // Save the WinVNC thread id
UpdateRectMessage = UpdateMsg; // Save the message ID to use for rectangle updates
CopyRectMessage = CopyMsg; // Save the message ID to use for copyrect
MouseMoveMessage = MouseMsg; // Save the message ID to send when mouse moves
HookMaster = TRUE; // Set the HookMaster flag for this instance
return TRUE;
}
else
{
// Stop the keyboard hook
SetKeyboardFilterHook(FALSE);
SetMouseFilterHook(FALSE);
// Kill the main hooks
if (hCallWndHook != NULL)
UnhookWindowsHookEx(hCallWndHook);
if (hGetMsgHook != NULL)
UnhookWindowsHookEx(hGetMsgHook);
if (hDialogMsgHook != NULL)
UnhookWindowsHookEx(hDialogMsgHook);
hCallWndHook = NULL;
hGetMsgHook = NULL;
hDialogMsgHook = NULL;
}
// The hook failed, so return an error code
return FALSE;
}
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
inline BOOL HookHandle(UINT MessageId, HWND hWnd, WPARAM wParam, LPARAM lParam)
{
// HANDLE DEFERRED UPDATES
// Is this a deferred-update message?
if (MessageId == VNC_DEFERRED_UPDATE)
{
// NOTE : NEVER use the SendDeferred- routines to send updates
// from here, or you'll get an infinite loop....!
// NB : The format of DEFERRED_UPDATE matches that of UpdateRectMessage,
// so just send the exact same message data to WinVNC
if (!PostThreadMessage(
vnc_thread_id,
UpdateRectMessage,
wParam,
lParam
))
vnc_thread_id = 0;
return FALSE;
}
//进程主动发出的更新消息
// *** Could use WM_COPYDATA to send data to WinVNC
/*
if (GetClassLong(hWnd, GCW_ATOM) == 32768)
{
_RPT4(_CRT_WARN, "DBG : popup menu message (hwnd=%d, msg=%d, l=%d, w=%d)/n",
hWnd, MessageId, lParam, wParam);
}
*/
// UPDATE-TRIGGERING MESSAGES
// Do something dependent upon message type
switch (MessageId)
{
// Messages indicating only a border repaint.
case WM_NCPAINT:
case WM_NCACTIVATE:
SendDeferredBorderRect(hWnd);
//窗口边界更新
old_cursor = NULL;
break;
// Messages indicating a client area repaint
case WM_CHAR:
case WM_KEYUP: // Handle key-presses
if (prf_use_KeyPress)
SendDeferredWindowRect(hWnd);
//键盘输入,焦点窗口全部更新
break;
case WM_LBUTTONUP: // Handle LMB clicks
if (prf_use_LButtonUp)
SendDeferredWindowRect(hWnd);
//鼠标左键点击,焦点窗口全部更新
break;
case WM_MBUTTONUP: // Handle MMB clicks
if (prf_use_MButtonUp)
SendDeferredWindowRect(hWnd);
//鼠标中键点击,焦点窗口全部更新
break;
case WM_RBUTTONUP: // Handle RMB clicks
if (prf_use_RButtonUp)
SendDeferredWindowRect(hWnd);
//鼠标右键点击,焦点窗口全部更新
break;
case WM_MOUSEWHEEL: // Handle mousewheel events
SendDeferredWindowRect(hWnd);
//鼠标滚轮,焦点窗口全部更新
break;
case WM_TIMER:
if (prf_use_Timer)
SendDeferredWindowRect(hWnd);
//定时器,焦点窗口全部更新
break;
case WM_HSCROLL:
case WM_VSCROLL:
if (((int) LOWORD(wParam) == SB_THUMBTRACK) || ((int) LOWORD(wParam) == SB_ENDSCROLL))
//SB_THUMBTRACK 滚动条滑块被拖动
//SB_THUMBPOSTION 拖动后滚动条滑块被释放
//这是第一种情况,滚动条即时跟新窗口
//在 SB_ENDSCROLL 滚动条通知代码,它指示用户已滚动之后, 释放鼠标按钮。
// 这是第二种情况, 释放鼠标按钮后再更新窗口
SendDeferredWindowRect(hWnd);
//滚动条,焦点窗口全部更新
break;
case 485: // HACK to handle popup menus
//The 0x1e5 message is defined in private microsoft headers as MN_SELECTITEM.
// 0x01e5 (undocumented) - requests the menu to redraw the item supplied in the message wParam using an internal method (Windows 9x, 2K, XP)
// This last message (0x01e5) is the most interesting and the most crucial discovery I made on this project.
//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.
//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.
//当鼠标在菜单选项边界移动,会触发。
{
// Get the old popup menu selection value
HANDLE prop = GetProp(hWnd, (LPCTSTR) MAKELONG(VNC_POPUPSELN_ATOM, 0));
//查找窗口资源列表中是否有"VNCHooks.PopUpMenu.Selected"
//GetProp可以通过原子来查找资源句柄
if (prop != (HANDLE) wParam)
//wparam是menu的item偏移量
//保证WINDOW中"VNCHooks.PopUpMenu.Selected"资源永远对应被选择的ITEM。
//如果menu的item选择没有变化的时候是不需要更新窗口的,
//一旦item发生改变,就会把item与"VNCHooks.PopUpMenu.Selected"资源保存的item相比较,如果不同则更新。
{
// It did, so update the menu & the selection value
SendDeferredWindowRect(hWnd);
SetProp(hWnd,
(LPCTSTR) MAKELONG(VNC_POPUPSELN_ATOM, 0),
(HANDLE) wParam);
}
}
break;
// Messages indicating a full window update
case WM_SYSCOLORCHANGE:
case WM_PALETTECHANGED:
case WM_SETTEXT:
case WM_ENABLE:
case BM_SETCHECK:
case BM_SETSTATE:
case EM_SETSEL:
//case WM_MENUSELECT:
SendDeferredWindowRect(hWnd);
break;
// Messages indicating that an area of the window needs updating
// Uses GetUpdateRect to find out which
case WM_PAINT:
if (prf_use_GetUpdateRect)
{
HRGN region;
region = CreateRectRgn(0, 0, 0, 0);
// Get the affected region
if (GetUpdateRgn(hWnd, region, FALSE) != ERROR)
{
int buffsize;
UINT x;
RGNDATA *buff;
POINT TopLeft;
// Get the top-left point of the client area
TopLeft.x = 0;
TopLeft.y = 0;
if (!ClientToScreen(hWnd, &TopLeft))
break;
// Get the size of buffer required
buffsize = GetRegionData(region, 0, 0);
if (buffsize != 0)
{
buff = (RGNDATA *) new BYTE [buffsize];
if (buff == NULL)
break;
// Now get the region data
if(GetRegionData(region, buffsize, buff))
{
for (x=0; x<(buff->rdh.nCount); x++)
//rdh.nCount 指构成区域的矩形数目
{
// Obtain the rectangles from the list
RECT *urect = (RECT *) (((BYTE *) buff) + sizeof(RGNDATAHEADER) + (x * sizeof(RECT)));
SendDeferredUpdateRect(
hWnd,
(SHORT) (TopLeft.x + urect->left),
(SHORT) (TopLeft.y + urect->top),
(SHORT) (TopLeft.x + urect->right),
(SHORT) (TopLeft.y + urect->bottom)
);
}
}
delete [] buff;
}
}
// Now free the region
if (region != NULL)
DeleteObject(region);
}
else
SendDeferredWindowRect(hWnd);
break;
// Messages indicating full repaint of this and a different window
// Send the new position of the window
case WM_WINDOWPOSCHANGING:
if (IsWindowVisible(hWnd))
SendWindowRect(hWnd);
break;
case WM_WINDOWPOSCHANGED:
if (IsWindowVisible(hWnd))
SendDeferredWindowRect(hWnd);
break;
// WinVNC also wants to know about mouse movement
case WM_NCMOUSEMOVE:
case WM_MOUSEMOVE:
// Inform WinVNC that the mouse has moved and pass it the current cursor handle
{
ULONG new_cursor = (ULONG)GetCursor();
if (new_cursor != old_cursor) {
if (!PostThreadMessage(
vnc_thread_id,
MouseMoveMessage,
(ULONG) new_cursor, 0))
vnc_thread_id = 0;
old_cursor=new_cursor;
}
}
break;
// VNCHOOKS PROPERTIES HANDLING WINDOWS
case WM_DESTROY:
RemoveProp(hWnd, (LPCTSTR) MAKELONG(VNC_POPUPSELN_ATOM, 0));
break;
}
return TRUE;
}
const UINT VNC_DEFERRED_UPDATE = RegisterWindowMessage("VNCHooks.Deferred.UpdateMessage");
这里注册了一个系统级别的消息,当prf_use_Deferral为真,监听到的窗口消息不会发送给处理线程,而是发送一个VNC_DEFERRED_UPDATE消息到该窗口的posted-message queue。当下次窗口处理该消息的时候又会被我们钩住。
先看入口函数
view plain
BOOL WINAPI DllMain (HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
{
// Find out why we're being called
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
_RPT0(_CRT_WARN, "vncHooks : Hook DLL loaded/n");
// Save the instance handle
hInstance = (HINSTANCE)hInst;
// Call the initialisation function
appHookedOK = InitInstance();
// ALWAYS return TRUE to avoid breaking unhookable applications!!!
return TRUE;
case DLL_PROCESS_DETACH:
_RPT0(_CRT_WARN, "vncHooks : Hook DLL unloaded/n");
// Call the exit function
// If the app failed to hook OK, ExitInstance will still operate OK (hopefully...)
ExitInstance();
return TRUE;
default:
return TRUE;
}
}
BOOL WINAPI DllMain (HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
当VNCHOOKS.dll被加载的时候,首先惯例保留自己的句柄hInst,然后进入
BOOL InitInstance() 函数
view plain
BOOL InitInstance()
{
// Create the global atoms
VNC_POPUPSELN_ATOM = GlobalAddAtom(VNC_POPUPSELN_ATOMNAME);
// 请求一个原子变量
if (VNC_POPUPSELN_ATOM == NULL)
return FALSE;
// Get the module name
char proc_name[_MAX_PATH];
DWORD size;
// Attempt to get the program/module name
if ((size = GetModuleFileName(
GetModuleHandle(NULL),
//If this parameter is NULL, GetModuleHandle returns a handle to the file used to create the calling process (.exe file).
//返回当前进程可执行文件的句柄
(char *) &proc_name,
_MAX_PATH
)) == 0)
return FALSE;
// Get the default system key for the module
// HKEY_LOCAL_MACHINE/Software/ORL/VNCHooks/Application_Prefs/excute.exe(或者带文件全路径)
//ORL是啥缩写。。
hModuleKey = GetModuleKey(HKEY_LOCAL_MACHINE, proc_name, false, false);
if (hModuleKey != NULL) {
_RPT0(_CRT_WARN, "vncHooks : loading machine prefs/n");
ReadSettings();
RegCloseKey(hModuleKey);
hModuleKey = NULL;
}
// Get the key for the module
hModuleKey = GetModuleKey(HKEY_CURRENT_USER, proc_name, false, false);
// HKEY_CURRENT_USER/Software/ORL/VNCHooks/Application_Prefs/excute.exe(或者带文件全路径)
// 这样理解,如果某个可执行文件会调用本dll来钩住系统消息,那么可以通过在注册表修改相应配置来控制钩住消息的处理。
if (hModuleKey != NULL) {
_RPT0(_CRT_WARN, "vncHooks : loading user prefs/n");
ReadSettings();
//通常我们可以在进程中通过WriteProfileString这类函数来保存值。
//下次可以通过GetProfileString和::GetProfileInt这类函数来读取值。
// UINT GetProfileInt(
//LPCTSTR lpAppName,
// LPCTSTR lpKeyName,
// INT nDefault
// );
//MSDN中注意(Note): This function is provided only for compatibility with 16-bit Windows-based applications. Applications should store initialization information in the registry.
//意思是这个函数是为了16位系统的程序准备的,因为在Dos和Win3.x的时代,大部分的应用程序都是采用了 ini 文件(初始化文件)来保存一些配置信息,如设置路径,环境变量等
//在一个变化的环境中,在应用程序安装到系统中后,每个人都会更改.ini文件。而没有人去删除该信息,一个.ini文件的最大尺寸是64KB。
//所以微软建议32位进程直接写入注册表,而不是调用这个函数。
//只有在Windows Server 2003 and Windows XP/2000/NT系统下,对于WIN.INI部分键会在注册表中作一个映射,对于这些内容的访问还是可以调用这个函数的。
//被映射的键在HKEY_LOCAL_MACHINE/Software/Microsoft/Windows NT/CurrentVersion/IniFileMapping/win.ini下列了出来。
//我们看到key的name是WIN.INI键值,value是对应的注册表路径,但路径前面有些符号。
//!表示WriteProfileString会在注册表和WIN.INI分别写入。
//#表示PC上已经装了Windows 3.1,再安装Windows NT后,当一个用户第一次登陆的时候,注册表会把值写入Windows 3.1 .ini
//@表示如果GetProfile注册表中未找到,不会再在WIN.INI中继续查找
//USR:表示路径替换为HKEY_CURRENT_USER/,效果相当于C语言中 #define USR: HKEY_CURRENT_USER/
//SYS:表示路径替换为HKEY_LOCAL_MACHINE/SOFTWARE/
//以上是对MSDN的翻译,并未验证。
//WriteProfileString同样也是先查看IniFileMapping,并不推荐使用。
//所以在WINVNC是直接写入注册表的。
RegCloseKey(hModuleKey);
hModuleKey = NULL;
}
return TRUE;
}
BOOL InitInstance()
首先调用
ATOM GlobalAddAtom( LPCTSTR lpString
);
申请了一个原子变量,WINDOWS系统维护了一张原子表,可以这么理解,保存了一个字符串数组,每个字符串不能超过127字符。每个字符串对应一个唯一的ID(16位)。
最常用于Dynamic Data Exchange、防止2次启动(在WINVNC中是用命名的MUTEX来防止二次启动的)等。原子变量是采用计数的,每次调用GlobalAddAtom会让计数加1,GlobalDeleteAtom会让计数减1,
直到计数为0系统才会删除。原子在这里的应用将在稍后看到。
接下来是从注册表读取配置。
7个配置项
view plain
BOOL prf_use_GetUpdateRect = FALSE; // Use the GetUpdateRect paint mode
BOOL prf_use_Timer = TRUE; // Use Timer events to trigger updates
BOOL prf_use_KeyPress = TRUE; // Use keyboard events
BOOL prf_use_LButtonUp = TRUE; // Use left mouse button up events
BOOL prf_use_MButtonUp = FALSE; // Use middle mouse button up events
BOOL prf_use_RButtonUp = FALSE; // Use right mouse button up events
BOOL prf_use_Deferral = TRUE; // Use deferred updates
view plain
HKEY OpenKey(HKEY basekey, const char* path[], bool writable, bool create) {
HKEY key = basekey;
DWORD flags = KEY_READ;
if (writable) flags |= KEY_WRITE;
if (create) flags |= KEY_CREATE_SUB_KEY;
unsigned int i = 0;
while (path[i]) {
HKEY newkey;
DWORD result, dw;
if (create) {
_RPT2(_CRT_WARN, "vncHooks : creating %s from %x/n", path[i], key);
result = RegCreateKeyEx(key, path[i], 0, REG_NONE,
REG_OPTION_NON_VOLATILE, flags, NULL,
&newkey, &dw);
} else {
_RPT2(_CRT_WARN, "vncHooks : opening %s from %x/n", path[i], key);
result = RegOpenKeyEx(key, path[i], 0, flags, &newkey);
}
if (key && (key != basekey)) RegCloseKey(key);
key = newkey;
if (result != ERROR_SUCCESS) {
_RPT2(_CRT_WARN, "vncHooks : failed to open %s(%lu)/n", path[i], result);
return NULL;
} else {
_RPT2(_CRT_WARN, "vncHooks : opened %s (%x)/n", path[i], key);
}
i++;
}
return key;
}
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
HKEY GetModuleKey(HKEY basekey, const char* proc_name, bool writable, bool create) {
// Work out the registry key to save this under
if (!sModulePrefs) {
sModulePrefs = (char *) malloc(strlen(proc_name) + 1);
if (sModulePrefs == NULL)
return FALSE;
strcpy(sModulePrefs, proc_name);
}
// Check whether the library's entry exists!
//static const TCHAR szSoftware[] = "Software";
//static const TCHAR szCompany[] = "ORL";
//static const TCHAR szProfile[] = "VNCHooks";
const char* appPath[] = {szSoftware, szCompany, szProfile, 0};
HKEY appKey = OpenKey(basekey, appPath, writable, create);
if (!appKey)
return NULL;
// Attempt to open the registry section for the application
//const char sPrefSegment[] = "Application_Prefs";
//char *sModulePrefs = NULL; // Name of the module that created us
const char* modPath[] = {sPrefSegment, sModulePrefs, 0};
HKEY modKey = OpenKey(appKey, modPath, writable, false);
// Software/ORL/VNCHooks/Application_Prefs/...xxx.exe(...表示全路径)
if (!modKey) {
// Cut off the app directory and just use the name
char *file_name = NameFromPath(proc_name);
if (!file_name)
{
RegCloseKey(appKey);
return NULL;
}
// Adjust the moduleprefs name
strcpy(sModulePrefs, file_name);
free(file_name);
// Now get the module key again
//const char sPrefSegment[] = "Application_Prefs";
const char* modPath2[] = {sPrefSegment, sModulePrefs, 0};
modKey = OpenKey(appKey, modPath2, writable, create);
// Software/ORL/VNCHooks/Application_Prefs/vnchooks.dll(如果注册表中没找到全路径,则查找文件名)
}
RegCloseKey(appKey);
return modKey;
}
HKEY GetModuleKey(HKEY basekey, const char* proc_name, bool writable, bool create)
proc_name传入的是全路径的可执行文件名称,函数会尝试全路径名称的读取,如果失败,则会调用NameFromPath去掉路径,再读取或者创建。
view plain
static const TCHAR szSoftware[] = "Software";//注册表原有,系统用来保存已安装的软件
static const TCHAR szCompany[] = "ORL";//需创建
static const TCHAR szProfile[] = "VNCHooks";//需创建
const char sPrefSegment[] = "Application_Prefs";//需创建
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
void ReadSettings() {
// Read in the prefs
prf_use_GetUpdateRect = GetProfileInt(
"use_GetUpdateRect",
TRUE
);
//注意这里的GetProfileInt重载了系统的API,在注册表指定路径读取
prf_use_Timer = GetProfileInt(
"use_Timer",
FALSE
);
prf_use_KeyPress = GetProfileInt(
"use_KeyPress",
TRUE
);
prf_use_LButtonUp = GetProfileInt(
"use_LButtonUp",
TRUE
);
prf_use_MButtonUp = GetProfileInt(
"use_MButtonUp",
TRUE
);
prf_use_RButtonUp = GetProfileInt(
"use_RButtonUp",
TRUE
);
prf_use_Deferral = GetProfileInt(
"use_Deferral",
TRUE
);
}
void ReadSettings()来读取配置,这样每一个可执行文件(确切的说应该是名字)都在注册表中拥有自己的键,保存配置。我们可以主动地在注册表中写入全路径的可执行文件的配置,那么在这个路径中的可执行文件调用本库的时候就会读取指定配置了,否则就是所有同名的可执行文件启动的进程共用1个配置。
进程卸载本库的时候(DLL_PROCESS_DETACH)会调用
view plain
BOOL ExitInstance()
{
// Free the created atoms
if (VNC_POPUPSELN_ATOM != NULL)
{
// GlobalDeleteAtom(VNC_POPUPSELN_ATOM);
VNC_POPUPSELN_ATOM = NULL;
}
// Write the module settings to disk
if (sModulePrefs != NULL)
{
// Get the module name
char proc_name[_MAX_PATH];
DWORD size;
// Attempt to get the program/module name
if ((size = GetModuleFileName(
GetModuleHandle(NULL),
(char *) &proc_name,
_MAX_PATH
)) == 0)
return FALSE;
// Get the key for the module
_RPT0(_CRT_WARN, "vncHooks : locating user prefs/n");
hModuleKey = GetModuleKey(HKEY_CURRENT_USER, proc_name, true, true);
if (hModuleKey == NULL)
return FALSE;
_RPT0(_CRT_WARN, "vncHooks : writing user prefs/n");
//在进程释放本库调用时BOOL ExitInstance() 就通过RegSetValueEx函数保存了设置。
//注意这里的GetModuleKey第四个参数为true,表示如果注册表未找到键,则创建。
WriteProfileInt(
"use_GetUpdateRect",
prf_use_GetUpdateRect
);
WriteProfileInt(
"use_Timer",
prf_use_Timer
);
WriteProfileInt(
"use_KeyPress",
prf_use_KeyPress
);
WriteProfileInt(
"use_LButtonUp",
prf_use_LButtonUp
);
WriteProfileInt(
"use_MButtonUp",
prf_use_MButtonUp
);
WriteProfileInt(
"use_RButtonUp",
prf_use_RButtonUp
);
WriteProfileInt(
"use_Deferral",
prf_use_Deferral
);
free(sModulePrefs);
sModulePrefs = NULL;
}
// Close the registry key for this module
if (hModuleKey != NULL) {
RegCloseKey(hModuleKey);
hModuleKey = NULL;
}
return TRUE;
}
BOOL ExitInstance()
它是依次释放资源,并且保存配置到注册表。
view plain
#pragma data_seg(".SharedData")
DWORD vnc_thread_id = 0;
UINT UpdateRectMessage = 0;
UINT CopyRectMessage = 0;
UINT MouseMoveMessage = 0;
HHOOK hCallWndHook = NULL; // Handle to the CallWnd hook
HHOOK hGetMsgHook = NULL; // Handle to the GetMsg hook
HHOOK hDialogMsgHook = NULL; // Handle to the DialogMsg hook
HHOOK hLLKeyboardHook = NULL; // Handle to LowLevel kbd hook
HHOOK hLLMouseHook = NULL; // Handle to LowLevel mouse hook
#pragma data_seg( )
这里用了一个共享段,用于限制整个系统只有一个钩子线程,既一个线程来接收更新消息。所以该线程ID和钩子句柄,另外还保存3个钩子线程定义的消息ID。
view plain
DllExport BOOL SetHooks(DWORD thread_id, UINT UpdateMsg, UINT CopyMsg, UINT MouseMsg)
{
// Don't add the hook if the thread id is NULL
if (!thread_id)
return FALSE;
// Don't add a hook if there is already one added
if (vnc_thread_id)
return FALSE;
//用于限制整个系统只有一个线程来接收更新消息。
// Add the CallWnd hook
hCallWndHook = SetWindowsHookEx(
WH_CALLWNDPROC, // Hook in before msg reaches app
(HOOKPROC) CallWndProc, // Hook procedure
hInstance, // This DLL instance
0L // Hook in to all apps
// GetCurrentThreadId() // DEBUG : HOOK ONLY WinVNC
);
// Add the GetMessage hook
hGetMsgHook = SetWindowsHookEx(
WH_GETMESSAGE, // Hook in before msg reaches app
(HOOKPROC) GetMessageProc, // Hook procedure
hInstance, // This DLL instance
0L // Hook in to all apps
// GetCurrentThreadId() // DEBUG : HOOK ONLY WinVNC
);
// Add the GetMessage hook
hDialogMsgHook = SetWindowsHookEx(
WH_SYSMSGFILTER, // Hook in dialogs, menus and scrollbars
(HOOKPROC) DialogMessageProc, // Hook procedure
hInstance, // This DLL instance
0L // Hook in to all apps
);
// Check that it worked
if ((hCallWndHook != NULL) && (hGetMsgHook != NULL) && (hDialogMsgHook != NULL))
{
vnc_thread_id = thread_id; // Save the WinVNC thread id
UpdateRectMessage = UpdateMsg; // Save the message ID to use for rectangle updates
CopyRectMessage = CopyMsg; // Save the message ID to use for copyrect
MouseMoveMessage = MouseMsg; // Save the message ID to send when mouse moves
HookMaster = TRUE; // Set the HookMaster flag for this instance
return TRUE;
}
else
{
// Stop the keyboard hook
SetKeyboardFilterHook(FALSE);
SetMouseFilterHook(FALSE);
// Kill the main hooks
if (hCallWndHook != NULL)
UnhookWindowsHookEx(hCallWndHook);
if (hGetMsgHook != NULL)
UnhookWindowsHookEx(hGetMsgHook);
if (hDialogMsgHook != NULL)
UnhookWindowsHookEx(hDialogMsgHook);
hCallWndHook = NULL;
hGetMsgHook = NULL;
hDialogMsgHook = NULL;
}
// The hook failed, so return an error code
return FALSE;
}
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
inline BOOL HookHandle(UINT MessageId, HWND hWnd, WPARAM wParam, LPARAM lParam)
{
// HANDLE DEFERRED UPDATES
// Is this a deferred-update message?
if (MessageId == VNC_DEFERRED_UPDATE)
{
// NOTE : NEVER use the SendDeferred- routines to send updates
// from here, or you'll get an infinite loop....!
// NB : The format of DEFERRED_UPDATE matches that of UpdateRectMessage,
// so just send the exact same message data to WinVNC
if (!PostThreadMessage(
vnc_thread_id,
UpdateRectMessage,
wParam,
lParam
))
vnc_thread_id = 0;
return FALSE;
}
//进程主动发出的更新消息
// *** Could use WM_COPYDATA to send data to WinVNC
/*
if (GetClassLong(hWnd, GCW_ATOM) == 32768)
{
_RPT4(_CRT_WARN, "DBG : popup menu message (hwnd=%d, msg=%d, l=%d, w=%d)/n",
hWnd, MessageId, lParam, wParam);
}
*/
// UPDATE-TRIGGERING MESSAGES
// Do something dependent upon message type
switch (MessageId)
{
// Messages indicating only a border repaint.
case WM_NCPAINT:
case WM_NCACTIVATE:
SendDeferredBorderRect(hWnd);
//窗口边界更新
old_cursor = NULL;
break;
// Messages indicating a client area repaint
case WM_CHAR:
case WM_KEYUP: // Handle key-presses
if (prf_use_KeyPress)
SendDeferredWindowRect(hWnd);
//键盘输入,焦点窗口全部更新
break;
case WM_LBUTTONUP: // Handle LMB clicks
if (prf_use_LButtonUp)
SendDeferredWindowRect(hWnd);
//鼠标左键点击,焦点窗口全部更新
break;
case WM_MBUTTONUP: // Handle MMB clicks
if (prf_use_MButtonUp)
SendDeferredWindowRect(hWnd);
//鼠标中键点击,焦点窗口全部更新
break;
case WM_RBUTTONUP: // Handle RMB clicks
if (prf_use_RButtonUp)
SendDeferredWindowRect(hWnd);
//鼠标右键点击,焦点窗口全部更新
break;
case WM_MOUSEWHEEL: // Handle mousewheel events
SendDeferredWindowRect(hWnd);
//鼠标滚轮,焦点窗口全部更新
break;
case WM_TIMER:
if (prf_use_Timer)
SendDeferredWindowRect(hWnd);
//定时器,焦点窗口全部更新
break;
case WM_HSCROLL:
case WM_VSCROLL:
if (((int) LOWORD(wParam) == SB_THUMBTRACK) || ((int) LOWORD(wParam) == SB_ENDSCROLL))
//SB_THUMBTRACK 滚动条滑块被拖动
//SB_THUMBPOSTION 拖动后滚动条滑块被释放
//这是第一种情况,滚动条即时跟新窗口
//在 SB_ENDSCROLL 滚动条通知代码,它指示用户已滚动之后, 释放鼠标按钮。
// 这是第二种情况, 释放鼠标按钮后再更新窗口
SendDeferredWindowRect(hWnd);
//滚动条,焦点窗口全部更新
break;
case 485: // HACK to handle popup menus
//The 0x1e5 message is defined in private microsoft headers as MN_SELECTITEM.
// 0x01e5 (undocumented) - requests the menu to redraw the item supplied in the message wParam using an internal method (Windows 9x, 2K, XP)
// This last message (0x01e5) is the most interesting and the most crucial discovery I made on this project.
//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.
//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.
//当鼠标在菜单选项边界移动,会触发。
{
// Get the old popup menu selection value
HANDLE prop = GetProp(hWnd, (LPCTSTR) MAKELONG(VNC_POPUPSELN_ATOM, 0));
//查找窗口资源列表中是否有"VNCHooks.PopUpMenu.Selected"
//GetProp可以通过原子来查找资源句柄
if (prop != (HANDLE) wParam)
//wparam是menu的item偏移量
//保证WINDOW中"VNCHooks.PopUpMenu.Selected"资源永远对应被选择的ITEM。
//如果menu的item选择没有变化的时候是不需要更新窗口的,
//一旦item发生改变,就会把item与"VNCHooks.PopUpMenu.Selected"资源保存的item相比较,如果不同则更新。
{
// It did, so update the menu & the selection value
SendDeferredWindowRect(hWnd);
SetProp(hWnd,
(LPCTSTR) MAKELONG(VNC_POPUPSELN_ATOM, 0),
(HANDLE) wParam);
}
}
break;
// Messages indicating a full window update
case WM_SYSCOLORCHANGE:
case WM_PALETTECHANGED:
case WM_SETTEXT:
case WM_ENABLE:
case BM_SETCHECK:
case BM_SETSTATE:
case EM_SETSEL:
//case WM_MENUSELECT:
SendDeferredWindowRect(hWnd);
break;
// Messages indicating that an area of the window needs updating
// Uses GetUpdateRect to find out which
case WM_PAINT:
if (prf_use_GetUpdateRect)
{
HRGN region;
region = CreateRectRgn(0, 0, 0, 0);
// Get the affected region
if (GetUpdateRgn(hWnd, region, FALSE) != ERROR)
{
int buffsize;
UINT x;
RGNDATA *buff;
POINT TopLeft;
// Get the top-left point of the client area
TopLeft.x = 0;
TopLeft.y = 0;
if (!ClientToScreen(hWnd, &TopLeft))
break;
// Get the size of buffer required
buffsize = GetRegionData(region, 0, 0);
if (buffsize != 0)
{
buff = (RGNDATA *) new BYTE [buffsize];
if (buff == NULL)
break;
// Now get the region data
if(GetRegionData(region, buffsize, buff))
{
for (x=0; x<(buff->rdh.nCount); x++)
//rdh.nCount 指构成区域的矩形数目
{
// Obtain the rectangles from the list
RECT *urect = (RECT *) (((BYTE *) buff) + sizeof(RGNDATAHEADER) + (x * sizeof(RECT)));
SendDeferredUpdateRect(
hWnd,
(SHORT) (TopLeft.x + urect->left),
(SHORT) (TopLeft.y + urect->top),
(SHORT) (TopLeft.x + urect->right),
(SHORT) (TopLeft.y + urect->bottom)
);
}
}
delete [] buff;
}
}
// Now free the region
if (region != NULL)
DeleteObject(region);
}
else
SendDeferredWindowRect(hWnd);
break;
// Messages indicating full repaint of this and a different window
// Send the new position of the window
case WM_WINDOWPOSCHANGING:
if (IsWindowVisible(hWnd))
SendWindowRect(hWnd);
break;
case WM_WINDOWPOSCHANGED:
if (IsWindowVisible(hWnd))
SendDeferredWindowRect(hWnd);
break;
// WinVNC also wants to know about mouse movement
case WM_NCMOUSEMOVE:
case WM_MOUSEMOVE:
// Inform WinVNC that the mouse has moved and pass it the current cursor handle
{
ULONG new_cursor = (ULONG)GetCursor();
if (new_cursor != old_cursor) {
if (!PostThreadMessage(
vnc_thread_id,
MouseMoveMessage,
(ULONG) new_cursor, 0))
vnc_thread_id = 0;
old_cursor=new_cursor;
}
}
break;
// VNCHOOKS PROPERTIES HANDLING WINDOWS
case WM_DESTROY:
RemoveProp(hWnd, (LPCTSTR) MAKELONG(VNC_POPUPSELN_ATOM, 0));
break;
}
return TRUE;
}
const UINT VNC_DEFERRED_UPDATE = RegisterWindowMessage("VNCHooks.Deferred.UpdateMessage");
这里注册了一个系统级别的消息,当prf_use_Deferral为真,监听到的窗口消息不会发送给处理线程,而是发送一个VNC_DEFERRED_UPDATE消息到该窗口的posted-message queue。当下次窗口处理该消息的时候又会被我们钩住。