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