You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ConnectingDialog.cxx 4.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
  2. * Copyright (C) 2010 D. R. Commander. All Rights Reserved.
  3. *
  4. * This is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation; either version 2 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This software is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this software; if not, write to the Free Software
  16. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
  17. * USA.
  18. */
  19. // -=- ConnectingDialog.cxx
  20. #include <stdlib.h>
  21. #include <vncviewer/ConnectingDialog.h>
  22. #include <vncviewer/resource.h>
  23. #include <network/TcpSocket.h>
  24. #include <rfb/Threading.h>
  25. #include <rfb/Hostname.h>
  26. #include <map>
  27. using namespace rfb;
  28. using namespace rfb::win32;
  29. // ConnectingDialog callback
  30. static INT_PTR CALLBACK ConnectingDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) {
  31. bool* activePtr = (bool*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
  32. switch (uMsg) {
  33. case WM_INITDIALOG:
  34. SetWindowLongPtr(hwnd, GWLP_USERDATA, lParam);
  35. return TRUE;
  36. case WM_COMMAND:
  37. switch (LOWORD(wParam)) {
  38. case IDCANCEL:
  39. if (activePtr)
  40. *activePtr = false;
  41. return TRUE;
  42. }
  43. break;
  44. case WM_DESTROY:
  45. if (activePtr)
  46. *activePtr = false;
  47. return TRUE;
  48. }
  49. return 0;
  50. }
  51. // Global map, used by ConnectingDialog::Threads to call back to their owning
  52. // ConnectingDialogs, while coping with the fact that the owner may already have quit.
  53. static std::map<int, ConnectingDialog*> dialogs;
  54. static int nextDialogId = 0;
  55. static Mutex dialogsLock;
  56. // ConnectingDialog::Thread
  57. // Attempts to connect to the specified host. If the connection succeeds, the
  58. // socket is saved in the owning ConnectingDialog, if still available, and the
  59. // event is signalled. If the connection fails, the Exception text is returned
  60. // to the dialog. If the dialog is already gone, the Exception/socket are discarded.
  61. // NB: This thread class cleans itself up on exit - DO NOT join()!
  62. class ConnectingDialog::Thread : public rfb::Thread {
  63. public:
  64. Thread(int dialogId_, const char* hostAndPort) : dialogId(dialogId_) {
  65. setDeleteAfterRun();
  66. getHostAndPort(hostAndPort, &host.buf, &port);
  67. }
  68. virtual void run() {
  69. try {
  70. returnSock(new network::TcpSocket(host.buf, port));
  71. } catch (rdr::Exception& e) {
  72. returnException(e);
  73. }
  74. }
  75. void returnSock(network::Socket* s) {
  76. Lock l(dialogsLock);
  77. if (dialogs.count(dialogId)) {
  78. dialogs[dialogId]->newSocket = s;
  79. SetEvent(dialogs[dialogId]->readyEvent);
  80. } else {
  81. delete s;
  82. }
  83. }
  84. void returnException(const rdr::Exception& e) {
  85. Lock l(dialogsLock);
  86. if (dialogs.count(dialogId)) {
  87. dialogs[dialogId]->errMsg.replaceBuf(strDup(e.str()));
  88. SetEvent(dialogs[dialogId]->readyEvent);
  89. }
  90. };
  91. CharArray host;
  92. int port;
  93. int dialogId;
  94. };
  95. ConnectingDialog::ConnectingDialog() : dialog(0), newSocket(0),
  96. readyEvent(CreateEvent(0, TRUE, FALSE, 0)), dialogId(0) {
  97. }
  98. network::Socket* ConnectingDialog::connect(const char* hostAndPort) {
  99. Thread* connectThread = 0;
  100. bool active = true;
  101. errMsg.replaceBuf(0);
  102. newSocket = 0;
  103. // Get a unique dialog identifier and create the dialog window
  104. {
  105. Lock l(dialogsLock);
  106. dialogId = ++nextDialogId;
  107. dialogs[dialogId] = this;
  108. dialog = CreateDialogParam(GetModuleHandle(0),
  109. MAKEINTRESOURCE(IDD_CONNECTING_DLG), 0, &ConnectingDlgProc, (LONG_PTR)&active);
  110. ShowWindow(dialog, SW_SHOW);
  111. ResetEvent(readyEvent);
  112. }
  113. // Create and start the connection thread
  114. try {
  115. connectThread = new Thread(dialogId, hostAndPort);
  116. connectThread->start();
  117. } catch (rdr::Exception& e) {
  118. errMsg.replaceBuf(strDup(e.str()));
  119. active = false;
  120. }
  121. // Process window messages until the connection thread signals readyEvent, or the dialog is cancelled
  122. while (active && (MsgWaitForMultipleObjects(1, &readyEvent.h, FALSE, INFINITE, QS_ALLINPUT) == WAIT_OBJECT_0 + 1)) {
  123. MSG msg;
  124. while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
  125. DispatchMessage(&msg);
  126. }
  127. // Remove this dialog from the table
  128. // NB: If the dialog was cancelled then the thread is still running, and will only
  129. // discover that we're gone when it looks up our unique Id in the dialog table.
  130. {
  131. Lock l(dialogsLock);
  132. dialogs.erase(dialogId);
  133. }
  134. // Close the dialog window
  135. DestroyWindow(dialog); dialog=0;
  136. // Throw the exception, if there was one
  137. if (errMsg.buf)
  138. throw rdr::Exception(errMsg.buf);
  139. // Otherwise, return the socket
  140. // NB: The socket will be null if the dialog was cancelled
  141. return newSocket;
  142. }