blob: 12c7ddcc8e70469f4adb73b2c4433f74f967760a [file] [log] [blame]
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <atlcrack.h>
#include <wrl/client.h>
#include "base/memory/ref_counted.h"
#include "base/timer/timer.h"
#include "base/win/atl.h"
#include "net/base/ip_endpoint.h"
#include "remoting/host/screen_resolution.h"
// The following header was generated by Visual Studio. We had to check it in
// due to a bug in VS2013. See for details.
#include "remoting/host/win/com_imported_mstscax.tlh"
#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
namespace remoting {
// RdpClientWindow is used to establish a connection to the given RDP endpoint.
// It is a GUI window class that hosts Microsoft RDP ActiveX control, which
// takes care of handling RDP properly. RdpClientWindow must be used only on
// a UI thread.
class RdpClientWindow
: public CWindowImpl<RdpClientWindow, CWindow, CFrameWinTraits>,
public IDispEventImpl<1, RdpClientWindow,
&__uuidof(mstsc::__MSTSCLib), 1, 0> {
// Receives connect/disconnect notifications. The notifications can be
// delivered after RdpClientWindow::Connect() returned success.
// RdpClientWindow guarantees that OnDisconnected() is the last notification
// the event handler receives. OnDisconnected() is guaranteed to be called
// only once.
class EventHandler {
virtual ~EventHandler() {}
// Invoked when the RDP control has established a connection.
virtual void OnConnected() = 0;
// Invoked when the RDP control has been disconnected from the RDP server.
// This includes both graceful shutdown and any fatal error condition.
// Once RdpClientWindow::Connect() returns success the owner of the
// |RdpClientWindow| object must keep it alive until OnDisconnected() is
// called.
// OnDisconnected() should not delete |RdpClientWindow| object directly.
// Instead it should post a task to delete the object. The ActiveX code
// expects the window be alive until the currently handled window message is
// completely processed.
virtual void OnDisconnected() = 0;
// Specifies the endpoint to connect to and passes the event handler pointer
// to be notified about connection events.
RdpClientWindow(const net::IPEndPoint& server_endpoint,
const std::string& terminal_id,
EventHandler* event_handler);
~RdpClientWindow() override;
// Creates the window along with the ActiveX control and initiates the
// connection. |resolution| specifies resolution of the screen. Returns false
// if an error occurs.
bool Connect(const ScreenResolution& resolution);
// Initiates shutdown of the connection. The caller must not delete |this|
// until it receives OnDisconnected() notification.
void Disconnect();
// Emulates pressing Ctrl+Alt+End combination that is translated to Secure
// Attention Sequence by the ActiveX control.
void InjectSas();
// Change the resolution of the desktop.
void ChangeResolution(const ScreenResolution& resolution);
typedef IDispEventImpl<1, RdpClientWindow,
&__uuidof(mstsc::__MSTSCLib), 1, 0> RdpEventsSink;
// Handled window messages.
// Requests the RDP ActiveX control to close the connection gracefully.
void OnClose();
// Creates the RDP ActiveX control, configures it, and initiates an RDP
// connection to |server_endpoint_|.
LRESULT OnCreate(CREATESTRUCT* create_struct);
// Releases the RDP ActiveX control interfaces.
void OnDestroy();
SINK_ENTRY_EX(1, __uuidof(mstsc::IMsTscAxEvents), 2,
SINK_ENTRY_EX(1, __uuidof(mstsc::IMsTscAxEvents), 3,
SINK_ENTRY_EX(1, __uuidof(mstsc::IMsTscAxEvents), 4,
SINK_ENTRY_EX(1, __uuidof(mstsc::IMsTscAxEvents), 10,
SINK_ENTRY_EX(1, __uuidof(mstsc::IMsTscAxEvents), 15,
SINK_ENTRY_EX(1, __uuidof(mstsc::IMsTscAxEvents), 18,
SINK_ENTRY_EX(1, __uuidof(mstsc::IMsTscAxEvents), 19,
// mstsc::IMsTscAxEvents notifications.
STDMETHOD(OnDisconnected)(long reason);
STDMETHOD(OnFatalError)(long error_code);
STDMETHOD(OnConfirmClose)(VARIANT_BOOL* allow_close);
int LogOnCreateError(HRESULT error);
// Wrappers for the event handler's methods that make sure that
// OnDisconnected() is the last notification delivered and is delivered
// only once.
void NotifyConnected();
void NotifyDisconnected();
// Updates the desktop using |screen_resolution_| if resizing is supported.
HRESULT UpdateDesktopResolution();
// Attempts to reapply the requested screen resolution. This method is used
// to workaround the inconsistent behavior seen by the RDP API which can fail
// for an indeterminate amount of time (i.e. a few seconds) after the user has
// logged into the session. In practice retrying after ~100ms will succeed,
// but we will use |apply_resolution_timer_| to try a few times if the machine
// is under load.
void ReapplyDesktopResolution();
// Invoked to report connect/disconnect events.
EventHandler* event_handler_;
// Contains the requested dimensions of the screen.
remoting::ScreenResolution screen_resolution_;
// Used for applying resolution changes after a timeout.
base::RepeatingTimer apply_resolution_timer_;
// Tracks the number of resolution change retries.
int apply_resolution_attempts_ = 0;
// The endpoint to connect to.
net::IPEndPoint server_endpoint_;
// The terminal ID assigned to this connection.
std::string terminal_id_;
// Our RDP session always starts logged out (i.e. at the logon screen), some
// functions do not work until the user has logged in so we want to track that
// event. We don't support logging out so this value should never revert from
// true to false.
bool user_logged_in_ = false;
// Interfaces exposed by the RDP ActiveX control.
Microsoft::WRL::ComPtr<mstsc::IMsRdpClient> client_;
Microsoft::WRL::ComPtr<mstsc::IMsRdpClient9> client_9_;
Microsoft::WRL::ComPtr<mstsc::IMsRdpClientAdvancedSettings> client_settings_;
// Used to cancel modal dialog boxes shown by the RDP control.
class WindowHook;
scoped_refptr<WindowHook> window_activate_hook_;
} // namespace remoting