/*++ Copyright (c) 1999-2000 Microsoft Corporation Module Name: TSRDPRemoteDesktopClient Abstract: This is the TS/RDP implementation of the Remote Desktop Client class. The Remote Desktop Client class hierarchy provides a pluggable C++ interface for remote desktop access, by abstracting the implementation specific details of remote desktop access for the client-side The TSRDPRemoteDesktopClass implements remote-desktopping with the help of an instance of the MSTSC ActiveX client control. Author: Tad Brockway 02/00 Revision History: --*/ #ifndef __TSRDPREMOTEDESKTOPCLIENT_H_ #define __TSRDPREMOTEDESKTOPCLIENT_H_ #include "resource.h" #include #include "RDCHostCP.h" #include #include #include #include #include "parseaddr.h" #pragma warning (disable: 4786) #include #include "icshelpapi.h" #define IDC_MSTSCEVENT_SOURCE_OBJ 1 #define IDC_CHANNELEVENT_SOURCE_OBJ 2 #define WM_STARTLISTEN (0xBFFE) #define WM_TSCONNECT (0xBFFF) #define WM_LISTENTIMEOUT_TIMER 1 #define WM_CONNECTCHECK_TIMER 2 #define MAX_FETCHIPADDRESSRETRY 5 // // MSTSC ActiveX GUID // #define MSTSCAX_TEXTGUID _T("{7cacbd7b-0d99-468f-ac33-22e495c0afe5}") #define RDC_CHECKCONN_TIMEOUT (30 * 1000) //millisec. default value to ping is 30 seconds #define RDC_CONNCHECK_ENTRY L"ConnectionCheck" // // Info for all the event functions is entered here // there is a way to have ATL do this automatically using typelib's // but it is slower. // static _ATL_FUNC_INFO TSRDPClientEventFuncNoParamsInfo = { CC_STDCALL, // Calling convention. VT_EMPTY, // Return type. 0, // Number of arguments. {VT_EMPTY} // Argument types. }; static _ATL_FUNC_INFO TSRDPClientEventFuncLongParamInfo = { CC_STDCALL, // Calling convention. VT_EMPTY, // Return type. 1, // Number of arguments. {VT_I4} // Argument types. }; static _ATL_FUNC_INFO TSRDPClientEventFuncTwoStringParamInfo = { CC_STDCALL, // Calling convention. VT_EMPTY, // Return type. 2, // Number of arguments. {VT_BSTR, // Argument types VT_BSTR} }; static _ATL_FUNC_INFO TSRDPClientEventFuncReceivePublicKeyParamInfo = { CC_STDCALL, // Calling convention. VT_EMPTY, // Return type. 2, // Number of arguments. {VT_BSTR, // Argument types VT_BYREF | VT_BOOL } }; static _ATL_FUNC_INFO TSRDPClientEventFuncOneStringParamInfo = { CC_STDCALL, // Calling convention. VT_EMPTY, // Return type. 1, // Number of arguments. {VT_BSTR} // Argument types }; /////////////////////////////////////////////////////// // // CMSTSCClientEventSink // class CTSRDPRemoteDesktopClient; class CMSTSCClientEventSink : public IDispEventSimpleImpl, public CRemoteDesktopTopLevelObject { public: CTSRDPRemoteDesktopClient *m_Obj; public: BEGIN_SINK_MAP(CMSTSCClientEventSink) SINK_ENTRY_INFO(IDC_MSTSCEVENT_SOURCE_OBJ, DIID_IMsTscAxEvents, DISPID_CONNECTED, OnRDPConnected, &TSRDPClientEventFuncNoParamsInfo) SINK_ENTRY_INFO(IDC_MSTSCEVENT_SOURCE_OBJ, DIID_IMsTscAxEvents, DISPID_DISCONNECTED, OnDisconnected, &TSRDPClientEventFuncLongParamInfo) SINK_ENTRY_INFO(IDC_MSTSCEVENT_SOURCE_OBJ, DIID_IMsTscAxEvents, DISPID_LOGINCOMPLETE, OnLoginComplete, &TSRDPClientEventFuncNoParamsInfo) SINK_ENTRY_INFO(IDC_MSTSCEVENT_SOURCE_OBJ, DIID_IMsTscAxEvents, DISPID_RECEVIEDTSPUBLICKEY, OnReceivedTSPublicKey, &TSRDPClientEventFuncReceivePublicKeyParamInfo) SINK_ENTRY_INFO(IDC_MSTSCEVENT_SOURCE_OBJ, DIID_IMsTscAxEvents, DISPID_CHANNELRECEIVEDDATA, OnReceiveData, &TSRDPClientEventFuncTwoStringParamInfo) END_SINK_MAP() CMSTSCClientEventSink() { m_Obj = NULL; } ~CMSTSCClientEventSink(); // // Event Sinks // void __stdcall OnReceivedTSPublicKey(BSTR publicKey, VARIANT_BOOL* pfContinue); HRESULT __stdcall OnRDPConnected(); HRESULT __stdcall OnLoginComplete(); HRESULT __stdcall OnDisconnected(long disconReason); void __stdcall OnReceiveData(BSTR chanName, BSTR data); // // Return the name of this class. // virtual const LPTSTR ClassName() { return TEXT("CMSTSCClientEventSink"); } }; /////////////////////////////////////////////////////// // // CCtlChannelEventSink // // Control Channel Event Sink // class CCtlChannelEventSink : public IDispEventSimpleImpl, public CRemoteDesktopTopLevelObject { public: CTSRDPRemoteDesktopClient *m_Obj; public: BEGIN_SINK_MAP(CCtlChannelEventSink) SINK_ENTRY_INFO(IDC_CHANNELEVENT_SOURCE_OBJ, DIID__ISAFRemoteDesktopDataChannelEvents, DISPID_RDSCHANNELEVENTS_CHANNELDATAREADY, DataReady, &TSRDPClientEventFuncOneStringParamInfo) END_SINK_MAP() CCtlChannelEventSink() { m_Obj = NULL; } ~CCtlChannelEventSink(); // // Event Sinks // void __stdcall DataReady(BSTR channelName); // // Return the name of this class. // virtual const LPTSTR ClassName() { return TEXT("CCtlChannelEventSink"); } }; /////////////////////////////////////////////////////// // // CTSRDPRemoteDesktopClient // class CMSTSCClientEventSink; class ATL_NO_VTABLE CTSRDPRemoteDesktopClient : public CComObjectRootEx, public CComControl, public IPersistStreamInitImpl, public IOleControlImpl, public IOleObjectImpl, public IOleInPlaceActiveObjectImpl, public IViewObjectExImpl, public IOleInPlaceObjectWindowlessImpl, public IPersistStorageImpl, public ISpecifyPropertyPagesImpl, public IQuickActivateImpl, public IDataObjectImpl, public IProvideClassInfo2Impl<&CLSID_TSRDPRemoteDesktopClient, &DIID__ISAFRemoteDesktopClientEvents, &LIBID_RDCCLIENTHOSTLib>, public CComCoClass, public IDispatchImpl, public IDispatchImpl, public IDataChannelIO, public CProxy_ISAFRemoteDesktopClientEvents< CTSRDPRemoteDesktopClient>, public CProxy_IDataChannelIOEvents< CTSRDPRemoteDesktopClient>, public IConnectionPointContainerImpl, public CRemoteDesktopTopLevelObject { friend CCtlChannelEventSink; private: IMsRdpClient2 *m_TSClient; HWND m_TSClientWnd; CAxWindow m_TSClientAxView; BOOL m_ConnectionInProgress; BOOL m_RemoteControlRequestInProgress; BOOL m_ConnectedToServer; BOOL m_Initialized; LONG m_LastExtendedErrorInfo; // // Event sink receives events fired by the TS client control.. // CMSTSCClientEventSink m_TSClientEventSink; // // Control Channel Event Sink // CCtlChannelEventSink m_CtlChannelEventSink; // // Multiplexes Channel Data // CComPtr m_ChannelMgr; CComPtr m_CtlChannel; // // The parsed connection parameters. // DWORD m_ConnectParmVersion; CComBSTR m_AssistantAccount; CComBSTR m_AssistantAccountPwd; CComBSTR m_HelpSessionName; CComBSTR m_HelpSessionID; CComBSTR m_HelpSessionPwd; CComBSTR m_TSSecurityBlob; ServerAddressList m_ServerAddressList; CComBSTR m_ConnectedServer; LONG m_ConnectedPort; // // The complete connection string. // CComBSTR m_ConnectParms; // // Expert side to be transmitted over to user // CComBSTR m_ExpertBlob; // // Search for a child window of the specified parent window. // typedef struct _WinSearch { HWND foundWindow; LPTSTR srchCaption; LPTSTR srchClass; } WINSEARCH, *PWINSEARCH; HWND SearchForWindow(HWND hwndParent, LPTSTR srchCaption, LPTSTR srchClass); static BOOL CALLBACK _WindowSrchProc(HWND hwnd, PWINSEARCH srch); //timer related members DWORD m_PrevTimer; UINT m_TimerId; DWORD m_RdcConnCheckTimeInterval; BOOL m_ListenConnectInProgress; // duration of StartListen() until mstscax connected. SOCKET m_ListenSocket; // listen() socket SOCKET m_TSConnectSocket; // accept() scoket DWORD m_ICSPort; // port that ICS library punch on ICS server BOOL m_InitListeningLibrary; // Instance of object initialize WinSock/ICS library. UINT_PTR m_ListenTimeoutTimerID; // Timer ID for listen timeout. void ListenConnectCleanup() { m_ListenConnectInProgress = FALSE; if( INVALID_SOCKET != m_ListenSocket ) { closesocket( m_ListenSocket ); } if( (UINT_PTR)0 != m_ListenTimeoutTimerID ) { KillTimer( m_ListenTimeoutTimerID ); } if( INVALID_SOCKET != m_TSConnectSocket ) { closesocket( m_TSConnectSocket ); } if( 0 != m_ICSPort ) { ClosePort( m_ICSPort ); } m_ListenSocket = INVALID_SOCKET; m_TSConnectSocket = INVALID_SOCKET; m_ICSPort = 0; } // // Variable to manage WinSock and ICS library startup/shutdown, WinSock/ICS library // is RDP specific so not declare in parent class. // static LONG gm_ListeningLibraryRefCount; // Number of time we reference WinSock and ICS library // // accessing only global variable, no need for per-instance. // static HRESULT InitListeningLibrary(); static HRESULT TerminateListeningLibrary(); // // Listen socket already in progress // inline BOOL ListenConnectInProgress() { return m_ListenConnectInProgress; } protected: // // Final Initialization. // virtual HRESULT Initialize(LPCREATESTRUCT pCreateStruct); // // Generate a remote control request message for the // server. // HRESULT GenerateRCRequest(BSTR *rcRequest); // // Generate a 'client authenticate' request. // HRESULT GenerateClientAuthenticateRequest(BSTR *authenticateReq); // // Generate a version information packet. // HRESULT GenerateVersionInfoPacket(BSTR *versionInfoPacket); // // Send the terminate shadowing key sequence to the server. // HRESULT SendTerminateRCKeysToServer(); // // Handle Remote Control 'Control' Channel messages. // VOID HandleControlChannelMsg(); // // Translate an MSTSC disconnect code into a Salem disconnect // code. // LONG TranslateMSTSCDisconnectCode(DisconnectReasonCode disconReason, ExtendedDisconnectReasonCode extendedReasonCode); // // Disconnects the client from the server. // STDMETHOD(DisconnectFromServerInternal)( LONG disconnectCode ); HRESULT SetupConnectionInfo(BOOL bListen, BSTR expertBlob); // // Connect to server with port // HRESULT ConnectServerPort( BSTR ServerName, LONG portNumber ); // // Internal stop listening function for dealing with calls from external // and internal contexts. // HRESULT StopListenInternal(LONG returnCode = 0); // // Connect to server with established socket // HRESULT ConnectServerWithOpenedSocket(); //generate a simple message for checking if the connection is alive HRESULT GenerateNullData(BSTR *bstrMsg); // // Retrieve connect parm // HRESULT RetrieveUserConnectParm( BSTR* pConnectParm ); void FireListenConnect( DWORD ErrCode ) { return; } HRESULT GetCurrentUser( LPTSTR* pszUserName ); public: // // Constructor/Destructor // CTSRDPRemoteDesktopClient() { // // We are window'd, even if our parent supports Windowless // controls. // m_bWindowOnly = TRUE; m_ConnectedToServer = FALSE; m_Initialized = FALSE; m_TSClient = NULL; m_TSClientWnd = NULL; m_ConnectionInProgress = FALSE; m_RemoteControlRequestInProgress = FALSE; m_LastExtendedErrorInfo = 0; m_TimerId = 0; //used for pinging m_RdcConnCheckTimeInterval = RDC_CHECKCONN_TIMEOUT; // // No reference to listening library. // m_InitListeningLibrary = FALSE; m_ListenConnectInProgress = FALSE; m_ListenSocket = INVALID_SOCKET; m_TSConnectSocket = INVALID_SOCKET; m_ListenTimeoutTimerID = (UINT_PTR) 0; m_ICSPort = 0; // // Not valid until unitialized. // SetValid(FALSE); } ~CTSRDPRemoteDesktopClient(); HRESULT FinalConstruct(); DECLARE_REGISTRY_RESOURCEID(IDR_TSRDPREMOTEDESKTOPCLIENT) DECLARE_PROTECT_FINAL_CONSTRUCT() // // Event Sinks // VOID OnRDPConnected(); VOID OnLoginComplete(); VOID OnDisconnected(long disconReason); VOID OnMSTSCReceiveData(BSTR data); VOID OnReceivedTSPublicKey(BSTR tsPublicKey, VARIANT_BOOL* bContinue); // // Interface Map // BEGIN_COM_MAP(CTSRDPRemoteDesktopClient) COM_INTERFACE_ENTRY(IViewObjectEx) COM_INTERFACE_ENTRY(IViewObject2) COM_INTERFACE_ENTRY(IViewObject) COM_INTERFACE_ENTRY(IOleInPlaceObjectWindowless) COM_INTERFACE_ENTRY(IOleInPlaceObject) COM_INTERFACE_ENTRY2(IOleWindow, IOleInPlaceObjectWindowless) COM_INTERFACE_ENTRY(IOleInPlaceActiveObject) COM_INTERFACE_ENTRY(IOleControl) COM_INTERFACE_ENTRY(IOleObject) COM_INTERFACE_ENTRY(IPersistStreamInit) COM_INTERFACE_ENTRY2(IPersist, IPersistStreamInit) COM_INTERFACE_ENTRY(ISpecifyPropertyPages) COM_INTERFACE_ENTRY(IQuickActivate) COM_INTERFACE_ENTRY(IPersistStorage) COM_INTERFACE_ENTRY(IDataObject) COM_INTERFACE_ENTRY(IProvideClassInfo) COM_INTERFACE_ENTRY(IProvideClassInfo2) COM_INTERFACE_ENTRY(ISAFRemoteDesktopClient) COM_INTERFACE_ENTRY(ISAFRemoteDesktopTestExtension) COM_INTERFACE_ENTRY2(IDispatch, ISAFRemoteDesktopClient) COM_INTERFACE_ENTRY(IDataChannelIO) COM_INTERFACE_ENTRY_IMPL(IConnectionPointContainer) END_COM_MAP() // // Property Map // BEGIN_PROP_MAP(CTSRDPRemoteDesktopClient) PROP_DATA_ENTRY("_cx", m_sizeExtent.cx, VT_UI4) PROP_DATA_ENTRY("_cy", m_sizeExtent.cy, VT_UI4) // Example entries // PROP_ENTRY("Property Description", dispid, clsid) // PROP_PAGE(CLSID_StockColorPage) END_PROP_MAP() // // Connection Point Map // BEGIN_CONNECTION_POINT_MAP(CTSRDPRemoteDesktopClient) CONNECTION_POINT_ENTRY(DIID__ISAFRemoteDesktopClientEvents) CONNECTION_POINT_ENTRY(DIID__IDataChannelIOEvents) END_CONNECTION_POINT_MAP() // // Message Map // BEGIN_MSG_MAP(CTSRDPRemoteDesktopClient) CHAIN_MSG_MAP(CComControl) DEFAULT_REFLECTION_HANDLER() MESSAGE_HANDLER(WM_CREATE, OnCreate) MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus) MESSAGE_HANDLER(WM_SIZE, OnSize) MESSAGE_HANDLER(WM_TSCONNECT, OnTSConnect) MESSAGE_HANDLER(WM_TIMER, OnTimer) END_MSG_MAP() // Handler prototypes: // LRESULT MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); // LRESULT CommandHandler(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled); // LRESULT NotifyHandler(int idCtrl, LPNMHDR pnmh, BOOL& bHandled); // // IViewObjectEx Methods // DECLARE_VIEW_STATUS(VIEWSTATUS_SOLIDBKGND | VIEWSTATUS_OPAQUE) public: LRESULT OnTSConnect(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); LRESULT OnStartListen(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); // // OnDraw // HRESULT OnDraw(ATL_DRAWINFO& di) { RECT& rc = *(RECT*)di.prcBounds; Rectangle(di.hdcDraw, rc.left, rc.top, rc.right, rc.bottom); HRESULT hr = S_FALSE; if (!m_Initialized) { hr = S_OK; SetTextAlign(di.hdcDraw, TA_CENTER|TA_BASELINE); LPCTSTR pszText = _T("TSRDP Remote Desktop Client"); TextOut(di.hdcDraw, (rc.left + rc.right) / 2, (rc.top + rc.bottom) / 2, pszText, lstrlen(pszText)); } return hr; } LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { // // Hide our window, by default. // //ShowWindow(SW_HIDE); if (!m_Initialized) { LPCREATESTRUCT pCreateStruct = (LPCREATESTRUCT)lParam; Initialize(pCreateStruct); } return 0; } LRESULT OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { DC_BEGIN_FN("CTSRDPRemoteDesktopClient::OnSetFocus"); // // Set focus back to the client window, if it exists. // if (m_TSClientWnd != NULL) { ::PostMessage(m_TSClientWnd, uMsg, wParam, lParam); } DC_END_FN(); return 0; } // // OnSize // LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { DC_BEGIN_FN("CTSRDPRemoteDesktopClient::OnSize"); if (m_TSClientWnd != NULL) { RECT rect; GetClientRect(&rect); ::MoveWindow(m_TSClientWnd, rect.left, rect.top, rect.right, rect.bottom, TRUE); } DC_END_FN(); return 0; } LRESULT OnKeyDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { bHandled = TRUE; return 0; //return DefWindowProc(uMsg, wParam, lParam); } // // ISAFRemoteDesktopClient Methods // STDMETHOD(ConnectToServer)(BSTR Server); STDMETHOD(DisconnectFromServer)(); STDMETHOD(ConnectRemoteDesktop)(); STDMETHOD(DisconnectRemoteDesktop)(); STDMETHOD(get_IsRemoteDesktopConnected)(BOOL * pVal); STDMETHOD(get_IsServerConnected)(BOOL * pVal); STDMETHOD(put_EnableSmartSizing)(BOOL val); STDMETHOD(get_EnableSmartSizing)(BOOL *pVal); STDMETHOD(put_ColorDepth)(LONG Val); STDMETHOD(get_ColorDepth)(LONG* pVal); STDMETHOD(get_ExtendedErrorInfo)(LONG *error) { *error = m_LastExtendedErrorInfo; return S_OK; } STDMETHOD(get_ChannelManager)(ISAFRemoteDesktopChannelMgr **mgr) { *mgr = m_ChannelMgr; return S_OK; } STDMETHOD(put_ConnectParms)(/*[in]*/BSTR parms) { m_ConnectParms = parms; return S_OK; } STDMETHOD(get_ConnectParms)(/*[out, retval]*/BSTR *parms) { CComBSTR tmp; tmp = m_ConnectParms; *parms = tmp.Detach(); return S_OK; } // // Scriptable Event Object Registration Properties (not supported) // STDMETHOD(put_OnConnected)(/*[in]*/IDispatch *iDisp) { return E_FAIL; } STDMETHOD(put_OnDisconnected)(/*[in]*/IDispatch *iDisp) { return E_FAIL; } STDMETHOD(put_OnConnectRemoteDesktopComplete)(/*[in]*/IDispatch *iDisp) { return E_FAIL; } STDMETHOD(put_OnListenConnect)(/*[in]*/IDispatch *iDisp) { return E_FAIL; } STDMETHOD(put_OnBeginConnect)(/*[in]*/IDispatch *iDisp) { return E_FAIL; } // // IDataChannelIO Methods // STDMETHOD(SendData)(/*[in]*/BSTR data); STDMETHOD(put_ChannelMgr)(/*[in]*/ISAFRemoteDesktopChannelMgr *newVal); // // Return the name of this class. // virtual const LPTSTR ClassName() { return TEXT("CTSRDPRemoteDesktopServer"); } // // ISAFRemoteDesktopTestExtension // STDMETHOD(put_TestExtDllName)(/*[in]*/BSTR newVal); STDMETHOD(put_TestExtParams)(/*[in]*/BSTR newVal); STDMETHOD(get_ConnectedServer)(/*[in]*/BSTR* Val) { HRESULT hr = S_OK; if( m_ConnectedToServer ) { *Val = m_ConnectedServer.Copy(); } else { hr = E_FAIL; } return hr; } STDMETHOD(get_ConnectedPort)(/*[in]*/LONG* Val) { HRESULT hr = S_OK; if( m_ConnectedToServer ) { *Val = m_ConnectedPort; } else { hr = E_FAIL; } return hr; } STDMETHOD(CreateListenEndpoint)( /*[in]*/ LONG port, /*[out, retval]*/ BSTR* pConnectParm ); STDMETHOD(StartListen)( /*[in]*/ LONG timeout ); STDMETHOD(AcceptListenConnection)( /*[in]*/BSTR expertBlob ); // // Stop listening waiting for TS server (helpee, user) to connect. // STDMETHOD(StopListen)() { return StopListenInternal(); }; }; /////////////////////////////////////////////////////// // // CTSRDPRemoteDesktopClient Inline Methods // inline STDMETHODIMP CTSRDPRemoteDesktopClient::get_IsServerConnected( BOOL *pVal ) /*++ Routine Description: Indicates whether the client is connected to the server, excluding control over the remote user's desktop. Arguments: pVal - Set to TRUE if the client is connected to the server. Return Value: S_OK on success. Otherwise, an error code is returned. --*/ { DC_BEGIN_FN("CTSRDPRemoteDesktopClient::get_IsServerConnected"); HRESULT hr = S_OK; if (IsValid()) { *pVal = m_ConnectedToServer; } else { ASSERT(FALSE); hr = E_FAIL; } DC_END_FN(); return hr; } inline STDMETHODIMP CTSRDPRemoteDesktopClient::get_IsRemoteDesktopConnected( BOOL *pVal ) /*++ Routine Description: Indicates whether the control is currently controlling the remote user's desktop. Arguments: pVal - Sets to TRUE if the control is currently connected to the server. Return Value: S_OK on success. Otherwise, an error code is returned. --*/ { DC_BEGIN_FN("CTSRDPRemoteDesktopClient::get_IsRemoteDesktopConnected"); HRESULT hr = S_OK; if (IsValid()) { *pVal = m_RemoteControlRequestInProgress; } else { ASSERT(FALSE); hr = E_FAIL; } DC_END_FN(); return hr; } #endif //__TSRDPREMOTEDESKTOPCLIENT_H_