UltraVNC(TightVNC、WinVNC)源码分析文档
2017-02-23 15:05
1396 查看
Posted on 2005-10-10 09:56
Piccolo Goo 阅读(3248) 评论(1) 编辑收藏
System Shell
IActiveDesktop
SetProcessShutdownParameters
One Instance Running by Mutex
sscanf
Kill ScreenSaver
Diable Nagle Algorithm
Screen Capture
Poll Schema
TightVNC
System Shell :
1.1 IActiveDesktop
Allows a client program to manage the desktop items and wallpaper on a local computer.
1.2 SetProcessShutdownParameters
The
SetProcessShutdownParameters function sets shutdown parameters for the currently calling process. This function sets a shutdown order for a process relative to the other processes in the system.
1.3 One Instance Running by Mutex
Use Win32 Mutex object to insure that only one instance is currently running in our OS.
1.4 sscanf
Read formatted data from a string.
1.5 Kill Screen Saver
1.6 Disable Nagle Algorithm
Nagle Algorithm主要是用于优化小数据包的发送,用于在IP栈中缓冲小数据包,积累一定数量的小数据包一起发送。这样可以减少系统发包的数量,Nagle Algorithm实现时内部也有一个Timeout机制,但这个Timeout时间长度不能在外部设置。而且对每个IP栈的实现,这个Timeout Span 都有一个实现相关的值。
Screen Capture :
2.1 Poll Schema
Poll方式就是最简单的一种方式了,也是大家在考虑截屏时最先想到一个解决方案。为取得系统的屏幕,poll方案每33ms轮询一次系统屏幕,将变化的部分添加加到一个UpdateRegion中。为获取这些屏幕变化的Rect,系统还采取了一种优化Polling算法,将屏幕分为32*32 pixel的小矩形块,算法给出了一个各个矩形块轮询的Order:
获取当前屏幕鼠标图像的思路:
第一步:获取屏幕鼠标的HCURSOR
GetCursorPos获取屏幕鼠标位置,然后WindowFromPoint获取鼠标的拥有者窗口,GetWindowThreadProcessId获取相关线程ID,比较该ID和GetCurrentThreadId返回的ID,如果相同,通过GetCursor直接获取鼠标的HCURSOR;否则,通过AttachThreadInput连接上目标线程的Input Mechanism,再调用GetCursor获取鼠标的HCURSOR。
第二步:获取HCURSOR的Bitmap
现在有必要对HRGN操作相关API做一下介绍
2.2 TightVNC
TightVNC(Tight Virtual Network Computing)是一个远程桌面控制的开源软件,详情请参考http://www.tightvnc.com.下载了TightVNC的代码,分析了一下其Server部分的代码, WinVNC下的文件很多,但我们按照它们各自的功能做一下划分,其结构如下:
其服务端的主要功能模块结构如下:
其核心框架就是四个类vncClient,vncServer,vncDesktop和vncBuffer.下面我就这四个类之间的联系和用途来作一下简单的分析:
vncServer:
vncServer主要是做如下的一些工作:容许vncClient动态的添加和删除;将本地vncDesktop对象内部状态的任何改变"传播"到各个客户端;传播客户端的鼠标和键盘事件到本地的vncDesktop对象。同时,其还创建了vncSockConnect,vncCORBAConnect和vncHTTPConnect来接受Socket,Corba和HTTP的连接。 vncServer为每个连接上来的客户端分配了一个ClientID(其实就是内部客户对象数组的Index),并且提供了对客户端管理的众多函数:
同时,vncServer还提供了对客户Teleport,Capability,KeyboardEnabled,PointerEnabled,Name,Authenticated属性的get/set方法。
下面我们来看一下vncServer对客户端连接上来和客户端认证成功这两个事件的处理流程:
vncServer::AddClient:
首先vncServer在其内部的vncClient *m_clientmap[MAX_CLIENTS]数组中为新连接上的客户端分配一个空闲的slot,并将其作为此客户的 clientID. 然后,为此连接分配一个vncClient对象,根据传递过来的参数,设置vncClient对象的相关属性,然后调用vncClient::Init方法将vncServer的实例指针和 clientID传给vncClient实例。接着,m_clientmap[clientid] = client并将此用户加入vncServer的未认证用户链表。
vncServer::Authenticated(vncClientId clientid):
首先从未认证用户列表中根据clientid获取vncClient对象,并将其从unauth list 中删除。如果是vncServer的第一个用户,创建vncDesktop对象,并调用m_desktop->Init(this)来初始化该vncDesktop对象。接下来,为这个用户分配一个vncBuffer *buffer = new vncBuffer(m_desktop);并通过调用vncClient::SetBuffer为vncClient设置这个Buffer,最后将此用户添加到auth list中。
vncServer提供了一个用户列表的操作接口,这些接口通过将vncServer的方法调用映射到对auth list中各个客户的同样的方法的函数调用,这些方法有:
vncDesktop:
vncDesktop是一个全局唯一的对象,根据注释,vncDesktop主要是处理从display buffer中获取数据;同时,它还利用RFBLib DLL为vncServer提供诸如鼠标移动和屏幕更新等信息。上面提到,vncServer在第一个用户连接上来时发现其m_desktop为空时就创建一个vncDesktip对象,并调用 vncDesktop::Init(this)对其初始化.在vcnDesktop::Init的实现中我们发现其创建了一个vncDesktopThread,vncDesktop的方法调用大部分都在这个vncDesktopThread里完成的.下面我们来分析一下这个线程都做了些什么:
vncDesktopThread::run_undetached(void *arg):
首先调用vncDesktop::Startup初始化,vncDesktop对象(见vncDesktop::Startup),然后就是处理桌面消息,调用 m_server->UpdateMouse()和m_server->UpdateRegion(rgncache) ,接下来调用vncServer::TriggerUpdate来发送屏幕更新到每个vncClient.然后就是处理RFB_SCREEN_UPDATE和RFB_MOUSE_UPDATE这两个注册消息。
vncClient:
vncClient做了数据发送的工作,在vncClient::SendUpdate函数的实现中,我们可以看到vncClient调用SendRFBMsg首先发送 ,然后SendCursorShapeUpdate发送鼠标形状更新,SendCursorPosUpdate发送鼠标Pos更新,发送SendCopyRect,最后调用SendRectangles发送需要更新的矩形的相关数据。其实每个客户端vncClient在调用vncClient::Init初始化的时候都开了一个线程,客户端的行为基本上都是在vncClientThread::run里完成的。该线程在跟客户端交互完成了认证,Pixel格式,Encoding算法等信息的协商后,就进入一个loop循环开始接受和处理远程客户端发过来的rfbSetPixelFormat,rfbSetEncodings,rfbFramebufferUpdateRequest,rfbKeyEvent,rfbPointerEvent,rfbClientCutText消息。
vncBuffer:
vncBuffer主要处理发送数据的Encoding工作,其提供了远程客户的本地视图,其主要是利用内部的vncDesktop指针来获取相关的数据。
1. 首先得讲一下它用到的omni_thread库
google搜出来的链接:http://omniorb.sourceforge.net/omni40/omnithread.html,介绍
了omni_thread库的基本使用,足够了,这里还是把它用中文啰嗦一下:
1) 它的接口与用C语言实现的POSIX threads接口是类似的。作为线程库,它提供了基本的
同步对象:互斥量,条件变量,信号量;还提供了线程对象;也提供了线程私有数据。
2) 互斥量(Mutex)
omni_mutex对象,操作它的方法有lock(),unlock()。acquire()和release()分别与
lock(), unlock()有相同的语义。
3) 条件变量(Conditional variable)
omni_condition对象。wait()方法使线程在条件变量上等待,signal()唤醒至少一个正等
待该条件变量的线程(pthread_cond_wait()是只唤醒一个正等待的线程,不知道是不是这
个 文档写错了,竟然与pthread语义不一样),broadcast()唤醒所有正等待该条件变量
的线程。
一旦omni_condition对象创建,一个互斥量就与该条件变量对象在对象的整个生命周期
中绑定,而pthread的条件变量只在wait期间进行绑定。
4) 信号量(semaphore)
omni_semaphore对象。wait()等待,即Dijkstra所说的P操作;post()唤醒,即V操作。
5) 线程对象(thread object)
有两种方法使用线程对象。第一种,以需要执行的函数为参数创建对象。第二种,从omni
_thread继承产生一个新类,omni_thread库将会执行该新类的run()或run_undetached()
函 数 。
一旦线程对象创建,即可调用start()或start_undetached()使线程开始运行。若调用sta
rt()函数,omni_thread库会以run()函数为执行例程,并detached模式运行。若调用
start_undetached()函数,omni_thread库会以run_undetached()函数为执行例程,但以u
ndetached模式运行。
vncDesktop处理所有与桌面相关的消息,如用户操作,Hook DLL的消息,等等。vncClient代表了一个vnc客户。vncServer是VNC服务器类。vncSockConnect代表了网络连接,比listen, accept就是它做的。vncEncodexxx的一系列类代表了各种编码方式。vncRegion代表了任意的屏幕区域,它提供了方法得到组成该区域的一系列矩形。基本上,都是先new xxx类,再调用它的Init函数,而该函数就会new一个xxxThread对象,并调用xxxThread的Init函数,xxxThread的Init函数就会创建线程了,线程的入口函数是run()或run_undetached()。
程序基本流程是,vncSockConnectThread对象accept一个客户后,就new一个vncClient对象,并调用其Init函数,该函数new一个vncClientThread对象并调用它的Init函数,该函数会启动与客户端进行RFB交互的线程,线程入口函数是:vncClientThread::run()。
源码中使用到了HOOK,一般翻译成挂钩,不过个人觉得某本书上翻译的异常分支比较直观一点。Tight VNC源码中提供的Hook源码比较简单,只是有一点要注意,Hook无法得到所有屏幕变化的区域,最终还是得靠全屏幕扫描来得到所有的屏幕变化区域。另外,Ultra VNC源码中提供的Hook源码用PostThreadMessage函数来发送消息,用这份Hook源码作测试,除了调用SetWindowsHook函数的进程外,其他的进程根本收不任何消息。MSDN讲该函数往GUI 线程发消息容易丢失,但我是发送到普通线程,并非GUI线程。用Process
Viewer查看,确实每个进程都把DLL映射到自己的地址空间中了。还是Tig ht VNC中的Hook比较好一点,使用PostMessage函数,消息不会丢失。
关于全屏幕扫描,前面讲到过,Hook无法得到屏幕的所有变化区域,还得辅之以屏幕扫描。Tight VNC中的屏幕区域扫描算法如下:(见PollArea函数)扫描方法是一次一行,由上至下,扫描线间距为32像素,依次取如下数组中的元素作为第 一行扫描线的位置:
const int vncDesktop::m_pollingOrder[32] = {
0, 16, 8, 24, 4, 20, 12, 28,
10, 26, 18, 2, 22, 6, 30, 14,
1, 17, 9, 25, 7, 23, 15, 31,
19, 3,27, 11, 29, 13, 5, 21};
对每行扫描线,以32像素长为单位,依次与前一次的屏幕比较,若有变化,则认为两条扫描线间32x32的矩形区域均有变化,记录到变化矩形列表中。若无变化,则认为两条扫描线间32x32的矩形区域均无变化。同样,通过Hook得到的屏幕区域变化也需要作记录。记录由Hook得到的屏幕变化区域见vncDesktopThread::run_undetached函数。
Piccolo Goo 阅读(3248) 评论(1) 编辑收藏
Content List:
System Shell
IActiveDesktop
SetProcessShutdownParameters
One Instance Running by Mutex
sscanf
Kill ScreenSaver
Diable Nagle Algorithm
Screen Capture
Poll Schema
TightVNC
System Shell :
1.1 IActiveDesktop
Allows a client program to manage the desktop items and wallpaper on a local computer.
#include IActiveDesktop* active_desktop = 0; CoCreateInstance(CLSID_ActiveDesktop, NULL, CLSCTX_INPROC_SERVER, IID_IActiveDesktop, (void**)&active_desktop);
1.2 SetProcessShutdownParameters
The
SetProcessShutdownParameters function sets shutdown parameters for the currently calling process. This function sets a shutdown order for a process relative to the other processes in the system.
// Set this process to be the last application to be shut down. SetProcessShutdownParameters(0x100, 0);
1.3 One Instance Running by Mutex
Use Win32 Mutex object to insure that only one instance is currently running in our OS.
const char mutexname [] = "WinVNC_Win32_Instance_Mutex"; BOOL vncInstHandler::Init() { // Create the named mutex HANDLE mutex = CreateMutex(NULL, FALSE, mutexname); if (mutex == NULL) return FALSE; // Check that the mutex didn't already exist if (GetLastError() == ERROR_ALREADY_EXISTS) return FALSE; return TRUE; }
1.4 sscanf
Read formatted data from a string.
// Check the protocol version int major, minor; sscanf((char *)&protocol_ver, "RFB %03d.%03d\n", &major, &minor);
1.5 Kill Screen Saver
// How to kill the screen saver depends on the OS switch (osversioninfo.dwPlatformId) case VER_PLATFORM_WIN32_WINDOWS: HWND hsswnd = FindWindow ("WindowsScreenSaverClass", NULL); if (hsswnd != NULL) PostMessage(hsswnd, WM_CLOSE, 0, 0); break; case VER_PLATFORM_WIN32_NT: HDESK hDesk = OpenDesktop( "Screen-saver", 0, FALSE, DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS ); if (hDesk != NULL) { EnumDesktopWindows(hDesk, (WNDENUMPROC) &KillScreenSaverFunc, 0); CloseDesktop(hDesk); // Pause long enough for the screen-saver to close //Sleep(2000); // Reset the screen saver so it can run again SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, TRUE, 0, SPIF_SENDWININICHANGE); } break; }
1.6 Disable Nagle Algorithm
Nagle Algorithm主要是用于优化小数据包的发送,用于在IP栈中缓冲小数据包,积累一定数量的小数据包一起发送。这样可以减少系统发包的数量,Nagle Algorithm实现时内部也有一个Timeout机制,但这个Timeout时间长度不能在外部设置。而且对每个IP栈的实现,这个Timeout Span 都有一个实现相关的值。
// Disable Nagle's algorithm BOOL nodelayval = TRUE; setsockopt(m_sock, IPPROTO_TCP, TCP_NODELAY, (const char *)&nodelayval, sizeof(BOOL));
Screen Capture :
2.1 Poll Schema
Poll方式就是最简单的一种方式了,也是大家在考虑截屏时最先想到一个解决方案。为取得系统的屏幕,poll方案每33ms轮询一次系统屏幕,将变化的部分添加加到一个UpdateRegion中。为获取这些屏幕变化的Rect,系统还采取了一种优化Polling算法,将屏幕分为32*32 pixel的小矩形块,算法给出了一个各个矩形块轮询的Order:
const int pollingOrder[32] = { 0, 16, 8, 24, 4, 20, 12, 28, 10, 26, 18, 2, 22, 6, 30, 14, 1, 17, 9, 25, 7, 23, 15, 31, 19, 3, 27, 11, 29, 13, 5, 21 };
获取当前屏幕鼠标图像的思路:
第一步:获取屏幕鼠标的HCURSOR
GetCursorPos获取屏幕鼠标位置,然后WindowFromPoint获取鼠标的拥有者窗口,GetWindowThreadProcessId获取相关线程ID,比较该ID和GetCurrentThreadId返回的ID,如果相同,通过GetCursor直接获取鼠标的HCURSOR;否则,通过AttachThreadInput连接上目标线程的Input Mechanism,再调用GetCursor获取鼠标的HCURSOR。
第二步:获取HCURSOR的Bitmap
int :GetCursorSendBuffer(BYTE *pBuffer, int nSize) { .... ICONINFO IconInfo; GetIconInfo(hcursor, &IconInfo); BITMAP bmMask; GetObject(IconInfo.hbmMask, sizeof(BITMAP), (LPVOID)&bmMask); GetBitmapBits(IconInfo.hbmMask,bmMask.bmWidthBytes * bmMask.bmHeight, m_mBits); .... }
现在有必要对HRGN操作相关API做一下介绍
GetRgnBox 获取HRGN的边界矩形 GetRegionData 可以将一个HRGN分解为一个RECT数组,见RGNDATA结构体说明 CombineRgn 对HRGN操作RGN_AND,RGN_COPY,RGN_DIFF,RGN_OR或是RGN_XOR
2.2 TightVNC
TightVNC(Tight Virtual Network Computing)是一个远程桌面控制的开源软件,详情请参考http://www.tightvnc.com.下载了TightVNC的代码,分析了一下其Server部分的代码, WinVNC下的文件很多,但我们按照它们各自的功能做一下划分,其结构如下:
Kernel | vncBuffer.cpp vncClient.cpp vncDesktop.cpp vncServer.cpp WinVNC.cpp |
GUI | vncAbout.cpp vncAcceptDialog.cpp vncAdvancedProperties.cpp vncConnDialog.cpp vncMenu.cpp vncProperties.cpp vncTimedMsgBox.cpp |
Misc | d3des.c Log.cpp MinMax.cpp RectList.cpp stdhdrs.cpp tableinitcmtemplate.cpp tableinittctemplate.cpp tabletranstemplate.cpp translate.cpp vncauth.c vncInstHandler.cpp vncKeymap.cpp vncRegion.cpp< vncService.cpp |
Network | VSocket.cpp vncSockConnect.cpp vncHTTPConnect.cpp rfbproto.h |
Encoding | vncEncodeCoRRE.cpp vncEncodeHexT.cpp vncEncoder.cpp vncEncodeRRE.cpp vncEncodeTight.cpp vncEncodeZlib.cpp vncEncodeZlibHex.cpp |
其核心框架就是四个类vncClient,vncServer,vncDesktop和vncBuffer.下面我就这四个类之间的联系和用途来作一下简单的分析:
vncServer:
vncServer主要是做如下的一些工作:容许vncClient动态的添加和删除;将本地vncDesktop对象内部状态的任何改变"传播"到各个客户端;传播客户端的鼠标和键盘事件到本地的vncDesktop对象。同时,其还创建了vncSockConnect,vncCORBAConnect和vncHTTPConnect来接受Socket,Corba和HTTP的连接。 vncServer为每个连接上来的客户端分配了一个ClientID(其实就是内部客户对象数组的Index),并且提供了对客户端管理的众多函数:
virtual void DisableClients(BOOL state); virtual void KillClient(vncClientId client); virtual void KillAuthClients(); virtual void KillUnauthClients(); virtual vncClient* GetClient(vncClientId clientid); vncClientId AddClient(VSocket *socket, BOOL auth, BOOL shared); virtual void RemoveClient(vncClientId client);
同时,vncServer还提供了对客户Teleport,Capability,KeyboardEnabled,PointerEnabled,Name,Authenticated属性的get/set方法。
下面我们来看一下vncServer对客户端连接上来和客户端认证成功这两个事件的处理流程:
vncServer::AddClient:
首先vncServer在其内部的vncClient *m_clientmap[MAX_CLIENTS]数组中为新连接上的客户端分配一个空闲的slot,并将其作为此客户的 clientID. 然后,为此连接分配一个vncClient对象,根据传递过来的参数,设置vncClient对象的相关属性,然后调用vncClient::Init方法将vncServer的实例指针和 clientID传给vncClient实例。接着,m_clientmap[clientid] = client并将此用户加入vncServer的未认证用户链表。
vncServer::Authenticated(vncClientId clientid):
首先从未认证用户列表中根据clientid获取vncClient对象,并将其从unauth list 中删除。如果是vncServer的第一个用户,创建vncDesktop对象,并调用m_desktop->Init(this)来初始化该vncDesktop对象。接下来,为这个用户分配一个vncBuffer *buffer = new vncBuffer(m_desktop);并通过调用vncClient::SetBuffer为vncClient设置这个Buffer,最后将此用户添加到auth list中。
vncServer提供了一个用户列表的操作接口,这些接口通过将vncServer的方法调用映射到对auth list中各个客户的同样的方法的函数调用,这些方法有:
virtual void TriggerUpdate(); virtual void UpdateRect(RECT &rect); virtual void UpdateRegion(vncRegion ®ion); virtual void CopyRect(RECT &dest, POINT &source); virtual void UpdateMouse(); virtual void UpdateClipText(LPSTR text); virtual void UpdatePalette();
vncDesktop:
vncDesktop是一个全局唯一的对象,根据注释,vncDesktop主要是处理从display buffer中获取数据;同时,它还利用RFBLib DLL为vncServer提供诸如鼠标移动和屏幕更新等信息。上面提到,vncServer在第一个用户连接上来时发现其m_desktop为空时就创建一个vncDesktip对象,并调用 vncDesktop::Init(this)对其初始化.在vcnDesktop::Init的实现中我们发现其创建了一个vncDesktopThread,vncDesktop的方法调用大部分都在这个vncDesktopThread里完成的.下面我们来分析一下这个线程都做了些什么:
vncDesktopThread::run_undetached(void *arg):
首先调用vncDesktop::Startup初始化,vncDesktop对象(见vncDesktop::Startup),然后就是处理桌面消息,调用 m_server->UpdateMouse()和m_server->UpdateRegion(rgncache) ,接下来调用vncServer::TriggerUpdate来发送屏幕更新到每个vncClient.然后就是处理RFB_SCREEN_UPDATE和RFB_MOUSE_UPDATE这两个注册消息。
vncClient:
vncClient做了数据发送的工作,在vncClient::SendUpdate函数的实现中,我们可以看到vncClient调用SendRFBMsg首先发送 ,然后SendCursorShapeUpdate发送鼠标形状更新,SendCursorPosUpdate发送鼠标Pos更新,发送SendCopyRect,最后调用SendRectangles发送需要更新的矩形的相关数据。其实每个客户端vncClient在调用vncClient::Init初始化的时候都开了一个线程,客户端的行为基本上都是在vncClientThread::run里完成的。该线程在跟客户端交互完成了认证,Pixel格式,Encoding算法等信息的协商后,就进入一个loop循环开始接受和处理远程客户端发过来的rfbSetPixelFormat,rfbSetEncodings,rfbFramebufferUpdateRequest,rfbKeyEvent,rfbPointerEvent,rfbClientCutText消息。
vncBuffer:
vncBuffer主要处理发送数据的Encoding工作,其提供了远程客户的本地视图,其主要是利用内部的vncDesktop指针来获取相关的数据。
1. 首先得讲一下它用到的omni_thread库
google搜出来的链接:http://omniorb.sourceforge.net/omni40/omnithread.html,介绍
了omni_thread库的基本使用,足够了,这里还是把它用中文啰嗦一下:
1) 它的接口与用C语言实现的POSIX threads接口是类似的。作为线程库,它提供了基本的
同步对象:互斥量,条件变量,信号量;还提供了线程对象;也提供了线程私有数据。
2) 互斥量(Mutex)
omni_mutex对象,操作它的方法有lock(),unlock()。acquire()和release()分别与
lock(), unlock()有相同的语义。
3) 条件变量(Conditional variable)
omni_condition对象。wait()方法使线程在条件变量上等待,signal()唤醒至少一个正等
待该条件变量的线程(pthread_cond_wait()是只唤醒一个正等待的线程,不知道是不是这
个 文档写错了,竟然与pthread语义不一样),broadcast()唤醒所有正等待该条件变量
的线程。
一旦omni_condition对象创建,一个互斥量就与该条件变量对象在对象的整个生命周期
中绑定,而pthread的条件变量只在wait期间进行绑定。
4) 信号量(semaphore)
omni_semaphore对象。wait()等待,即Dijkstra所说的P操作;post()唤醒,即V操作。
5) 线程对象(thread object)
有两种方法使用线程对象。第一种,以需要执行的函数为参数创建对象。第二种,从omni
_thread继承产生一个新类,omni_thread库将会执行该新类的run()或run_undetached()
函 数 。
一旦线程对象创建,即可调用start()或start_undetached()使线程开始运行。若调用sta
rt()函数,omni_thread库会以run()函数为执行例程,并detached模式运行。若调用
start_undetached()函数,omni_thread库会以run_undetached()函数为执行例程,但以u
ndetached模式运行。
vncDesktop处理所有与桌面相关的消息,如用户操作,Hook DLL的消息,等等。vncClient代表了一个vnc客户。vncServer是VNC服务器类。vncSockConnect代表了网络连接,比listen, accept就是它做的。vncEncodexxx的一系列类代表了各种编码方式。vncRegion代表了任意的屏幕区域,它提供了方法得到组成该区域的一系列矩形。基本上,都是先new xxx类,再调用它的Init函数,而该函数就会new一个xxxThread对象,并调用xxxThread的Init函数,xxxThread的Init函数就会创建线程了,线程的入口函数是run()或run_undetached()。
程序基本流程是,vncSockConnectThread对象accept一个客户后,就new一个vncClient对象,并调用其Init函数,该函数new一个vncClientThread对象并调用它的Init函数,该函数会启动与客户端进行RFB交互的线程,线程入口函数是:vncClientThread::run()。
源码中使用到了HOOK,一般翻译成挂钩,不过个人觉得某本书上翻译的异常分支比较直观一点。Tight VNC源码中提供的Hook源码比较简单,只是有一点要注意,Hook无法得到所有屏幕变化的区域,最终还是得靠全屏幕扫描来得到所有的屏幕变化区域。另外,Ultra VNC源码中提供的Hook源码用PostThreadMessage函数来发送消息,用这份Hook源码作测试,除了调用SetWindowsHook函数的进程外,其他的进程根本收不任何消息。MSDN讲该函数往GUI 线程发消息容易丢失,但我是发送到普通线程,并非GUI线程。用Process
Viewer查看,确实每个进程都把DLL映射到自己的地址空间中了。还是Tig ht VNC中的Hook比较好一点,使用PostMessage函数,消息不会丢失。
关于全屏幕扫描,前面讲到过,Hook无法得到屏幕的所有变化区域,还得辅之以屏幕扫描。Tight VNC中的屏幕区域扫描算法如下:(见PollArea函数)扫描方法是一次一行,由上至下,扫描线间距为32像素,依次取如下数组中的元素作为第 一行扫描线的位置:
const int vncDesktop::m_pollingOrder[32] = {
0, 16, 8, 24, 4, 20, 12, 28,
10, 26, 18, 2, 22, 6, 30, 14,
1, 17, 9, 25, 7, 23, 15, 31,
19, 3,27, 11, 29, 13, 5, 21};
对每行扫描线,以32像素长为单位,依次与前一次的屏幕比较,若有变化,则认为两条扫描线间32x32的矩形区域均有变化,记录到变化矩形列表中。若无变化,则认为两条扫描线间32x32的矩形区域均无变化。同样,通过Hook得到的屏幕区域变化也需要作记录。记录由Hook得到的屏幕变化区域见vncDesktopThread::run_undetached函数。
相关文章推荐
- 【资料汇编】结巴中文分词官方文档和源码分析系列文章
- Tiled源码分析(二): 多文档支持
- WINVNC源码分析(五)——vnchooks
- Windows下用Doxygen轻松为c,c++,java编写源码文档、协助源码分析
- VNC源码研究(二十四)vnc-4.0-winsrc版本之winvnc工程分析
- lua源码分析文档
- 【资料汇编】结巴中文分词官方文档和源码分析系列文章
- TightVNC分析文档
- WINVNC源码分析(三)rdr
- ecshop数据库表与模板结构、源码分析在线说明文档
- Scrapy源码分析-Spiders爬虫中文文档(一)
- Jimmy的文档:Glusterfs之rpc模块源码分析附录(rdma和tcp)
- WINVNC源码分析(四)-vnchooks
- WINVNC源码分析(二)——图像
- WINVNC源码分析(四)-vnchooks
- CowNew开源学习文档-hibernate 的HQL源码分析1
- TightVNC分析文档(转)
- VNC源码研究(二十四)vnc-4.0-winsrc版本之winvnc工程分析
- WINVNC源码分析(三)——omni_thread库
- TightVNC分析文档