| /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. |
| * Copyright (C) 2010 D. R. Commander. All Rights Reserved. |
| * |
| * This is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This software is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this software; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
| * USA. |
| */ |
| |
| // -=- ConnectingDialog.cxx |
| |
| #include <stdlib.h> |
| #include <vncviewer/ConnectingDialog.h> |
| #include <vncviewer/resource.h> |
| #include <network/TcpSocket.h> |
| #include <rfb/Threading.h> |
| #include <rfb/Hostname.h> |
| #include <map> |
| |
| using namespace rfb; |
| using namespace rfb::win32; |
| |
| |
| // ConnectingDialog callback |
| static INT_PTR CALLBACK ConnectingDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { |
| bool* activePtr = (bool*)GetWindowLongPtr(hwnd, GWLP_USERDATA); |
| switch (uMsg) { |
| case WM_INITDIALOG: |
| SetWindowLongPtr(hwnd, GWLP_USERDATA, lParam); |
| return TRUE; |
| case WM_COMMAND: |
| switch (LOWORD(wParam)) { |
| case IDCANCEL: |
| if (activePtr) |
| *activePtr = false; |
| return TRUE; |
| } |
| break; |
| case WM_DESTROY: |
| if (activePtr) |
| *activePtr = false; |
| return TRUE; |
| } |
| return 0; |
| } |
| |
| |
| // Global map, used by ConnectingDialog::Threads to call back to their owning |
| // ConnectingDialogs, while coping with the fact that the owner may already have quit. |
| static std::map<int, ConnectingDialog*> dialogs; |
| static int nextDialogId = 0; |
| static Mutex dialogsLock; |
| |
| |
| // ConnectingDialog::Thread |
| // Attempts to connect to the specified host. If the connection succeeds, the |
| // socket is saved in the owning ConnectingDialog, if still available, and the |
| // event is signalled. If the connection fails, the Exception text is returned |
| // to the dialog. If the dialog is already gone, the Exception/socket are discarded. |
| // NB: This thread class cleans itself up on exit - DO NOT join()! |
| class ConnectingDialog::Thread : public rfb::Thread { |
| public: |
| Thread(int dialogId_, const char* hostAndPort) : dialogId(dialogId_) { |
| setDeleteAfterRun(); |
| getHostAndPort(hostAndPort, &host.buf, &port); |
| } |
| virtual void run() { |
| try { |
| returnSock(new network::TcpSocket(host.buf, port)); |
| } catch (rdr::Exception& e) { |
| returnException(e); |
| } |
| } |
| void returnSock(network::Socket* s) { |
| Lock l(dialogsLock); |
| if (dialogs.count(dialogId)) { |
| dialogs[dialogId]->newSocket = s; |
| SetEvent(dialogs[dialogId]->readyEvent); |
| } else { |
| delete s; |
| } |
| } |
| void returnException(const rdr::Exception& e) { |
| Lock l(dialogsLock); |
| if (dialogs.count(dialogId)) { |
| dialogs[dialogId]->errMsg.replaceBuf(strDup(e.str())); |
| SetEvent(dialogs[dialogId]->readyEvent); |
| } |
| }; |
| CharArray host; |
| int port; |
| int dialogId; |
| }; |
| |
| |
| ConnectingDialog::ConnectingDialog() : dialog(0), newSocket(0), |
| readyEvent(CreateEvent(0, TRUE, FALSE, 0)), dialogId(0) { |
| } |
| |
| network::Socket* ConnectingDialog::connect(const char* hostAndPort) { |
| Thread* connectThread = 0; |
| bool active = true; |
| errMsg.replaceBuf(0); |
| newSocket = 0; |
| |
| // Get a unique dialog identifier and create the dialog window |
| { |
| Lock l(dialogsLock); |
| dialogId = ++nextDialogId; |
| dialogs[dialogId] = this; |
| dialog = CreateDialogParam(GetModuleHandle(0), |
| MAKEINTRESOURCE(IDD_CONNECTING_DLG), 0, &ConnectingDlgProc, (LONG_PTR)&active); |
| ShowWindow(dialog, SW_SHOW); |
| ResetEvent(readyEvent); |
| } |
| |
| // Create and start the connection thread |
| try { |
| connectThread = new Thread(dialogId, hostAndPort); |
| connectThread->start(); |
| } catch (rdr::Exception& e) { |
| errMsg.replaceBuf(strDup(e.str())); |
| active = false; |
| } |
| |
| // Process window messages until the connection thread signals readyEvent, or the dialog is cancelled |
| while (active && (MsgWaitForMultipleObjects(1, &readyEvent.h, FALSE, INFINITE, QS_ALLINPUT) == WAIT_OBJECT_0 + 1)) { |
| MSG msg; |
| while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) |
| DispatchMessage(&msg); |
| } |
| |
| // Remove this dialog from the table |
| // NB: If the dialog was cancelled then the thread is still running, and will only |
| // discover that we're gone when it looks up our unique Id in the dialog table. |
| { |
| Lock l(dialogsLock); |
| dialogs.erase(dialogId); |
| } |
| |
| // Close the dialog window |
| DestroyWindow(dialog); dialog=0; |
| |
| // Throw the exception, if there was one |
| if (errMsg.buf) |
| throw rdr::Exception(errMsg.buf); |
| |
| // Otherwise, return the socket |
| // NB: The socket will be null if the dialog was cancelled |
| return newSocket; |
| } |