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

来源:互联网 发布:人机界面简单编程实例 编辑:程序博客网 时间:2024/05/01 09:47

此工程编译出winvnc4.exe二进制文件。它得到的二进制是作为VNC服务端来使用的。

一、从main说起

[cpp] view plaincopy
  1. int main(int argc, const char* argv[]) {  
  2.   int result = 0;  
  3.   
  4.   try {  
  5.     // - Initialise the available loggers  
  6.     //freopen("\\\\drupe\\tjr\\WinVNC4.log","ab",stderr);  
  7.     //setbuf(stderr, 0);  
  8.     initStdIOLoggers();  
  9.     initFileLogger("C:\\temp\\WinVNC4.log");  
  10.     rfb::win32::initEventLogLogger(VNCServerService::Name);  
  11.   
  12.     // - By default, just log errors to stderr  
  13.     logParams.setParam("*:stderr:0");  
  14.   
  15.     // - Print program details and process the command line  
  16.     programInfo();  
  17.     processParams(argc, argv);  
  18.   
  19.     // - Run the server if required  
  20.     if (runServer) {  
  21.       if (close_console) {  
  22.         vlog.info("closing console");  
  23.         if (!FreeConsole())  
  24.           vlog.info("unable to close console:%u", GetLastError());  
  25.       }  
  26.   
  27.       network::TcpSocket::initTcpSockets();  
  28.       VNCServerWin32 server;  
  29.   
  30.       if (runAsService) {  
  31.         printf("Starting Service-Mode VNC Server.\n");  
  32.         VNCServerService service(server);  
  33.         ///>启动服务  
  34.         service.start();  
  35.         result = service.getStatus().dwWin32ExitCode;  
  36.       } else {  
  37.         printf("Starting User-Mode VNC Server.\n");  
  38.         ///>启动server  
  39.         result = server.run();  
  40.       }  
  41.     }  
  42.   
  43.     vlog.debug("WinVNC service destroyed");  
  44.   } catch (rdr::Exception& e) {  
  45.     try {  
  46.       vlog.error("Fatal Error: %s", e.str());  
  47.     } catch (...) {  
  48.       fprintf(stderr, "WinVNC: Fatal Error: %s\n", e.str());  
  49.     }  
  50.     if (!runAsService)  
  51.       MsgBox(0, TStr(e.str()), MB_ICONERROR | MB_OK);  
  52.   }  
  53.   
  54.   vlog.debug("WinVNC process quitting");  
  55.   return result;  
  56. }  
步一、打印信息设置
步二、打印程序详细信息

programInfo();

步三、处理命令行

[cpp] view plaincopy
  1. processParams(argc, argv);  
[cpp] view plaincopy
  1. void  
  2. processParams(int argc, const char* argv[]) {  
  3.   for (int i=1; i<argc; i++) {  
  4.     try {  
  5.   
  6.       if (strcasecmp(argv[i], "-connect") == 0) {  
  7.         runServer = false;  
  8.         CharArray host;  
  9.         if (i+1 < argc) {  
  10.           host.buf = strDup(argv[i+1]);  
  11.         } else {  
  12.           AddNewClientDialog ancd;  
  13.           if (ancd.showDialog())  
  14.             host.buf = strDup(ancd.getHostName());  
  15.         }  
  16.         if (host.buf) {  
  17.           HWND hwnd = FindWindow(0, _T("winvnc::IPC_Interface"));  
  18.           COPYDATASTRUCT copyData;  
  19.           copyData.dwData = 1; // *** AddNewClient  
  20.           copyData.cbData = strlen(host.buf);  
  21.           copyData.lpData = (void*)host.buf;  
  22.           i++;  
  23.           SendMessage(hwnd, WM_COPYDATA, 0, (LPARAM)&copyData);  
  24.           printf("Sent connect request to VNC Server...\n");  
  25.         }  
  26.       } else if (strcasecmp(argv[i], "-disconnect") == 0) {  
  27.         HWND hwnd = FindWindow(0, _T("winvnc::IPC_Interface"));  
  28.         COPYDATASTRUCT copyData;  
  29.         copyData.dwData = 2; // *** DisconnectClients  
  30.         copyData.lpData = 0;  
  31.         copyData.cbData = 0;  
  32.         SendMessage(hwnd, WM_COPYDATA, 0, (LPARAM)©Data);  
  33.         printf("Sent disconnect request to VNC Server...\n");  
  34.         runServer = false;  
  35.       } else if (strcasecmp(argv[i], "-start") == 0) {  
  36.         printf("Attempting to start service...\n");  
  37.         runServer = false;  
  38.         if (rfb::win32::startService(VNCServerService::Name))  
  39.           printf("Started service successfully\n");  
  40.       } else if (strcasecmp(argv[i], "-stop") == 0) {  
  41.         printf("Attempting to stop service...\n");  
  42.         runServer = false;  
  43.         if (rfb::win32::stopService(VNCServerService::Name))  
  44.           printf("Stopped service successfully\n");  
  45.       } else if (strcasecmp(argv[i], "-status") == 0) {  
  46.         printf("Querying service status...\n");  
  47.         runServer = false;  
  48.         rfb::win32::printServiceStatus(VNCServerService::Name);  
  49.   
  50.       } else if (strcasecmp(argv[i], "-service") == 0) {  
  51.         printf("Run in service mode\n");  
  52.         runAsService = true;  
  53.   
  54.       } else if (strcasecmp(argv[i], "-register") == 0) {  
  55.         printf("Attempting to register service...\n");  
  56.         runServer = false;  
  57.         int j = i;  
  58.         i = argc;  
  59.         if (rfb::win32::registerService(VNCServerService::Name,  
  60.                                         _T("VNC Server Version 4"),  
  61.                                         argc-(j+1), &argv[j+1]))  
  62.           printf("Registered service successfully\n");  
  63.       } else if (strcasecmp(argv[i], "-unregister") == 0) {  
  64.         printf("Attempting to unregister service...\n");  
  65.         runServer = false;  
  66.         if (rfb::win32::unregisterService(VNCServerService::Name))  
  67.           printf("Unregistered service successfully\n");  
  68.   
  69.       } else if (strcasecmp(argv[i], "-noconsole") == 0) {  
  70.         close_console = true;  
  71.   
  72.       } else if ((strcasecmp(argv[i], "-help") == 0) ||  
  73.         (strcasecmp(argv[i], "--help") == 0) ||  
  74.         (strcasecmp(argv[i], "-h") == 0) ||  
  75.         (strcasecmp(argv[i], "/?") == 0)) {  
  76.         runServer = false;  
  77.         programUsage();  
  78.         break;  
  79.   
  80.       } else {  
  81.         // Try to process <option>=<value>, or -<bool>  
  82.         if (Configuration::setParam(argv[i], true))  
  83.           continue;  
  84.         // Try to process -<option> <value>  
  85.         if ((argv[i][0] == '-') && (i+1 < argc)) {  
  86.           if (Configuration::setParam(&argv[i][1], argv[i+1], true)) {  
  87.             i++;  
  88.             continue;  
  89.           }  
  90.         }  
  91.         // Nope.  Show them usage and don't run the server  
  92.         runServer = false;  
  93.         programUsage();  
  94.         break;  
  95.       }  
  96.   
  97.     } catch (rdr::Exception& e) {  
  98.       vlog.error(e.str());  
  99.     }  
  100.   }  
  101. }  
(1)从命令行中提取出IP地址串,并组成把它放到COPYDATASTRUCT中。

找到窗口_T("winvnc::IPC_Interface"),将得到的COPYDATASTRUCT数据发送到窗口中。

因为在服务端此窗口的创建是在  “第一个构造函数:”,  而此时窗口一定是找不到的,那么还有一种情况可以找到就是VNC服务端与VNC客户端并存时就会出现。

这是因为VNC客户端也会创建此窗口。

那么可以说,这时的发送WM_COPYDATA其实是发送给客户端的

[cpp] view plaincopy
  1. if (host.buf) {  
  2.          HWND hwnd = FindWindow(0, _T("winvnc::IPC_Interface"));  
  3.          COPYDATASTRUCT copyData;  
  4.          copyData.dwData = 1; // *** AddNewClient  
  5.          copyData.cbData = strlen(host.buf);  
  6.          copyData.lpData = (void*)host.buf;  
  7.          i++;  
  8.          SendMessage(hwnd, WM_COPYDATA, 0, (LPARAM)©Data);  
  9.          printf("Sent connect request to VNC Server...\n");  
  10.        }  


针对此WM_COPYDATA的响应是:






(2)

(3)

(4)

(5)

(6)

(7)


步四、启动服务

有两种方式可以选择:服务方式、用户模式(!!!!!!!!最重要)

[cpp] view plaincopy
  1. if (runAsService) {  
  2.         printf("Starting Service-Mode VNC Server.\n");  
  3.         VNCServerService service(server);  
  4.         ///>启动服务  
  5.         service.start();  
  6.         result = service.getStatus().dwWin32ExitCode;  
  7.       } else {  
  8.         printf("Starting User-Mode VNC Server.\n");  
  9.         ///>启动server  
  10.         result = server.run();  
  11.       }  
启动时得保证  runServer ==  true;

runServer的初始值是static bool runServer = true;,当我们有指定命令行时,此变量会变为false。

如在 “步三、处理命令行"  ,命令行是“-connect”、"-disconnect"、“-start”、"-stop"、"-status"、"-register"、"-unregister"、"-help"

意思是如果指定了命令行,那么是不会启动服务的。

如果是要关闭console界面那么调用系统API   

[cpp] view plaincopy
  1. if (!FreeConsole())  
来实现

(1)以服务方式启动客户端

[cpp] view plaincopy
  1. VNCServerWin32 server;  
  2.   
  3.       if (runAsService) {  
  4.         printf("Starting Service-Mode VNC Server.\n");  
  5.         VNCServerService service(server);  
  6.         ///>启动服务  
  7.         service.start();  
  8.         result = service.getStatus().dwWin32ExitCode;  


(2)以用户模式启动客户端服务

VNCServerWin32 server;

[cpp] view plaincopy
  1. result = server.run();  
[cpp] view plaincopy
  1. ///>服务器启动  
  2. int VNCServerWin32::run() {  
  3.   { Lock l(runLock);  
  4.     hostThread = Thread::self();  
  5.     runServer = true;  
  6.   }  
  7.   
  8.   // - Register for notification of configuration changes  
  9.   ///>注册表设置  
  10.   if (isServiceProcess())  
  11.     config.setKey(HKEY_LOCAL_MACHINE, RegConfigPath);  
  12.   else  
  13.     config.setKey(HKEY_CURRENT_USER, RegConfigPath);  
  14.   config.setNotifyThread(Thread::self(), VNCM_REG_CHANGED);  
  15.   
  16.   // - Create the tray icon if possible  
  17.   STrayIconThread trayIcon(*this, IDI_ICON, IDI_CONNECTED, IDR_TRAY);  
  18.   
  19.   DWORD result = 0;  
  20.   try {  
  21.     // - Create some managed listening sockets  
  22.       ///>创建监听socket  
  23.     ManagedListener rfb(&sockMgr, &vncServer);  
  24.     ManagedListener http(&sockMgr, httpServer);  
  25.   
  26.     // - Continue to operate until WM_QUIT is processed  
  27.     MSG msg;  
  28.     do {  
  29.       // -=- Make sure we're listening on the right ports.  
  30.         ///>设置监听端口  
  31.       rfb.setPort(port_number, localHost);  
  32.       http.setPort(http_port, localHost);  
  33.   
  34.       // -=- Update the Java viewer's web page port number.  
  35.       httpServer->setRFBport(rfb.sock ? port_number : 0);  
  36.   
  37.       // -=- Update the TCP address filter for both ports, if open.  
  38.       CharArray pattern;  
  39.       pattern.buf = hosts.getData();  
  40.       if (!localHost) {  
  41.         rfb.setFilter(pattern.buf);  
  42.         http.setFilter(pattern.buf);  
  43.       }  
  44.   
  45.       // - If there is a listening port then add the address to the  
  46.       //   tray icon's tool-tip text.  
  47.       {  
  48.         const TCHAR* prefix = isServiceProcess() ?  
  49.           _T("VNC Server (Service):") : _T("VNC Server (User):");  
  50.   
  51.         std::list<char*> addrs;  
  52.         if (rfb.sock)  
  53.           rfb.sock->getMyAddresses(&addrs);  
  54.         else  
  55.           addrs.push_front(strDup("Not accepting connections"));  
  56.   
  57.         std::list<char*>::iterator i, next_i;  
  58.         int length = _tcslen(prefix)+1;  
  59.         for (i=addrs.begin(); i!= addrs.end(); i++)  
  60.           length += strlen(*i) + 1;  
  61.   
  62.         TCharArray toolTip(length);  
  63.         _tcscpy(toolTip.buf, prefix);  
  64.         for (i=addrs.begin(); i!= addrs.end(); i=next_i) {  
  65.           next_i = i; next_i ++;  
  66.           TCharArray addr = *i;    // Assumes ownership of string  
  67.           _tcscat(toolTip.buf, addr.buf);  
  68.           if (next_i != addrs.end())  
  69.             _tcscat(toolTip.buf, _T(","));  
  70.         }  
  71.         trayIcon.setToolTip(toolTip.buf);  
  72.       }  
  73.   
  74.       vlog.debug("Entering message loop");  
  75.   
  76.       // - Run the server until the registry changes, or we're told to quit  
  77.       ///>进入socket监听,进入消息循环  
  78.       while (sockMgr.getMessage(&msg, NULL, 0, 0)) {  
  79.         if (msg.hwnd == 0) {  
  80.           if (msg.message == VNCM_REG_CHANGED)  
  81.             break;  
  82.           if (msg.message == VNCM_COMMAND)  
  83.             doCommand();  
  84.         }  
  85.         TranslateMessage(&msg);  
  86.         DispatchMessage(&msg);  
  87.       }  
  88.   
  89.     } while ((msg.message != WM_QUIT) || runServer);  
  90.   
  91.     vlog.debug("Server exited cleanly");  
  92.   } catch (rdr::SystemException &s) {  
  93.     vlog.error(s.str());  
  94.     result = s.err;  
  95.   } catch (rdr::Exception &e) {  
  96.     vlog.error(e.str());  
  97.   }  
  98.   
  99.   { Lock l(runLock);  
  100.     runServer = false;  
  101.     hostThread = 0;  
  102.   }  
  103.   
  104.   return result;  
  105. }  
(2、1)注册表设置,并向线程发送NCM_REG_CHANGED消息

[cpp] view plaincopy
  1. if (isServiceProcess())  
  2.    config.setKey(HKEY_LOCAL_MACHINE, RegConfigPath);  
  3.  else  
  4.    config.setKey(HKEY_CURRENT_USER, RegConfigPath);  
  5.  config.setNotifyThread(Thread::self(), VNCM_REG_CHANGED);  
发送的消息最后是在  “(2、4)进入一个while的操作,直到收到WM_QUIT后才退出”   中被收到的

(2、2)创建 tray icon

[cpp] view plaincopy
  1. // - Create the tray icon if possible  
  2.  STrayIconThread trayIcon(*this, IDI_ICON, IDI_CONNECTED, IDR_TRAY);  


创建时会调用STrayIconThread的构造函数:

[cpp] view plaincopy
  1. STrayIconThread::STrayIconThread(VNCServerWin32& sm, UINT inactiveIcon_, UINT activeIcon_, UINT menu_)  
  2. : server(sm), inactiveIcon(inactiveIcon_), activeIcon(activeIcon_), menu(menu_),  
  3.   windowHandle(0), runTrayIcon(true) {  
  4.   start();  
  5. }  
因为STrayIconThread继承自Thread,所以这里的start其实会调用到STrayIconThread::run()

[cpp] view plaincopy
  1. void STrayIconThread::run() {  
  2.   while (runTrayIcon) {  
  3.     if (rfb::win32::desktopChangeRequired() &&   
  4.       !rfb::win32::changeDesktop())  
  5.       Sleep(2000);  
  6.   
  7.     STrayIcon icon(*this);  
  8.     windowHandle = icon.getHandle();  
  9.   
  10.     MSG msg;  
  11.     while (runTrayIcon && ::GetMessage(&msg, 0, 0, 0) > 0) {  
  12.       TranslateMessage(&msg);  
  13.       DispatchMessage(&msg);  
  14.     }  
  15.   
  16.     windowHandle = 0;  
  17.   }  
  18. }  
(2、2、1)

[cpp] view plaincopy
  1. if (rfb::win32::desktopChangeRequired() &&   
  2.             !rfb::win32::changeDesktop())  
  3.             Sleep(2000);  


(2、2、2)创建STrayIcon,并获得它的窗口句柄。

[cpp] view plaincopy
  1. STrayIcon icon(*this);  
  2.         windowHandle = icon.getHandle();  
STrayIcon继承自TrayIcon, TrayIcon继承自MsgWindow 

[cpp] view plaincopy
  1. class winvnc::STrayIcon : public TrayIcon  
[cpp] view plaincopy
  1. class TrayIcon : public MsgWindow  
[cpp] view plaincopy
  1. class MsgWindow {  
  2.    public:  
  3.      MsgWindow(const TCHAR* _name);  
  4.      virtual ~MsgWindow();  
  5.   
  6.      const TCHAR* getName() {return name.buf;}  
  7.      HWND getHandle() const {return handle;}  
  8.   
  9.      virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam);  
  10.   
  11.    protected:  
  12.      TCharArray name;  
  13.      HWND handle;  
  14.    };  

创建STrayIcon时会调用到三个构造函数:

第一个构造函数:

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



[cpp] view plaincopy
  1. MsgWindow::MsgWindow(const TCHAR* name_) : name(tstrDup(name_)), handle(0) {  
  2.   vlog.debug("creating window \"%s\"", (const char*)CStr(name.buf));  
  3.   handle = CreateWindow((const TCHAR*)baseClass.classAtom, name.buf, WS_OVERLAPPED,  
  4.     0, 0, 10, 10, 0, 0, baseClass.instance, this);  
  5.   if (!handle) {  
  6.     throw rdr::SystemException("unable to create WMNotifier window instance", GetLastError());  
  7.   }  
  8.   vlog.debug("created window \"%s\" (%x)", (const char*)CStr(name.buf), handle);  
  9. }  

窗口类名为(const TCHAR*)baseClass.classAtom;    _T("rfb::win32::MsgWindowClass")

baseClass是类MsgWindowClass,它用来管理窗口类。

[cpp] view plaincopy
  1. MsgWindowClass::MsgWindowClass() : classAtom(0) {  
  2.   WNDCLASS wndClass;  
  3.   wndClass.style = 0;  
  4.   wndClass.lpfnWndProc = MsgWindowProc;  
  5.   wndClass.cbClsExtra = 0;  
  6.   wndClass.cbWndExtra = 0;  
  7.   wndClass.hInstance = instance = GetModuleHandle(0);  
  8.   wndClass.hIcon = 0;  
  9.   wndClass.hCursor = 0;  
  10.   wndClass.hbrBackground = 0;  
  11.   wndClass.lpszMenuName = 0;  
  12.   wndClass.lpszClassName = _T("rfb::win32::MsgWindowClass");  
  13.   classAtom = RegisterClass(&wndClass);  
  14.   if (!classAtom) {  
  15.     throw rdr::SystemException("unable to register MsgWindow window class", GetLastError());  
  16.   }  
  17. }  
特别重要的是它的窗口消息处理函数MsgWindowProc。

如果消息是在创建窗口时,即CreateWindows时,会收到消息WM_CREATE,此时把(long)((CREATESTRUCT*)lParam)->lpCreateParams,即MsgWindow的this指针作为

窗口数据存起来。之后的MsgWindowsProc中对各消息的处理就是取出窗口中的数据,强转为MsgWindows后,调用它的processMessage 函数,

因为MsgWindows类中的processMessage成员是虚函数,所以如果继承类有去重载它,那么是只会调用了继承类的processMessage函数。如:
STrayIcon的成员processMessage.


[cpp] view plaincopy
  1. LRESULT CALLBACK MsgWindowProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) {  
  2.   LRESULT result;  
  3.   
  4.   if (msg == WM_CREATE)  
  5.     SetWindowLong(wnd, GWL_USERDATA, (long)((CREATESTRUCT*)lParam)->lpCreateParams);  
  6.   else if (msg == WM_DESTROY)  
  7.     SetWindowLong(wnd, GWL_USERDATA, 0);  
  8.   MsgWindow* _this = (MsgWindow*) GetWindowLong(wnd, GWL_USERDATA);  
  9.   if (!_this) {  
  10.     vlog.info("null _this in %x, message %x", wnd, msg);  
  11.     return SafeDefWindowProc(wnd, msg, wParam, lParam);  
  12.   }  
  13.   
  14.   try {  
  15.     result = _this->processMessage(msg, wParam, lParam);  
  16.   } catch (rdr::Exception& e) {  
  17.     vlog.error("untrapped: %s", e.str());  
  18.   }  
  19.   
  20.   return result;  
  21. };  


第二个构造函数:

[cpp] view plaincopy
  1. TrayIcon() : MsgWindow(_T("VNCTray")) {  
  2. fdef NOTIFYICONDATA_V1_SIZE  
  3.       nid.cbSize = NOTIFYICONDATA_V1_SIZE;  
  4. lse  
  5.       nid.cbSize = sizeof(NOTIFYICONDATA);  
  6. ndif  
  7.   
  8.       nid.hWnd = getHandle();  
  9.       nid.uID = 0;  
  10.       nid.hIcon = 0;  
  11.       nid.uFlags = NIF_ICON | NIF_MESSAGE;  
  12.       nid.uCallbackMessage = WM_USER;  
  13.     }  


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

[cpp] view plaincopy
  1. STrayIcon(STrayIconThread& t) : thread(t),  
  2.     vncConfig(_T("vncconfig.exe"), isServiceProcess() ? _T("-noconsole -service") : _T("-noconsole")),  
  3.     vncConnect(_T("winvnc4.exe"), _T("-connect")) {  
  4.   
  5.     // ***  
  6.     SetWindowText(getHandle(), _T("winvnc::IPC_Interface"));  
  7.     // ***  
  8.   
  9.     SetTimer(getHandle(), 1, 3000, 0);  
  10.     PostMessage(getHandle(), WM_TIMER, 1, 0);  
  11.     PostMessage(getHandle(), WM_SET_TOOLTIP, 0, 0);  
  12.   }  

在STrayIcon的构造函数中:

首先构造两对象

[cpp] view plaincopy
  1. LaunchProcess vncConfig;  
  2. LaunchProcess vncConnect;  
作用:在窗口收到消息WM_COMMAND,命令为ID_CONNECT时就连接服务端:

[cpp] view plaincopy
  1. CurrentUserToken token;  
  2.           if (token.isValid())  
  3.             vncConnect.start(isServiceProcess() ? (HANDLE)token : 0);  
  4.           else  
  5.             vlog.error("Options: unknown current user");  
[cpp] view plaincopy
  1. void LaunchProcess::start(HANDLE userToken) {  
  2.   if (procInfo.hProcess && (WaitForSingleObject(procInfo.hProcess, 0) != WAIT_OBJECT_0))  
  3.     return;  
  4.   await();  
  5.   
  6.   // - Create storage for the process startup information  
  7.   STARTUPINFO sinfo;  
  8.   memset(&sinfo, 0, sizeof(sinfo));  
  9.   sinfo.cb = sizeof(sinfo);  
  10.   
  11.   // - Concoct a suitable command-line  
  12.   TCharArray exePath;  
  13.   if (!tstrContains(exeName.buf, _T('\\'))) {  
  14.     ModuleFileName filename;  
  15.     TCharArray path; splitPath(filename.buf, &path.buf, 0);  
  16.     exePath.buf = new TCHAR[_tcslen(path.buf) + _tcslen(exeName.buf) + 2];  
  17.     _stprintf(exePath.buf, _T("%s\\%s"), path.buf, exeName.buf);  
  18.   } else {  
  19.     exePath.buf = tstrDup(exeName.buf);  
  20.   }  
  21.   
  22.   // - Start the VNC server  
  23.   // Note: We specify the exe's precise path in the ApplicationName parameter,  
  24.   //       AND include the name as the first part of the CommandLine parameter,  
  25.   //       because CreateProcess doesn't make ApplicationName argv[0] in C programs.  
  26.   TCharArray cmdLine(_tcslen(exeName.buf) + 3 + _tcslen(params.buf) + 1);  
  27.   _stprintf(cmdLine.buf, _T("\"%s\" %s"), exeName.buf, params.buf);  
  28. #ifdef _DEBUG  
  29.   DWORD flags = CREATE_NEW_CONSOLE;  
  30. #else  
  31.   DWORD flags = CREATE_NO_WINDOW;  
  32. #endif  
  33.   BOOL success;  
  34.   if (userToken)  
  35.     success = CreateProcessAsUser(userToken, exePath.buf, cmdLine.buf, 0, 0, FALSE, flags, 0, 0, &sinfo, &procInfo);  
  36.   else  
  37.     success = CreateProcess(exePath.buf, cmdLine.buf, 0, 0, FALSE, flags, 0, 0, &sinfo, &procInfo);  
  38.   if (!success)  
  39.     throw rdr::SystemException("unable to launch process", GetLastError());  
  40.   
  41.   // Wait for it to finish initialising  
  42.   WaitForInputIdle(procInfo.hProcess, 15000);  
  43. }  



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

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

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

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

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

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

[cpp] view plaincopy
  1. if (rfb::win32::desktopChangeRequired()) {  
  2.        SendMessage(getHandle(), WM_CLOSE, 0, 0);  
  3.        return 0;  
  4.      }  
  5.      setIcon(thread.server.isServerInUse() ? thread.activeIcon : thread.inactiveIcon);  
  6.      return 0;  
WM_SET_TOOLTIP用于设置”工具提示信息“

[cpp] view plaincopy
  1. case WM_SET_TOOLTIP:  
  2.       {  
  3.         rfb::Lock l(thread.lock);  
  4.         if (thread.toolTip.buf)  
  5.           setToolTip(thread.toolTip.buf);  
  6.       }  







(2、2、3)消息循环

[cpp] view plaincopy
  1. MSG msg;  
  2.         while (runTrayIcon && ::GetMessage(&msg, 0, 0, 0) > 0) {  
  3.             TranslateMessage(&msg);  
  4.             DispatchMessage(&msg);  
  5.         }  

(2、3)创建监听socket

[cpp] view plaincopy
  1. ManagedListener rfb(&sockMgr, &vncServer);  
  2.    ManagedListener http(&sockMgr, httpServer);  

(2、4)进入一个while的操作,直到收到WM_QUIT后才退出

[cpp] view plaincopy
  1. MSG msg;  
  2. do {  
  3.       // 第一件事:设置监听端口  
  4.       // 第二件事:Update the Java viewer's web page port number  
  5.       //           Update the TCP address filter for both ports, if open.  
  6.       //  
  7.       // 第三件事:如果有一个正在监听的端口,那么就增加地址到icon的tooltip(提示信息)  
  8.       //  
  9.       // - Run the server until the registry changes, or we're told to quit  
  10.       ///>进入socket监听,进入消息循环  
  11.       while (sockMgr.getMessage(&msg, NULL, 0, 0)) {  
  12.         if (msg.hwnd == 0) {  
  13.           if (msg.message == VNCM_REG_CHANGED)  
  14.             break;  
  15.           if (msg.message == VNCM_COMMAND)  
  16.             doCommand();  
  17.         }  
  18.         TranslateMessage(&msg);  
  19.         DispatchMessage(&msg);  
  20.       }  
  21. 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)定义
[cpp] view plaincopy
  1. static DWORD threadStorage = TlsAlloc();  
为一个TLS数组索引变量与这个线程的TLS数组的某个槽(slot)关联起来,例如获得一个索引变量:
(2)设置值
 
[cpp] view plaincopy
  1. WINBASEAPI  
  2. BOOL  
  3. WINAPI  
  4. TlsSetValue(  
  5.     __in     DWORD dwTlsIndex,  
  6.     __in_opt LPVOID lpTlsValue  
  7.     );  

[cpp] view plaincopy
  1. DWORD WINAPI  
  2. Thread::threadProc(LPVOID lpParameter) {  
  3.   Thread* thread = (Thread*) lpParameter;  
  4.  <span style="color:#ff0000;"> TlsSetValue(threadStorage, thread);</span>  
  5.   logAction(thread"started");  
  6.   try {  
  7.     thread->run();  
  8.     logAction(thread"stopped");  
  9.   } catch (rdr::Exception& e) {  
  10.     logError(thread, e.str());  
  11.   }  
  12.   bool deleteThread = false;  
  13.   {  
  14.     Lock l(thread->mutex);  
  15.     thread->state = ThreadStopped;  
  16.     thread->sig->signal();  
  17.     deleteThread = thread->deleteAfterRun;  
  18.   }  
  19.   if (deleteThread)  
  20.     delete thread;  
  21.   return 0;  
  22. }  

(3)获得值
[cpp] view plaincopy
  1. WINBASEAPI  
  2. LPVOID  
  3. WINAPI  
  4. TlsGetValue(  
  5.     __in DWORD dwTlsIndex  
  6.     );  


[cpp] view plaincopy
  1. Thread*  
  2. Thread::self() {  
  3.   <span style="color:#ff0000;">Thread* thread = (Thread*) TlsGetValue(threadStorage);</span>  
  4.   if (!thread) {  
  5.     thread = new Thread(GetCurrentThread(), GetCurrentThreadId());  
  6.    <span style="color:#cc0000;"> TlsSetValue(threadStorage, thread);</span>  
  7.   }  
  8.   return thread;  
  9. }  





2、__declspec(thread)的使用



[cpp] view plaincopy
  1. #include <stdio.h>  
  2. #include <assert.h>  
  3.   
  4. // 这就是两个线程都要访问的变量  
  5. __declspec(threadint g_nData = 0;  
  6.   
  7.   
  8.   
  9.   
  10. DWORD WINAPI ThreadProc(LPVOID lpParameter)  
  11. {  
  12.     g_nData = 5;  
  13.     // 辅线程睡眠100ms,保证主线程的g_nData = 10;语句执行成功  
  14.     Sleep(100);  
  15.     TCHAR szMsg[100] = {0};  
  16.     wsprintf(szMsg, L"Auxi thread, g_nData:%d\n", g_nData);  
  17.     MessageBox(NULL, szMsg, L"AuxiThread", MB_ICONINFORMATION);  
  18.     return 0;  
  19. }  
  20.   
  21.   
  22. int main()  
  23. {  
  24.     DWORD dwId;  
  25.     // 创建线程,并立即启动它  
  26.     HANDLE hThread = CreateThread(NULL, 1024, ThreadProc, NULL, 0, &dwId);  
  27.     assert(hThread);  
  28.     // 主线程睡50ms,保证辅线程的g_nData = 5语句执行成功。  
  29.     Sleep(50);  
  30.     g_nData = 10;  
  31.       
  32.     TCHAR szMsg[100] = {0};  
  33.     wsprintf(szMsg, L"Result %d\n", g_nData);  
  34.     MessageBox(NULL, szMsg, L"MainThread", MB_ICONINFORMATION);  
  35.   
  36.     return 0;  
  37. }  



0 0
原创粉丝点击