// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef REMOTING_HOST_WIN_RDP_CLIENT_WINDOW_H_
#define REMOTING_HOST_WIN_RDP_CLIENT_WINDOW_H_
// clang-format off
// This needs to be included before ATL headers.
#include "base/win/atl.h"
// clang-format on
#include <atlapp.h>
#include <atlcrack.h>
#include <wrl/client.h>
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/timer/timer.h"
#include "base/win/atl.h"
#include "net/base/ip_endpoint.h"
#include "remoting/host/base/screen_resolution.h"
// The following header was generated by Visual Studio. We had to check it in
// due to a bug in VS2013. See crbug.com/318952 for details.
#include "remoting/host/win/com_imported_mstscax.h"
#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::IMsTscAxEvents),
&__uuidof(mstsc::__MSTSCLib),
1,
0> {
public:
// 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 {
public:
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;
};
DECLARE_WND_CLASS(L"RdpClientWindow")
// 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);
private:
typedef IDispEventImpl<1,
RdpClientWindow,
&__uuidof(mstsc::IMsTscAxEvents),
&__uuidof(mstsc::__MSTSCLib),
1,
0>
RdpEventsSink;
// Handled window messages.
BEGIN_MSG_MAP_EX(RdpClientWindow)
MSG_WM_CLOSE(OnClose)
MSG_WM_CREATE(OnCreate)
MSG_WM_DESTROY(OnDestroy)
END_MSG_MAP()
// 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();
BEGIN_SINK_MAP(RdpClientWindow)
SINK_ENTRY_EX(1,
__uuidof(mstsc::IMsTscAxEvents),
2,
&RdpClientWindow::OnConnected)
SINK_ENTRY_EX(1,
__uuidof(mstsc::IMsTscAxEvents),
3,
&RdpClientWindow::OnLoginComplete)
SINK_ENTRY_EX(1,
__uuidof(mstsc::IMsTscAxEvents),
4,
&RdpClientWindow::OnDisconnected)
SINK_ENTRY_EX(1,
__uuidof(mstsc::IMsTscAxEvents),
10,
&RdpClientWindow::OnFatalError)
SINK_ENTRY_EX(1,
__uuidof(mstsc::IMsTscAxEvents),
15,
&RdpClientWindow::OnConfirmClose)
SINK_ENTRY_EX(1,
__uuidof(mstsc::IMsTscAxEvents),
18,
&RdpClientWindow::OnAuthenticationWarningDisplayed)
SINK_ENTRY_EX(1,
__uuidof(mstsc::IMsTscAxEvents),
19,
&RdpClientWindow::OnAuthenticationWarningDismissed)
END_SINK_MAP()
// mstsc::IMsTscAxEvents notifications.
STDMETHOD(OnAuthenticationWarningDisplayed)();
STDMETHOD(OnAuthenticationWarningDismissed)();
STDMETHOD(OnConnected)();
STDMETHOD(OnLoginComplete)();
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.
raw_ptr<EventHandler> event_handler_;
// Contains the requested dimensions of the screen. Used for both login and
// user sessions.
remoting::ScreenResolution display_settings_;
// Contains the last successful display update for the user session. This
// needs to be tracked as trying to re-apply the same values can cause DXGI
// capture to fail.
remoting::ScreenResolution session_display_settings_;
// 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::IMsRdpClient9> client_;
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
#endif // REMOTING_HOST_WIN_RDP_CLIENT_WINDOW_H_