TightVNC 2.0.4 Connection Closed问题

来源:互联网 发布:南安教育网络办公系统 编辑:程序博客网 时间:2024/05/13 20:59

症状(SYMPTOM):

 服务端tvnserver-2.0.4,客户端tvnviewer-1.5.4,采用vs2010编译,操作系统为WinXP SP2/SP3。客户端在连接并且通过验证后报Connection Closed。该问题与验证方式、服务端屏幕分辨率等无关。该症状不是必然发生在每台机器上,经测试,部分环境下不会出现,但没有得到精确地规律。

原因(CAUSE):

 经查,该症状是由于服务器在验证客户端后为客户端创建桌面对象时,返回NULL,服务器主动断开了客户端的连接所致,见RfbClient.cpp文件的RfbClient::execute函数,代码如下:
 void RfbClient::execute(){
   ...
   m_desktop = m_extAuthListener->onClientAuth(this);
   ...
  }
 RfbClient是RFB协议的客户端类,服务器为每个RFB客户端创建一个RfbClient对象,每个对象有一个工作线程,RfbClient::execute是工作线程方法。m_desktop返回NULL,导致抛出异常,服务器主动关闭连接。继续跟踪,调用路径(调用栈)如下:
  Thread::threadProc()
  RfbClient::execute()---(rfb-sconn工程的RfbClient.cpp)
  RfbClientManager::onClientAuth()---(RfbClientManager.cpp)
  WinDesktop::WinDesktop()---(WinDesktop.cpp)
  WindowsUserInput::WindowsUserInput(ClipboardListener *clipboardListener,bool ctrlAltDelEnabled)---(WindowsUserInput.cpp)
  KeyEvent::KeyEvent(bool ctrlAltDelEnabled)---(KeyEvent.cpp)
  InputInjector::InputInjector(bool ctrlAltDelEnabled)---(InputInjector.cpp)
  void InputInjector::resetModifiers()---(win-system工程的InputInjector.cpp)
  void InputInjector::injectKeyEvent(BYTE vkCode, bool release, bool extended)---(win-system工程的InputInjector.cpp)
  最后在inputInjector::resetModifiers方法中执行injectKeyEvent(VK_LWIN, true);语句产生异常,inputInjector::resetModifiers方法代码如下:
  void InputInjector::resetModifiers()
 {
   injectKeyEvent(VK_MENU, true);
   injectKeyEvent(VK_LMENU, true);
   injectKeyEvent(VK_RMENU, true);
   injectKeyEvent(VK_SHIFT, true);
   injectKeyEvent(VK_LSHIFT, true);
   injectKeyEvent(VK_RSHIFT, true);
   injectKeyEvent(VK_CONTROL, true);
   injectKeyEvent(VK_LCONTROL, true);
   injectKeyEvent(VK_RCONTROL, true);
   injectKeyEvent(VK_LWIN, true);///<此处产生异常
   injectKeyEvent(VK_RWIN, true);
   injectKeyEvent(VK_DELETE, true);
 }
 InputInjector::injectKeyEvent代码如下:
  void InputInjector::injectKeyEvent(BYTE vkCode, bool release, bool extended){
   ...
   INPUT keyEvent = {0};

    keyEvent.type = INPUT_KEYBOARD;
    keyEvent.ki.wVk = vkCode;
    keyEvent.ki.wScan = MapVirtualKey(vkCode, 0);

    if (release) {
      keyEvent.ki.dwFlags = KEYEVENTF_KEYUP;
    }

    if (extended) {
      keyEvent.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
    }
   if (SendInput(1, &keyEvent, sizeof(keyEvent)) == 0) {///<此处执行失败,GetLastError返回1350
       DWORD errCode = GetLastError();
       if (errCode != ERROR_SUCCESS) {
         throw SystemException(errCode);///<执行到此处抛出异常
       } else {
         throw Exception(_T("SendInput() function failed"));
       }
     }
   }
   ...
  }
  SendInput函数模拟VK_LWIN(左边窗口键)失败,返回NULL。GetLastError返回1350,为“无法在与安全性无关联的对象上运行安全性操作。”。
 为查明SendInput函数为何不能模拟VK_LWIN键,特用vs2010写了2个测试工程,一个是控制台应用程序,一个是MFC基于对话框的应用程序。控制台应用程序执行SendInput返回0,GetLastError返回错误码0;MFC程序执行SendInput返回0,GetLastError返回错误码127。与TightVNC中的返回码都不一致。时间关系,未进一步分析SendInput函数失败的真正原因。
 
解决方案(SOLUTION):
 
 直接注释掉InputInjector::resetModifiers函数中的模拟VK_LWIN的代码,修改后的代码为:
 void InputInjector::resetModifiers()
 {
   injectKeyEvent(VK_MENU, true);
   injectKeyEvent(VK_LMENU, true);
   injectKeyEvent(VK_RMENU, true);
   injectKeyEvent(VK_SHIFT, true);
   injectKeyEvent(VK_LSHIFT, true);
   injectKeyEvent(VK_RSHIFT, true);
   injectKeyEvent(VK_CONTROL, true);
   injectKeyEvent(VK_LCONTROL, true);
   injectKeyEvent(VK_RCONTROL, true);
   //injectKeyEvent(VK_LWIN, true);///<此处产生异常
   //injectKeyEvent(VK_RWIN, true);
   injectKeyEvent(VK_DELETE, true);
 }
 
 重新编译win-system工程和tvnserver工程,问题得到解决。
 
  这只是个临时解决办法,最终的解决办法是找出SendInput函数失败的原因,然后寻找解决之道。