VNC源码研究(二十四)vnc-4.0-winsrc版本之winvnc工程分析
2013-03-11 11:06
597 查看
转载请标明是引用于 http://blog.csdn.net/chenyujing1234
此工程编译出winvnc4.exe二进制文件。它得到的二进制是作为VNC服务端来使用的。
步二、打印程序详细信息
programInfo();
步三、处理命令行
找到窗口_T("winvnc::IPC_Interface"),将得到的COPYDATASTRUCT数据发送到窗口中。
因为在服务端此窗口的创建是在 “第一个构造函数:”, 而此时窗口一定是找不到的,那么还有一种情况可以找到就是VNC服务端与VNC客户端并存时就会出现。
这是因为VNC客户端也会创建此窗口。
那么可以说,这时的发送WM_COPYDATA其实是发送给客户端的。
针对此WM_COPYDATA的响应是:
(2)
(3)
(4)
(5)
(6)
(7)
步四、启动服务
有两种方式可以选择:服务方式、用户模式(!!!!!!!!最重要)
runServer的初始值是static bool runServer = true;,当我们有指定命令行时,此变量会变为false。
如在 “步三、处理命令行" ,命令行是“-connect”、"-disconnect"、“-start”、"-stop"、"-status"、"-register"、"-unregister"、"-help"
意思是如果指定了命令行,那么是不会启动服务的。
如果是要关闭console界面那么调用系统API
(1)以服务方式启动客户端
(2)以用户模式启动客户端服务
VNCServerWin32 server;
(2、2)创建 tray icon
创建时会调用STrayIconThread的构造函数:
(2、2、2)创建STrayIcon,并获得它的窗口句柄。
创建STrayIcon时会调用到三个构造函数:
第一个构造函数:
MsgWindow的构造函数就是创建一个窗口,窗口名称为"VNCTray",大小为10*10;
窗口类名为(const TCHAR*)baseClass.classAtom; _T("rfb::win32::MsgWindowClass")
baseClass是类MsgWindowClass,它用来管理窗口类。
如果消息是在创建窗口时,即CreateWindows时,会收到消息WM_CREATE,此时把(long)((CREATESTRUCT*)lParam)->lpCreateParams,即MsgWindow的this指针作为
窗口数据存起来。之后的MsgWindowsProc中对各消息的处理就是取出窗口中的数据,强转为MsgWindows后,调用它的processMessage 函数,
因为MsgWindows类中的processMessage成员是虚函数,所以如果继承类有去重载它,那么是只会调用了继承类的processMessage函数。如:
STrayIcon的成员processMessage.
第二个构造函数:
第三个构造函数:STrayIcon构造函数
在STrayIcon的构造函数中:
首先构造两对象
然后,设置窗口名称为_T("winvnc::IPC_Interface"
然后开启定时器1,每3S触发一次;
最后给窗口发送WM_TIMER与WM_SET_TOOLTIP消息;
作用:WM_TIMER用于刷新图标状态
如果桌面不是输入桌面,那么就把图标关闭,如果(“当前桌面和名称”与"输入桌面名称“是否一致来判断);
如果桌面还是存在那么更新当前的图标状态。(激活或非激活)
(2、2、3)消息循环
(2、3)创建监听socket
(2、4)进入一个while的操作,直到收到WM_QUIT后才退出
这里最重要的是 sockMgr.getMessage(&msg, NULL, 0, 0 函数!!!!!!!!
缩写为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)定义
(2)设置值
(3)获得值
此工程编译出winvnc4.exe二进制文件。它得到的二进制是作为VNC服务端来使用的。
一、从main说起
int main(int argc, const char* argv[]) { int result = 0; try { // - Initialise the available loggers //freopen("\\\\drupe\\tjr\\WinVNC4.log","ab",stderr); //setbuf(stderr, 0); initStdIOLoggers(); initFileLogger("C:\\temp\\WinVNC4.log"); rfb::win32::initEventLogLogger(VNCServerService::Name); // - By default, just log errors to stderr logParams.setParam("*:stderr:0"); // - Print program details and process the command line programInfo(); processParams(argc, argv); // - Run the server if required if (runServer) { if (close_console) { vlog.info("closing console"); if (!FreeConsole()) vlog.info("unable to close console:%u", GetLastError()); } network::TcpSocket::initTcpSockets(); VNCServerWin32 server; if (runAsService) { printf("Starting Service-Mode VNC Server.\n"); VNCServerService service(server); ///>启动服务 service.start(); result = service.getStatus().dwWin32ExitCode; } else { printf("Starting User-Mode VNC Server.\n"); ///>启动server result = server.run(); } } vlog.debug("WinVNC service destroyed"); } catch (rdr::Exception& e) { try { vlog.error("Fatal Error: %s", e.str()); } catch (...) { fprintf(stderr, "WinVNC: Fatal Error: %s\n", e.str()); } if (!runAsService) MsgBox(0, TStr(e.str()), MB_ICONERROR | MB_OK); } vlog.debug("WinVNC process quitting"); return result; }步一、打印信息设置
步二、打印程序详细信息
programInfo();
步三、处理命令行
processParams(argc, argv);
void processParams(int argc, const char* argv[]) { for (int i=1; i<argc; i++) { try { if (strcasecmp(argv[i], "-connect") == 0) { runServer = false; CharArray host; if (i+1 < argc) { host.buf = strDup(argv[i+1]); } else { AddNewClientDialog ancd; if (ancd.showDialog()) host.buf = strDup(ancd.getHostName()); } if (host.buf) { HWND hwnd = FindWindow(0, _T("winvnc::IPC_Interface")); COPYDATASTRUCT copyData; copyData.dwData = 1; // *** AddNewClient copyData.cbData = strlen(host.buf); copyData.lpData = (void*)host.buf; i++; SendMessage(hwnd, WM_COPYDATA, 0, (LPARAM)©Data); printf("Sent connect request to VNC Server...\n"); } } else if (strcasecmp(argv[i], "-disconnect") == 0) { HWND hwnd = FindWindow(0, _T("winvnc::IPC_Interface")); COPYDATASTRUCT copyData; copyData.dwData = 2; // *** DisconnectClients copyData.lpData = 0; copyData.cbData = 0; SendMessage(hwnd, WM_COPYDATA, 0, (LPARAM)©Data); printf("Sent disconnect request to VNC Server...\n"); runServer = false; } else if (strcasecmp(argv[i], "-start") == 0) { printf("Attempting to start service...\n"); runServer = false; if (rfb::win32::startService(VNCServerService::Name)) printf("Started service successfully\n"); } else if (strcasecmp(argv[i], "-stop") == 0) { printf("Attempting to stop service...\n"); runServer = false; if (rfb::win32::stopService(VNCServerService::Name)) printf("Stopped service successfully\n"); } else if (strcasecmp(argv[i], "-status") == 0) { printf("Querying service status...\n"); runServer = false; rfb::win32::printServiceStatus(VNCServerService::Name); } else if (strcasecmp(argv[i], "-service") == 0) { printf("Run in service mode\n"); runAsService = true; } else if (strcasecmp(argv[i], "-register") == 0) { printf("Attempting to register service...\n"); runServer = false; int j = i; i = argc; if (rfb::win32::registerService(VNCServerService::Name, _T("VNC Server Version 4"), argc-(j+1), &argv[j+1])) printf("Registered service successfully\n"); } else if (strcasecmp(argv[i], "-unregister") == 0) { printf("Attempting to unregister service...\n"); runServer = false; if (rfb::win32::unregisterService(VNCServerService::Name)) printf("Unregistered service successfully\n"); } else if (strcasecmp(argv[i], "-noconsole") == 0) { close_console = true; } else if ((strcasecmp(argv[i], "-help") == 0) || (strcasecmp(argv[i], "--help") == 0) || (strcasecmp(argv[i], "-h") == 0) || (strcasecmp(argv[i], "/?") == 0)) { runServer = false; programUsage(); break; } else { // Try to process <option>=<value>, or -<bool> if (Configuration::setParam(argv[i], true)) continue; // Try to process -<option> <value> if ((argv[i][0] == '-') && (i+1 < argc)) { if (Configuration::setParam(&argv[i][1], argv[i+1], true)) { i++; continue; } } // Nope. Show them usage and don't run the server runServer = false; programUsage(); break; } } catch (rdr::Exception& e) { vlog.error(e.str()); } } }(1)从命令行中提取出IP地址串,并组成把它放到COPYDATASTRUCT中。
找到窗口_T("winvnc::IPC_Interface"),将得到的COPYDATASTRUCT数据发送到窗口中。
因为在服务端此窗口的创建是在 “第一个构造函数:”, 而此时窗口一定是找不到的,那么还有一种情况可以找到就是VNC服务端与VNC客户端并存时就会出现。
这是因为VNC客户端也会创建此窗口。
那么可以说,这时的发送WM_COPYDATA其实是发送给客户端的。
if (host.buf) { HWND hwnd = FindWindow(0, _T("winvnc::IPC_Interface")); COPYDATASTRUCT copyData; copyData.dwData = 1; // *** AddNewClient copyData.cbData = strlen(host.buf); copyData.lpData = (void*)host.buf; i++; SendMessage(hwnd, WM_COPYDATA, 0, (LPARAM)©Data); printf("Sent connect request to VNC Server...\n"); }
针对此WM_COPYDATA的响应是:
(2)
(3)
(4)
(5)
(6)
(7)
步四、启动服务
有两种方式可以选择:服务方式、用户模式(!!!!!!!!最重要)
if (runAsService) { printf("Starting Service-Mode VNC Server.\n"); VNCServerService service(server); ///>启动服务 service.start(); result = service.getStatus().dwWin32ExitCode; } else { printf("Starting User-Mode VNC Server.\n"); ///>启动server result = server.run(); }启动时得保证 runServer == true;
runServer的初始值是static bool runServer = true;,当我们有指定命令行时,此变量会变为false。
如在 “步三、处理命令行" ,命令行是“-connect”、"-disconnect"、“-start”、"-stop"、"-status"、"-register"、"-unregister"、"-help"
意思是如果指定了命令行,那么是不会启动服务的。
如果是要关闭console界面那么调用系统API
if (!FreeConsole())来实现
(1)以服务方式启动客户端
VNCServerWin32 server; if (runAsService) { printf("Starting Service-Mode VNC Server.\n"); VNCServerService service(server); ///>启动服务 service.start(); result = service.getStatus().dwWin32ExitCode;
(2)以用户模式启动客户端服务
VNCServerWin32 server;
result = server.run();
///>服务器启动 int VNCServerWin32::run() { { Lock l(runLock); hostThread = Thread::self(); runServer = true; } // - Register for notification of configuration changes ///>注册表设置 if (isServiceProcess()) config.setKey(HKEY_LOCAL_MACHINE, RegConfigPath); else config.setKey(HKEY_CURRENT_USER, RegConfigPath); config.setNotifyThread(Thread::self(), VNCM_REG_CHANGED); // - Create the tray icon if possible STrayIconThread trayIcon(*this, IDI_ICON, IDI_CONNECTED, IDR_TRAY); DWORD result = 0; try { // - Create some managed listening sockets ///>创建监听socket ManagedListener rfb(&sockMgr, &vncServer); ManagedListener http(&sockMgr, httpServer); // - Continue to operate until WM_QUIT is processed MSG msg; do { // -=- Make sure we're listening on the right ports. ///>设置监听端口 rfb.setPort(port_number, localHost); http.setPort(http_port, localHost); // -=- Update the Java viewer's web page port number. httpServer->setRFBport(rfb.sock ? port_number : 0); // -=- Update the TCP address filter for both ports, if open. CharArray pattern; pattern.buf = hosts.getData(); if (!localHost) { rfb.setFilter(pattern.buf); http.setFilter(pattern.buf); } // - If there is a listening port then add the address to the // tray icon's tool-tip text. { const TCHAR* prefix = isServiceProcess() ? _T("VNC Server (Service):") : _T("VNC Server (User):"); std::list<char*> addrs; if (rfb.sock) rfb.sock->getMyAddresses(&addrs); else addrs.push_front(strDup("Not accepting connections")); std::list<char*>::iterator i, next_i; int length = _tcslen(prefix)+1; for (i=addrs.begin(); i!= addrs.end(); i++) length += strlen(*i) + 1; TCharArray toolTip(length); _tcscpy(toolTip.buf, prefix); for (i=addrs.begin(); i!= addrs.end(); i=next_i) { next_i = i; next_i ++; TCharArray addr = *i; // Assumes ownership of string _tcscat(toolTip.buf, addr.buf); if (next_i != addrs.end()) _tcscat(toolTip.buf, _T(",")); } trayIcon.setToolTip(toolTip.buf); } vlog.debug("Entering message loop"); // - Run the server until the registry changes, or we're told to quit ///>进入socket监听,进入消息循环 while (sockMgr.getMessage(&msg, NULL, 0, 0)) { if (msg.hwnd == 0) { if (msg.message == VNCM_REG_CHANGED) break; if (msg.message == VNCM_COMMAND) doCommand(); } TranslateMessage(&msg); DispatchMessage(&msg); } } while ((msg.message != WM_QUIT) || runServer); vlog.debug("Server exited cleanly"); } catch (rdr::SystemException &s) { vlog.error(s.str()); result = s.err; } catch (rdr::Exception &e) { vlog.error(e.str()); } { Lock l(runLock); runServer = false; hostThread = 0; } return result; }(2、1)注册表设置,并向线程发送NCM_REG_CHANGED消息
if (isServiceProcess()) config.setKey(HKEY_LOCAL_MACHINE, RegConfigPath); else config.setKey(HKEY_CURRENT_USER, RegConfigPath); config.setNotifyThread(Thread::self(), VNCM_REG_CHANGED);发送的消息最后是在 “(2、4)进入一个while的操作,直到收到WM_QUIT后才退出” 中被收到的
(2、2)创建 tray icon
// - Create the tray icon if possible STrayIconThread trayIcon(*this, IDI_ICON, IDI_CONNECTED, IDR_TRAY);
创建时会调用STrayIconThread的构造函数:
STrayIconThread::STrayIconThread(VNCServerWin32& sm, UINT inactiveIcon_, UINT activeIcon_, UINT menu_) : server(sm), inactiveIcon(inactiveIcon_), activeIcon(activeIcon_), menu(menu_), windowHandle(0), runTrayIcon(true) { start(); }因为STrayIconThread继承自Thread,所以这里的start其实会调用到STrayIconThread::run()
void STrayIconThread::run() { while (runTrayIcon) { if (rfb::win32::desktopChangeRequired() && !rfb::win32::changeDesktop()) Sleep(2000); STrayIcon icon(*this); windowHandle = icon.getHandle(); MSG msg; while (runTrayIcon && ::GetMessage(&msg, 0, 0, 0) > 0) { TranslateMessage(&msg); DispatchMessage(&msg); } windowHandle = 0; } }(2、2、1)
if (rfb::win32::desktopChangeRequired() && !rfb::win32::changeDesktop()) Sleep(2000);
(2、2、2)创建STrayIcon,并获得它的窗口句柄。
STrayIcon icon(*this); windowHandle = icon.getHandle();STrayIcon继承自TrayIcon, TrayIcon继承自MsgWindow
class winvnc::STrayIcon : public TrayIcon
class TrayIcon : public MsgWindow
class MsgWindow { public: MsgWindow(const TCHAR* _name); virtual ~MsgWindow(); const TCHAR* getName() {return name.buf;} HWND getHandle() const {return handle;} virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam); protected: TCharArray name; HWND handle; };
创建STrayIcon时会调用到三个构造函数:
第一个构造函数:
MsgWindow的构造函数就是创建一个窗口,窗口名称为"VNCTray",大小为10*10;
MsgWindow::MsgWindow(const TCHAR* name_) : name(tstrDup(name_)), handle(0) { vlog.debug("creating window \"%s\"", (const char*)CStr(name.buf)); handle = CreateWindow((const TCHAR*)baseClass.classAtom, name.buf, WS_OVERLAPPED, 0, 0, 10, 10, 0, 0, baseClass.instance, this); if (!handle) { throw rdr::SystemException("unable to create WMNotifier window instance", GetLastError()); } vlog.debug("created window \"%s\" (%x)", (const char*)CStr(name.buf), handle); }
窗口类名为(const TCHAR*)baseClass.classAtom; _T("rfb::win32::MsgWindowClass")
baseClass是类MsgWindowClass,它用来管理窗口类。
MsgWindowClass::MsgWindowClass() : classAtom(0) { WNDCLASS wndClass; wndClass.style = 0; wndClass.lpfnWndProc = MsgWindowProc; wndClass.cbClsExtra = 0; wndClass.cbWndExtra = 0; wndClass.hInstance = instance = GetModuleHandle(0); wndClass.hIcon = 0; wndClass.hCursor = 0; wndClass.hbrBackground = 0; wndClass.lpszMenuName = 0; wndClass.lpszClassName = _T("rfb::win32::MsgWindowClass"); classAtom = RegisterClass(&wndClass); if (!classAtom) { throw rdr::SystemException("unable to register MsgWindow window class", GetLastError()); } }特别重要的是它的窗口消息处理函数MsgWindowProc。
如果消息是在创建窗口时,即CreateWindows时,会收到消息WM_CREATE,此时把(long)((CREATESTRUCT*)lParam)->lpCreateParams,即MsgWindow的this指针作为
窗口数据存起来。之后的MsgWindowsProc中对各消息的处理就是取出窗口中的数据,强转为MsgWindows后,调用它的processMessage 函数,
因为MsgWindows类中的processMessage成员是虚函数,所以如果继承类有去重载它,那么是只会调用了继承类的processMessage函数。如:
STrayIcon的成员processMessage.
LRESULT CALLBACK MsgWindowProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) { LRESULT result; if (msg == WM_CREATE) SetWindowLong(wnd, GWL_USERDATA, (long)((CREATESTRUCT*)lParam)->lpCreateParams); else if (msg == WM_DESTROY) SetWindowLong(wnd, GWL_USERDATA, 0); MsgWindow* _this = (MsgWindow*) GetWindowLong(wnd, GWL_USERDATA); if (!_this) { vlog.info("null _this in %x, message %x", wnd, msg); return SafeDefWindowProc(wnd, msg, wParam, lParam); } try { result = _this->processMessage(msg, wParam, lParam); } catch (rdr::Exception& e) { vlog.error("untrapped: %s", e.str()); } return result; };
第二个构造函数:
TrayIcon() : MsgWindow(_T("VNCTray")) { #ifdef NOTIFYICONDATA_V1_SIZE nid.cbSize = NOTIFYICONDATA_V1_SIZE; #else nid.cbSize = sizeof(NOTIFYICONDATA); #endif nid.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); else vlog.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 information STARTUPINFO sinfo; memset(&sinfo, 0, sizeof(sinfo)); sinfo.cb = sizeof(sinfo); // - Concoct a suitable command-line TCharArray 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 _DEBUG DWORD flags = CREATE_NEW_CONSOLE; #else DWORD flags = CREATE_NO_WINDOW; #endif BOOL success; if (userToken) success = CreateProcessAsUser(userToken, exePath.buf, cmdLine.buf, 0, 0, FALSE, flags, 0, 0, &sinfo, &procInfo); else success = 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 initialising WaitForInputIdle(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); }
(2、2、3)消息循环
MSG msg; while (runTrayIcon && ::GetMessage(&msg, 0, 0, 0) > 0) { TranslateMessage(&msg); DispatchMessage(&msg); }
(2、3)创建监听socket
ManagedListener rfb(&sockMgr, &vncServer); ManagedListener http(&sockMgr, httpServer);
(2、4)进入一个while的操作,直到收到WM_QUIT后才退出
MSG msg; do { // 第一件事:设置监听端口 // 第二件事:Update the Java viewer's web page port number // Update the TCP address filter for both ports, if open. // // 第三件事:如果有一个正在监听的端口,那么就增加地址到icon的tooltip(提示信息) // // - Run the server until the registry changes, or we're told to quit ///>进入socket监听,进入消息循环 while (sockMgr.getMessage(&msg, NULL, 0, 0)) { if (msg.hwnd == 0) { if (msg.message == VNCM_REG_CHANGED) break; if (msg.message == VNCM_COMMAND) doCommand(); } TranslateMessage(&msg); DispatchMessage(&msg); } } while ((msg.message != WM_QUIT) || runServer);
这里最重要的是 sockMgr.getMessage(&msg, NULL, 0, 0 函数!!!!!!!!
三、扩展知识
1、TLSAlloc()缩写为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)设置值
WINBASEAPI BOOL WINAPI TlsSetValue( __in DWORD dwTlsIndex, __in_opt LPVOID lpTlsValue );
DWORD WINAPI Thread::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)获得值
WINBASEAPI LPVOID WINAPI TlsGetValue( __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; }
相关文章推荐
- VNC源码研究(二十四)vnc-4.0-winsrc版本之winvnc工程分析
- vnc-4.0-winsrc版本之winvnc工程分析_源码研究
- VNC源码研究(二十四)vnc-4.0-winsrc版本之winvnc工程分析
- druid1.0.21版本源码研究之连接回收(分析解决mysql8小时断线)
- .NET/Rotor源码研究1补遗 - 解决无法检测操作系统版本的错误
- Android笔记1——开发环境搭建及工程导入、源码api版本修改
- block实现源码分析3-深入研究Block捕获外部变量和__block实现原理
- Android 4.0 Launcher源码分析系列(一)
- thrift源码研究-TProtocol类体系架构分析
- Android 4.0 Launcher2源码分析——Launcher内容加载详细过程
- Mysql源码分析最新的5.6版本-每日更新中......
- SpringMVC关于json、xml自动转换的原理研究[附带源码分析]
- Android 4.0日历(calendar)源码分析之月视图
- 转:Irrlicht 0.1引擎源码分析与研究(一)
- Activity的绘制流程简单分析(基于android 4.0源码进行分析)
- Tcpdump源码分析系列3:main函数继续研究
- Android 4.0 Launcher源码分析系列
- 嵌入式linux开发uboot移植(二)——uboot工程源码目录分析
- Android 4.0 Launcher2源码分析——导入eclipse进行调试【转】
- Android 4.0 Launcher2源码分析——Workspace切换AllApps流程