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

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

一、从main说起

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

步一、打印信息设置
步二、打印程序详细信息

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

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

(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. STrayIcon icon(*this);
  7. windowHandle = icon.getHandle();
  8. MSG msg;
  9. while (runTrayIcon && ::GetMessage(&msg, 0, 0, 0) > 0) {
  10. TranslateMessage(&msg);
  11. DispatchMessage(&msg);
  12. }
  13. windowHandle = 0;
  14. }
  15. }

(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. const TCHAR* getName() {return name.buf;}
  6. HWND getHandle() const {return handle;}
  7. virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam);
  8. protected:
  9. TCharArray name;
  10. HWND handle;
  11. };

创建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. if (msg == WM_CREATE)
  4. SetWindowLong(wnd, GWL_USERDATA, (long)((CREATESTRUCT*)lParam)->lpCreateParams);
  5. else if (msg == WM_DESTROY)
  6. SetWindowLong(wnd, GWL_USERDATA, 0);
  7. MsgWindow* _this = (MsgWindow*) GetWindowLong(wnd, GWL_USERDATA);
  8. if (!_this) {
  9. vlog.info("null _this in %x, message %x", wnd, msg);
  10. return SafeDefWindowProc(wnd, msg, wParam, lParam);
  11. }
  12. try {
  13. result = _this->processMessage(msg, wParam, lParam);
  14. } catch (rdr::Exception& e) {
  15. vlog.error("untrapped: %s", e.str());
  16. }
  17. return result;
  18. };

第二个构造函数:

[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. nid.hWnd = getHandle();
  8. nid.uID = 0;
  9. nid.hIcon = 0;
  10. nid.uFlags = NIF_ICON | NIF_MESSAGE;
  11. nid.uCallbackMessage = WM_USER;
  12. }

第三个构造函数: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. SetWindowText(getHandle(), _T("winvnc::IPC_Interface"));
  6. // ***
  7. SetTimer(getHandle(), 1, 3000, 0);
  8. PostMessage(getHandle(), WM_TIMER, 1, 0);
  9. PostMessage(getHandle(), WM_SET_TOOLTIP, 0, 0);
  10. }

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

然后,设置窗口名称为_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. __declspec(thread) int g_nData = 0;
  5. DWORD WINAPI ThreadProc(LPVOID lpParameter)
  6. {
  7. g_nData = 5;
  8. // 辅线程睡眠100ms,保证主线程的g_nData = 10;语句执行成功
  9. Sleep(100);
  10. TCHAR szMsg[100] = {0};
  11. wsprintf(szMsg, L"Auxi thread, g_nData:%d\n", g_nData);
  12. MessageBox(NULL, szMsg, L"AuxiThread", MB_ICONINFORMATION);
  13. return 0;
  14. }
  15. int main()
  16. {
  17. DWORD dwId;
  18. // 创建线程,并立即启动它
  19. HANDLE hThread = CreateThread(NULL, 1024, ThreadProc, NULL, 0, &dwId);
  20. assert(hThread);
  21. // 主线程睡50ms,保证辅线程的g_nData = 5语句执行成功。
  22. Sleep(50);
  23. g_nData = 10;
  24. TCHAR szMsg[100] = {0};
  25. wsprintf(szMsg, L"Result %d\n", g_nData);
  26. MessageBox(NULL, szMsg, L"MainThread", MB_ICONINFORMATION);
  27. return 0;
  28. }

转载: http://blog.csdn.net/chenyujing1234    

vnc-4.0-winsrc版本之winvnc工程分析_源码研究相关推荐

  1. 【MyBatis使用】mapper.xml 文件内<if test>标签判断参数值不等于null和空 当参数值为 0 时筛选条件失效原因分析(源码探究)

    这个问题有不少小伙伴遇到过,也给出了解决方案,但是没有探究原因,这次读一下源码,看看原因在哪里. 1. 条件失效情况复现 Mapper.xml内的动态SQL如下[伪代码] <select id= ...

  2. Python 3.10版本及其依赖项 Linux下源码编译 安装到指定路径/目录

    Python 3.10版本及其依赖项 Linux下源码编译 安装到指定路径/目录 安装需求 准备工作 Python及其依赖项 libffi glibc GDBM mpdecimal bz2 xz re ...

  3. java计算机毕业设计建筑公司工程信息管理系统源码+mysql数据库+系统+lw文档+部署

    java计算机毕业设计建筑公司工程信息管理系统源码+mysql数据库+系统+lw文档+部署 java计算机毕业设计建筑公司工程信息管理系统源码+mysql数据库+系统+lw文档+部署 本源码技术栈: ...

  4. 各个版本spring的jar包以及源码下载地址

    各个版本spring的jar包以及源码下载地址,目前最高版本到spring4.1.2,留存备用: http://maven.springframework.org/release/org/spring ...

  5. tinkphp1.0贺岁版小程序应用平台系统源码

    介绍: tinkphp1.0贺岁版小程序应用平台系统源码 安装说明:直接放入服务器或者空间,访问域名根据安装向导进行安装. 程序魅力:此程序是类似微信小程序一样的机制系统,但不是微信小程序,跟微信不搭 ...

  6. 后台版本趣味测试威信小程序源码下载支持自定义问题等等

    这是一款有后台版本的趣味测试小程序 支持用户自定义添加和删除问题 支持用户个人中心等等 该程序是微擎框架的,所以后台需要有微擎 PS:该小程序的登录接口并未替换更新还是采用的旧版登录接口所以登录只会显 ...

  7. 小程序源码:后台版本趣味测试微信小程序源码下载支持自定义问题等等

    这是一款有后台版本的趣味测试小程序 支持用户自定义添加和删除问题 支持流量主后台设置 支持用户个人中心等等 该程序是微擎框架的,所以后台需要有微擎 PS:该小程序的登录接口并未替换更新还是采用的旧版登 ...

  8. 从0到1项目搭建-框架搭建(附源码)

    前言 大家好,本文是基于 SpringBoot 从0搭建一个企业级开发项目,基于SpringBoot 的项目,并集成MyBatis-Plus.Druid.Logback 等主流技术.希望对大家有所帮助 ...

  9. opencv4.0在linux下编译,Ubuntu 18.04源码编译安装OpenCV 4.0步骤

    Ubuntu 18.04下标准常规安装方法安装的OpenCV版本比较低,想尝鲜使用4.0版本,只好源码安装. 安装环境 OS:Ubuntu 18.04 64 bit 显卡:NVidia GTX 108 ...

  10. abp vnext2.0核心组件之DDD组件之实体结构源码解析

    接着abp vnext2.0核心组件之模块加载组件源码解析和abp vnext2.0核心组件之.Net Core默认DI组件切换到AutoFac源码解析集合.Net Core3.1,基本环境已经完备, ...

最新文章

  1. 4GL之Non-SCROLLING CURSOR
  2. python socket编程
  3. android中的Json一
  4. 【Android View绘制之旅】Measure过程
  5. SAP ERP物料和SAP Cloud for Customer的同步
  6. [html] H5的video可以播放哪些类型的文件?可以播放rtsp流吗?
  7. Core Data系列三——基本使用
  8. 代码注释: (文字图案:HIRE)
  9. stk在计算机仿真中的应用_学习电路仿真:proteus电路仿真软件在ARM中的应用解析...
  10. BLIP:用更干净更多样的数据进行多模态预训练,性能超越CLIP!代码已开源!...
  11. CSS中调用JS函数和变量
  12. iptables说明(转)
  13. php mvc 路由,PHP MVC框架路由学习笔记
  14. Explaining and Harnessing Adversarial Examples论文解读
  15. python模拟三次输入密码_python 3.0 模拟用户登录功能并实现三次错误锁定
  16. 终极成语接龙,谁能继续往下接,哈哈!!!
  17. 2021-08-18 HarmonyOS实战 CommonDialog的使用
  18. word中如何单独修改某一页页眉
  19. GitHub无法访问下载
  20. 多项目同时进行如何做好进度管理

热门文章

  1. windows 修改密码 在计算机管理中,管理员如何在Windows 10中更改其他用户的密码...
  2. 今日份bug,点击win10任务栏视窗动态壁纸消失的bug,暂未发现解决方法。
  3. electron 修改修改应用默认图标
  4. 谈谈创业这点事(3)
  5. 最新Java后端面经合集 | 阿里腾讯百度字节
  6. Linux command – Stressful Application Test
  7. 龙果支付 mysql_龙果开源支付系统搭建与部署
  8. 让孩子喜欢科学的31部少儿科普书籍
  9. 使用canvas绘制一个三角形
  10. 微信小程序内嵌网页链接