VNC源码研究(二十四)vnc-4.0-winsrc版本之winvnc工程分析

发布时间:2025-12-09 21:10:21 浏览次数:4

 转载请标明是引用于 http://blog.csdn.net/chenyujing1234    

MsgWindow的构造函数就是创建一个窗口,窗口名称为"VNCTray",大小为10*10;

TrayIcon() : MsgWindow(_T("VNCTray")) {#ifdef NOTIFYICONDATA_V1_SIZEnid.cbSize = NOTIFYICONDATA_V1_SIZE;#elsenid.cbSize = sizeof(NOTIFYICONDATA);#endifnid.hWnd = getHandle();nid.uID = 0;nid.hIcon = 0;nid.uFlags = NIF_ICON | NIF_MESSAGE;nid.uCallbackMessage = WM_USER;}

第三个构造函数:STrayIcon构造函数

STrayIcon(STrayIconThread& t) : thread(t),vncConfig(_T("vncconfig.exe"), isServiceProcess() ? _T("-noconsole -service") : _T("-noconsole")),vncConnect(_T("winvnc4.exe"), _T("-connect")) {// ***SetWindowText(getHandle(), _T("winvnc::IPC_Interface"));// ***SetTimer(getHandle(), 1, 3000, 0);PostMessage(getHandle(), WM_TIMER, 1, 0);PostMessage(getHandle(), WM_SET_TOOLTIP, 0, 0);}
在STrayIcon的构造函数中:

首先构造两对象

LaunchProcess vncConfig;LaunchProcess vncConnect;作用:在窗口收到消息WM_COMMAND,命令为ID_CONNECT时就连接服务端: CurrentUserToken token;if (token.isValid())vncConnect.start(isServiceProcess() ? (HANDLE)token : 0);elsevlog.error("Options: unknown current user"); void LaunchProcess::start(HANDLE userToken) {if (procInfo.hProcess && (WaitForSingleObject(procInfo.hProcess, 0) != WAIT_OBJECT_0))return;await();// - Create storage for the process startup informationSTARTUPINFO sinfo;memset(&sinfo, 0, sizeof(sinfo));sinfo.cb = sizeof(sinfo);// - Concoct a suitable command-lineTCharArray exePath;if (!tstrContains(exeName.buf, _T('\\'))) {ModuleFileName filename;TCharArray path; splitPath(filename.buf, &path.buf, 0);exePath.buf = new TCHAR[_tcslen(path.buf) + _tcslen(exeName.buf) + 2];_stprintf(exePath.buf, _T("%s\\%s"), path.buf, exeName.buf);} else {exePath.buf = tstrDup(exeName.buf);}// - Start the VNC server// Note: We specify the exe's precise path in the ApplicationName parameter,// AND include the name as the first part of the CommandLine parameter,// because CreateProcess doesn't make ApplicationName argv[0] in C programs.TCharArray cmdLine(_tcslen(exeName.buf) + 3 + _tcslen(params.buf) + 1);_stprintf(cmdLine.buf, _T("\"%s\" %s"), exeName.buf, params.buf);#ifdef _DEBUGDWORD flags = CREATE_NEW_CONSOLE;#elseDWORD flags = CREATE_NO_WINDOW;#endifBOOL success;if (userToken)success = CreateProcessAsUser(userToken, exePath.buf, cmdLine.buf, 0, 0, FALSE, flags, 0, 0, &sinfo, &procInfo);elsesuccess = CreateProcess(exePath.buf, cmdLine.buf, 0, 0, FALSE, flags, 0, 0, &sinfo, &procInfo);if (!success)throw rdr::SystemException("unable to launch process", GetLastError());// Wait for it to finish initialisingWaitForInputIdle(procInfo.hProcess, 15000);}


然后,设置窗口名称为_T("winvnc::IPC_Interface"

然后开启定时器1,每3S触发一次;

最后给窗口发送WM_TIMER与WM_SET_TOOLTIP消息;

作用:WM_TIMER用于刷新图标状态

如果桌面不是输入桌面,那么就把图标关闭,如果(“当前桌面和名称”与"输入桌面名称“是否一致来判断);

如果桌面还是存在那么更新当前的图标状态。(激活或非激活)

if (rfb::win32::desktopChangeRequired()) {SendMessage(getHandle(), WM_CLOSE, 0, 0);return 0;}setIcon(thread.server.isServerInUse() ? thread.activeIcon : thread.inactiveIcon);return 0;WM_SET_TOOLTIP用于设置”工具提示信息“ case WM_SET_TOOLTIP:{rfb::Lock l(thread.lock);if (thread.toolTip.buf)setToolTip(thread.toolTip.buf);}



缩写为TLS。进程中的全局变量与函数内定义的静态(static)变量,是各个线程都可以访问的共享变量。在一个线程修改的内存内容,对所有线程都生效。这是一个优点也是一个缺点。说它是优点,线程的数据交换变得非常快捷。说它是缺点,一个线程死掉了,其它线程也性命不保; 多个线程访问共享数据,需要昂贵的同步开销,也容易造成同步相关的BUG。

如果需要在一个线程内部的各个 函数调用都能访问、但其它线程不能访问的 变量(被称为static memory local to a thread 线程局部 静态变量),就需要新的机制来实现。这就是TLS。 线程局部存储在不同的平台有不同的实现,可移植性不太好。幸好要实现 线程局部存储并不难,最简单的办法就是建立一个全局表,通过当前线程ID去查询相应的数据,因为各个线程的ID不同,查到的数据自然也不同了。 Win32 方法一:每个线程创建时系统给它分配一个 LPVOID指针的数组(叫做TLS数组),这个数组从C编程角度是隐藏着的不能直接访问,需要通过一些C API函数调用访问。首先定义 些DWORD线程 全局变量或函数静态 变量,准备作为各个线程访问自己的TLS 数组的 索引变量。一个 线程使用TLS时,第一步在线程内调用TlsAlloc()函数, 为一个TLS数组索引变量与这个线程的TLS数组的某个槽(slot)关联起来,例如获得一个索引变量: global_dwTLSindex=TLSAlloc(); 注意,此步之后,当前线程实际**问的是这个TLS 数组索引变量的线程内的拷贝版本。也就说,不同线程虽然看起来用的是同名的TLS 数组索引变量,但实际上各个线程得到的可能是不同DWORD值。其意义在于,每个使用TLS的 线程获得了一个DWORD类型的 线程局部静态变量作为TLS 数组的索引变量。C/C++原本没有直接定义 线程局部静态变量的机制,所以在如此大费周折。 第二步,为当前线程动态分配一块内存区域(使用LocalAlloc() 函数调用),然后把指向这块内存区域的 指针放入TLS 数组相应的槽中(使用TlsValue()函数调用)。 第三步,在当前线程的任何函数内,都可以通过TLS 数组的索引变量,使用TlsGetValue()函数得到上一步的那块内存区域的 指针,然后就可以进行内存区域的读写操作了。这就实现了在一个线程内部这个范围处处可访问的变量。 最后,如果不再需要上述线程局部静态变量,要动态释放掉这块内存区域(使用LocalFree()函数),然后从TLS 数组中放弃对应的槽(使用TlsFree()函数)。 在VNC-4.0-winsrc开源代码中看到,它用了TSL的方法: (1)定义 static DWORD threadStorage = TlsAlloc(); 为一个TLS数组索引变量与这个线程的TLS数组的某个槽(slot)关联起来,例如获得一个索引变量: (2)设置值 WINBASEAPIBOOLWINAPITlsSetValue(__in DWORD dwTlsIndex,__in_opt LPVOID lpTlsValue);
DWORD WINAPIThread::threadProc(LPVOID lpParameter) {Thread* thread = (Thread*) lpParameter; TlsSetValue(threadStorage, thread);logAction(thread, "started");try {thread->run();logAction(thread, "stopped");} catch (rdr::Exception& e) {logError(thread, e.str());}bool deleteThread = false;{Lock l(thread->mutex);thread->state = ThreadStopped;thread->sig->signal();deleteThread = thread->deleteAfterRun;}if (deleteThread)delete thread;return 0;}
(3)获得值 WINBASEAPILPVOIDWINAPITlsGetValue(__in DWORD dwTlsIndex);

Thread*Thread::self() {Thread* thread = (Thread*) TlsGetValue(threadStorage);if (!thread) {thread = new Thread(GetCurrentThread(), GetCurrentThreadId()); TlsSetValue(threadStorage, thread);}return thread;}




2、__declspec(thread)的使用



#include <stdio.h>#include <assert.h>// 这就是两个线程都要访问的变量__declspec(thread) int g_nData = 0;DWORD WINAPI ThreadProc(LPVOID lpParameter){g_nData = 5;// 辅线程睡眠100ms,保证主线程的g_nData = 10;语句执行成功Sleep(100);TCHAR szMsg[100] = {0};wsprintf(szMsg, L"Auxi thread, g_nData:%d\n", g_nData);MessageBox(NULL, szMsg, L"AuxiThread", MB_ICONINFORMATION);return 0;}int main(){DWORD dwId;// 创建线程,并立即启动它HANDLE hThread = CreateThread(NULL, 1024, ThreadProc, NULL, 0, &dwId);assert(hThread);// 主线程睡50ms,保证辅线程的g_nData = 5语句执行成功。Sleep(50);g_nData = 10;TCHAR szMsg[100] = {0};wsprintf(szMsg, L"Result %d\n", g_nData);MessageBox(NULL, szMsg, L"MainThread", MB_ICONINFORMATION);return 0;}







需要做网站?需要网络推广?欢迎咨询客户经理 13272073477