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時間長は外部に設定できない.さらに、このTimeout Spanは、各IPスタックの実装に関連する値を有する.
// 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スキームは33 msごとにシステム画面をポーリングし、変化した部分をUpdateRegionに追加する.これらの画面の変化のRectを取得するために、システムはまた1種の最適化Pollingアルゴリズムを採用して、スクリーンを32*32 pixelの小さい矩形のブロックに分けて、アルゴリズムは1つの各矩形のブロックのポーリングの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
};
現在の画面のマウス画像を取得する考え方:
ステップ1:画面マウスのHCURSORを取得する
GetCursorPosはスクリーンマウスの位置を取得し、WindowFromPointはマウスの所有者ウィンドウを取得し、GetWindowThreadProcessIdは関連スレッドIDを取得し、このIDとGetCurrentThreadIdが返したIDを比較し、同じであればGetCursorでマウスのHCURSORを直接取得する.そうでなければ、AttachThreadInputでターゲットスレッドのInput Mechanismに接続し、GetCursorを呼び出してマウスのHCURSORを取得します.
ステップ2: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のコードをダウンロードして、サーバーの部分のコードを分析して、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
そのサービス側の主な機能モジュールの構造は以下の通りである:その核心フレームワークは4つのクラスvncClient、vncServer、vncDesktopとvncBufferである.次に、この4つのクラス間の連絡と用途について簡単な分析を行います.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がクライアント接続に成功したことと、クライアント認証に成功したことの2つのイベントの処理手順を見てみましょう.
vncServer::AddClient:
まず、vncServerの内部にあるvncClient*m_Clientmap[MAX_CLIENTS]配列では、新規接続上のクライアントに対して空きslotを割り当てる、クライアントのクライアントIDとして使用する.次に、この接続に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は、auth list内の各顧客に対する同じメソッドの関数呼び出しにvncServerのメソッド呼び出しをマッピングするユーザーリストの操作インタフェースを提供します.
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という2つの登録メッセージ.
vncClient:
vncClientはデータ送信の仕事をして、vncClient::SendUpdate関数の実現の中で、私達はvncClient呼び出しを見ることができます
SendRFBMsgは最初に送信され、その後
SendCursorShapeUpdateはマウスの形状更新を送信し、
SendCursorPosUpdateマウスPos更新、送信を送信
SendCopyRect、最終呼び出し
SendRectanglesは、更新する必要がある長方形に関するデータを送信します.実は各クライアントvncClientはvncClient::Init初期化を呼び出すときにスレッドを開き、クライアントの動作は基本的にvncClientThread::runで完了します.このスレッドは,クライアントと対話して認証,Pixelフォーマット,Encodingアルゴリズムなどの情報の交渉が完了すると,リモートクライアントから送信されたrfbSetPixelFormat,rfbSetEncodings,rfbFramebufferUpdateRequest,rfbKeyEvent,rfbPointerEvent,rfbClientCutTextメッセージの受信と処理を開始するloopループに入る.
vncBuffer:
vncBufferは、主に内部のvncDesktopポインタを使用して関連するデータを取得するリモート・クライアントのローカル・ビューを提供する送信データのEncoding作業を処理します.
Reference:
  • http://www.tightvnc.com/