blob: 7f52e991e6e83808584591cfd3c83b5c1882b048 [file] [log] [blame]
Constantin Kaplinsky729598c2006-05-25 05:12:25 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
DRCc75dc442010-05-20 07:44:49 +00002 * Copyright (C) 2010 D. R. Commander. All Rights Reserved.
Constantin Kaplinsky729598c2006-05-25 05:12:25 +00003 *
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
20// -=- ConnectingDialog.cxx
21
22#include <stdlib.h>
23#include <vncviewer/ConnectingDialog.h>
24#include <vncviewer/resource.h>
25#include <network/TcpSocket.h>
26#include <rfb/Threading.h>
27#include <rfb/Hostname.h>
28#include <map>
29
30using namespace rfb;
31using namespace rfb::win32;
32
33
34// ConnectingDialog callback
DRCc75dc442010-05-20 07:44:49 +000035static INT_PTR CALLBACK ConnectingDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) {
36 bool* activePtr = (bool*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000037 switch (uMsg) {
38 case WM_INITDIALOG:
DRCc75dc442010-05-20 07:44:49 +000039 SetWindowLongPtr(hwnd, GWLP_USERDATA, lParam);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000040 return TRUE;
41 case WM_COMMAND:
42 switch (LOWORD(wParam)) {
43 case IDCANCEL:
44 if (activePtr)
45 *activePtr = false;
46 return TRUE;
47 }
48 break;
49 case WM_DESTROY:
50 if (activePtr)
51 *activePtr = false;
52 return TRUE;
53 }
54 return 0;
55}
56
57
58// Global map, used by ConnectingDialog::Threads to call back to their owning
59// ConnectingDialogs, while coping with the fact that the owner may already have quit.
60static std::map<int, ConnectingDialog*> dialogs;
61static int nextDialogId = 0;
62static Mutex dialogsLock;
63
64
65// ConnectingDialog::Thread
66// Attempts to connect to the specified host. If the connection succeeds, the
67// socket is saved in the owning ConnectingDialog, if still available, and the
68// event is signalled. If the connection fails, the Exception text is returned
69// to the dialog. If the dialog is already gone, the Exception/socket are discarded.
70// NB: This thread class cleans itself up on exit - DO NOT join()!
71class ConnectingDialog::Thread : public rfb::Thread {
72public:
73 Thread(int dialogId_, const char* hostAndPort) : dialogId(dialogId_) {
74 setDeleteAfterRun();
75 getHostAndPort(hostAndPort, &host.buf, &port);
76 }
77 virtual void run() {
78 try {
79 returnSock(new network::TcpSocket(host.buf, port));
80 } catch (rdr::Exception& e) {
81 returnException(e);
82 }
83 }
84 void returnSock(network::Socket* s) {
85 Lock l(dialogsLock);
86 if (dialogs.count(dialogId)) {
87 dialogs[dialogId]->newSocket = s;
88 SetEvent(dialogs[dialogId]->readyEvent);
89 } else {
90 delete s;
91 }
92 }
93 void returnException(const rdr::Exception& e) {
94 Lock l(dialogsLock);
95 if (dialogs.count(dialogId)) {
96 dialogs[dialogId]->errMsg.replaceBuf(strDup(e.str()));
97 SetEvent(dialogs[dialogId]->readyEvent);
98 }
99 };
100 CharArray host;
101 int port;
102 int dialogId;
103};
104
105
Peter Åstrand6132a9a2008-12-11 09:07:08 +0000106ConnectingDialog::ConnectingDialog() : dialog(0), newSocket(0),
107 readyEvent(CreateEvent(0, TRUE, FALSE, 0)), dialogId(0) {
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000108}
109
110network::Socket* ConnectingDialog::connect(const char* hostAndPort) {
111 Thread* connectThread = 0;
112 bool active = true;
113 errMsg.replaceBuf(0);
114 newSocket = 0;
115
116 // Get a unique dialog identifier and create the dialog window
117 {
118 Lock l(dialogsLock);
119 dialogId = ++nextDialogId;
120 dialogs[dialogId] = this;
121 dialog = CreateDialogParam(GetModuleHandle(0),
DRCc75dc442010-05-20 07:44:49 +0000122 MAKEINTRESOURCE(IDD_CONNECTING_DLG), 0, &ConnectingDlgProc, (LONG_PTR)&active);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000123 ShowWindow(dialog, SW_SHOW);
124 ResetEvent(readyEvent);
125 }
126
127 // Create and start the connection thread
128 try {
129 connectThread = new Thread(dialogId, hostAndPort);
130 connectThread->start();
131 } catch (rdr::Exception& e) {
132 errMsg.replaceBuf(strDup(e.str()));
133 active = false;
134 }
135
136 // Process window messages until the connection thread signals readyEvent, or the dialog is cancelled
137 while (active && (MsgWaitForMultipleObjects(1, &readyEvent.h, FALSE, INFINITE, QS_ALLINPUT) == WAIT_OBJECT_0 + 1)) {
138 MSG msg;
139 while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
140 DispatchMessage(&msg);
141 }
142
143 // Remove this dialog from the table
144 // NB: If the dialog was cancelled then the thread is still running, and will only
145 // discover that we're gone when it looks up our unique Id in the dialog table.
146 {
147 Lock l(dialogsLock);
148 dialogs.erase(dialogId);
149 }
150
151 // Close the dialog window
152 DestroyWindow(dialog); dialog=0;
153
154 // Throw the exception, if there was one
155 if (errMsg.buf)
156 throw rdr::Exception(errMsg.buf);
157
158 // Otherwise, return the socket
159 // NB: The socket will be null if the dialog was cancelled
160 return newSocket;
161}