windows-nt/Source/XPSP1/NT/termsrv/remdsk/client/rdhost/tsrdpremotedesktopclient.cpp

3066 lines
72 KiB
C++
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
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:
--*/
#include "stdafx.h"
#ifdef TRC_FILE
#undef TRC_FILE
#endif
#define TRC_FILE "_tsrdpc"
#include "RDCHost.h"
#include "TSRDPRemoteDesktopClient.h"
#include <RemoteDesktopChannels.h>
#include <mstsax_i.c>
#include <TSRDPRemoteDesktop.h>
#include "pchannel.h"
#include <tsremdsk.h>
#include <sessmgr.h>
#include <sessmgr_i.c>
#include <regapi.h>
#include "parseaddr.h"
#include "icshelpapi.h"
#include <tsperf.h>
#include "base64.h"
#define ISRCSTATUSCODE(code) ((code) > SAFERROR_SHADOWEND_BASE)
//
// Variable to manage WinSock and ICS library startup/shutdown
//
LONG CTSRDPRemoteDesktopClient::gm_ListeningLibraryRefCount = 0; // Number of time that WinSock is intialized
HRESULT
CTSRDPRemoteDesktopClient::InitListeningLibrary()
/*++
Description:
Function to initialize WinSock and ICS library for StartListen(), function add
reference count to library if WinSock/ICS library already initialized.
Parameters:
None.
Returns:
S_OK or error code.
--*/
{
WSADATA wsaData;
WORD versionRequested;
INT intRC;
DWORD dwStatus;
HRESULT hr = S_OK;
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::InitListeningLibrary");
// Our COM object is apartment-threaded model, need a critical section if
// we switch to multi-threaded
if( gm_ListeningLibraryRefCount == 0 )
{
//
// Initialize WinSock.
//
versionRequested = MAKEWORD(1, 1);
intRC = WSAStartup(versionRequested, &wsaData);
if( intRC != 0 )
{
intRC = WSAGetLastError();
TRC_ERR((TB, _T("WSAStartup failed %d"), intRC));
TRC_ASSERT( (intRC == 0), (TB, _T("WSAStartup failed...\n")) );
hr = HRESULT_FROM_WIN32( intRC );
goto CLEANUPANDEXIT;
}
/************************************************************************/
/* Now confirm that this WinSock supports version 1.1. Note that if */
/* the DLL supports versions greater than 1.1 in addition to 1.1 then */
/* it will still return 1.1 in the version information as that is the */
/* version requested. */
/************************************************************************/
if ((LOBYTE(wsaData.wVersion) != 1) ||
(HIBYTE(wsaData.wVersion) != 1))
{
/********************************************************************/
/* Oops - this WinSock doesn't support version 1.1. */
/********************************************************************/
TRC_ERR((TB, _T("WinSock doesn't support version 1.1")));
WSACleanup();
hr = HRESULT_FROM_WIN32( WSAVERNOTSUPPORTED );
goto CLEANUPANDEXIT;
}
//
// Initialize ICS library.
//
dwStatus = StartICSLib();
if( ERROR_SUCCESS != dwStatus )
{
// Shutdown WinSock so that we have a matching WSAStatup() and StartICSLib().
WSACleanup();
hr = HRESULT_FROM_WIN32( dwStatus );
TRC_ERR((TB, _T("StartICSLib() failed with %d"), dwStatus));
TRC_ASSERT( (ERROR_SUCCESS == dwStatus), (TB, _T("StartICSLib() failed...\n")) );
goto CLEANUPANDEXIT;
}
}
InterlockedIncrement( &gm_ListeningLibraryRefCount );
CLEANUPANDEXIT:
DC_END_FN();
return hr;
}
HRESULT
CTSRDPRemoteDesktopClient::TerminateListeningLibrary()
/*++
Description:
Function to shutdown ICS libaray and WinSock, decrement reference count
if more than one object is referencing WinSock/ICS library.
Parameters:
None.
Returns:
S_OK or error code
Note:
Not multi-thread safe, need CRITICAL_SECTION if we switch to multi-threaded
model.
--*/
{
HRESULT hr = S_OK;
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::TerminateListeningLibrary");
ASSERT( gm_ListeningLibraryRefCount > 0 );
if( gm_ListeningLibraryRefCount <= 0 )
{
TRC_ERR((TB, _T("TerminateListeningLibrary() called before InitListeningLibrary()")));
hr = HRESULT_FROM_WIN32(WSANOTINITIALISED);
goto CLEANUPANDEXIT;
}
if( 0 == InterlockedDecrement( &gm_ListeningLibraryRefCount ) )
{
// Stop ICS libray.
StopICSLib();
// Shutdown WinSock
WSACleanup();
}
CLEANUPANDEXIT:
DC_END_FN();
return hr;
}
///////////////////////////////////////////////////////
//
// CMSTSCClientEventSink Methods
//
CMSTSCClientEventSink::~CMSTSCClientEventSink()
{
DC_BEGIN_FN("CMSTSCClientEventSink::~CMSTSCClientEventSink");
if (m_Obj) {
ASSERT(m_Obj->IsValid());
}
DC_END_FN();
}
//
// Event Sinks
//
HRESULT __stdcall
CMSTSCClientEventSink::OnRDPConnected()
{
m_Obj->OnRDPConnected();
return S_OK;
}
HRESULT __stdcall
CMSTSCClientEventSink::OnLoginComplete()
{
m_Obj->OnLoginComplete();
return S_OK;
}
HRESULT __stdcall
CMSTSCClientEventSink::OnDisconnected(
long disconReason
)
{
m_Obj->OnDisconnected(disconReason);
return S_OK;
}
void __stdcall CMSTSCClientEventSink::OnReceiveData(
BSTR chanName,
BSTR data
)
{
m_Obj->OnMSTSCReceiveData(data);
}
void __stdcall CMSTSCClientEventSink::OnReceivedTSPublicKey(
BSTR publicKey,
VARIANT_BOOL* pfbContinueLogon
)
{
m_Obj->OnReceivedTSPublicKey(publicKey, pfbContinueLogon);
}
///////////////////////////////////////////////////////
//
// CCtlChannelEventSink Methods
//
CCtlChannelEventSink::~CCtlChannelEventSink()
{
DC_BEGIN_FN("CCtlChannelEventSink::~CCtlChannelEventSink");
if (m_Obj) {
ASSERT(m_Obj->IsValid());
}
DC_END_FN();
}
//
// Event Sinks
//
void __stdcall
CCtlChannelEventSink::DataReady(BSTR channelName)
{
m_Obj->HandleControlChannelMsg();
}
///////////////////////////////////////////////////////
//
// CTSRDPRemoteDesktopClient Methods
//
HRESULT
CTSRDPRemoteDesktopClient::FinalConstruct()
{
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::FinalConstruct");
HRESULT hr = S_OK;
if (!AtlAxWinInit()) {
TRC_ERR((TB, L"AtlAxWinInit failed."));
hr = E_FAIL;
}
DC_END_FN();
return hr;
}
CTSRDPRemoteDesktopClient::~CTSRDPRemoteDesktopClient()
/*++
Routine Description:
The Destructor
Arguments:
Return Value:
--*/
{
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::~CTSRDPRemoteDesktopClient");
if (m_ChannelMgr) {
m_CtlChannelEventSink.DispEventUnadvise(m_CtlChannel);
}
if (m_TSClient != NULL) {
m_TSClient->Release();
m_TSClient = NULL;
}
if( m_TimerId > 0 ) {
KillTimer( m_TimerId );
}
ListenConnectCleanup();
if( m_InitListeningLibrary )
{
// Dereference listening library.
TerminateListeningLibrary();
}
DC_END_FN();
}
HRESULT
CTSRDPRemoteDesktopClient::Initialize(
LPCREATESTRUCT pCreateStruct
)
/*++
Routine Description:
Final Initialization
Arguments:
pCreateStruct - WM_CREATE, create struct.
Return Value:
S_OK on success. Otherwise, an error code is returned.
--*/
{
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::Initialize");
RECT rcClient = { 0, 0, pCreateStruct->cx, pCreateStruct->cy };
HRESULT hr;
IUnknown *pUnk = NULL;
DWORD result;
IMsRdpClientAdvancedSettings2 *advancedSettings;
CComBSTR bstr;
HKEY hKey = NULL;
HRESULT hrIgnore;
ASSERT(!m_Initialized);
//
// Create the client Window.
//
m_TSClientWnd = m_TSClientAxView.Create(
m_hWnd, rcClient, MSTSCAX_TEXTGUID,
WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN, 0
);
if (m_TSClientWnd == NULL) {
hr = HRESULT_FROM_WIN32(GetLastError());
TRC_ERR((TB, L"Window Create: %08X", GetLastError()));
goto CLEANUPANDEXIT;
}
//
// Get IUnknown
//
hr = AtlAxGetControl(m_TSClientWnd, &pUnk);
if (!SUCCEEDED(hr)) {
TRC_ERR((TB, L"AtlAxGetControl: %08X", hr));
pUnk = NULL;
goto CLEANUPANDEXIT;
}
//
// Initialize the event sink.
//
m_TSClientEventSink.m_Obj = this;
//
// Add the event sink.
//
hr = m_TSClientEventSink.DispEventAdvise(pUnk);
if (!SUCCEEDED(hr)) {
TRC_ERR((TB, L"DispEventAdvise: %08X", hr));
goto CLEANUPANDEXIT;
}
//
// Get the control.
//
hr = pUnk->QueryInterface(__uuidof(IMsRdpClient2), (void**)&m_TSClient);
if (!SUCCEEDED(hr)) {
TRC_ERR((TB, L"QueryInterface: %08X", hr));
goto CLEANUPANDEXIT;
}
//
// Specify that the MSTSC input handler window should accept background
// events.
//
hr = m_TSClient->get_AdvancedSettings3(&advancedSettings);
if (!SUCCEEDED(hr)) {
TRC_ERR((TB, L"IMsTscAdvancedSettings: %08X", hr));
goto CLEANUPANDEXIT;
}
hr = advancedSettings->put_allowBackgroundInput(1);
//
// Disable autoreconnect it doesn't apply to Salem
//
hr = advancedSettings->put_EnableAutoReconnect(VARIANT_FALSE);
if (!SUCCEEDED(hr)) {
TRC_ERR((TB, L"put_EnableAutoReconnect: %08X", hr));
result = E_FAIL;
goto CLEANUPANDEXIT;
}
//
// Disable advanced desktop features for the help session.
// An error here is not critical, so we ignore it.
//
LONG flags = TS_PERF_DISABLE_WALLPAPER | TS_PERF_DISABLE_THEMING;
hrIgnore = advancedSettings->put_PerformanceFlags(flags);
if (!SUCCEEDED(hrIgnore)) {
TRC_ERR((TB, L"put_PerformanceFlags: %08X", hrIgnore));
}
//
// Disable CTRL_ALT_BREAK, ignore error
//
hrIgnore = advancedSettings->put_HotKeyFullScreen(0);
if (!SUCCEEDED(hrIgnore)) {
TRC_ERR((TB, L"put_HotKeyFullScreen: %08X", hrIgnore));
}
//
// Don't allow mstscax to grab input focus on connect. Ignore error
// on failure.
//
hrIgnore = advancedSettings->put_GrabFocusOnConnect(FALSE);
if (!SUCCEEDED(hrIgnore)) {
TRC_ERR((TB, L"put_HotKeyFullScreen: %08X", hrIgnore));
}
advancedSettings->Release();
if (!SUCCEEDED(hr)) {
TRC_ERR((TB, L"put_allowBackgroundInput: %08X", hr));
goto CLEANUPANDEXIT;
}
//
// Create the "remote desktop" virtual channel with the TS Client.
//
bstr = TSRDPREMOTEDESKTOP_VC_CHANNEL;
hr = m_TSClient->CreateVirtualChannels(bstr);
if (!SUCCEEDED(hr)) {
TRC_ERR((TB, L"CreateVirtualChannels: %08X", hr));
result = E_FAIL;
goto CLEANUPANDEXIT;
}
//
// Set the Shadow Persistent option
//
hr = m_TSClient->SetVirtualChannelOptions(bstr, CHANNEL_OPTION_REMOTE_CONTROL_PERSISTENT);
if (!SUCCEEDED(hr)) {
TRC_ERR((TB, L"SetVirtualChannelOptions: %08X", hr));
result = E_FAIL;
goto CLEANUPANDEXIT;
}
//initialize timer-related stuff
m_PrevTimer = GetTickCount();
//
//get the time interval for pings from the registry
//
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,
REG_CONTROL_SALEM,
0,
KEY_READ,
&hKey
) == ERROR_SUCCESS ) {
DWORD dwSize = sizeof(DWORD);
DWORD dwType;
if((RegQueryValueEx(hKey,
RDC_CONNCHECK_ENTRY,
NULL,
&dwType,
(PBYTE) &m_RdcConnCheckTimeInterval,
&dwSize
) == ERROR_SUCCESS) && dwType == REG_DWORD ) {
m_RdcConnCheckTimeInterval *= 1000; //we need this in millisecs
}
else
{
//
//fall back to default, if reg lookup failed
//
m_RdcConnCheckTimeInterval = RDC_CHECKCONN_TIMEOUT;
}
}
CLEANUPANDEXIT:
if(NULL != hKey )
RegCloseKey(hKey);
//
// m_TSClient keeps our reference to the client object until
// the destructor is called.
//
if (pUnk != NULL) {
pUnk->Release();
}
SetValid(SUCCEEDED(hr));
DC_END_FN();
return hr;
}
STDMETHODIMP
CTSRDPRemoteDesktopClient::SendData(
BSTR data
)
/*++
Routine Description:
IDataChannelIO Data Channel Send Method
Arguments:
data - Data to send.
Return Value:
S_OK on success. Otherwise, an error code is returned.
--*/
{
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::SendData");
CComBSTR channelName;
HRESULT hr;
ASSERT(IsValid());
channelName = TSRDPREMOTEDESKTOP_VC_CHANNEL;
hr = m_TSClient->SendOnVirtualChannel(
channelName,
(BSTR)data
);
if (!SUCCEEDED(hr)) {
TRC_ERR((TB, L"SendOnVirtualChannel: %08X", hr));
}
//
//update timer
//
m_PrevTimer = GetTickCount();
DC_END_FN();
return hr;
}
STDMETHODIMP
CTSRDPRemoteDesktopClient::put_EnableSmartSizing(
BOOL val
)
/*++
Routine Description:
Enable/Disable Smart Sizing
Arguments:
val - TRUE for enable. FALSE, otherwise.
Return Value:
S_OK on success. Otherwise, an error code is returned.
--*/
{
HRESULT hr;
IMsRdpClientAdvancedSettings *pAdvSettings = NULL;
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::put_EnableSmartSizing");
if (!IsValid()) {
ASSERT(FALSE);
hr = E_FAIL;
goto CLEANUPANDEXIT;
}
hr = m_TSClient->get_AdvancedSettings2(&pAdvSettings);
if (hr != S_OK) {
TRC_ERR((TB, L"get_AdvancedSettings2: %08X", hr));
goto CLEANUPANDEXIT;
}
hr = pAdvSettings->put_SmartSizing(val ? VARIANT_TRUE : VARIANT_FALSE);
pAdvSettings->Release();
CLEANUPANDEXIT:
DC_END_FN();
return hr;
}
STDMETHODIMP
CTSRDPRemoteDesktopClient::get_EnableSmartSizing(
BOOL *pVal
)
/*++
Routine Description:
Enable/Disable Smart Sizing
Arguments:
val - TRUE for enable. FALSE, otherwise.
Return Value:
S_OK on success. Otherwise, an error code is returned.
--*/
{
HRESULT hr;
VARIANT_BOOL vb;
IMsRdpClientAdvancedSettings *pAdvSettings = NULL;
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::put_EnableSmartSizing");
if (!IsValid()) {
ASSERT(FALSE);
hr = E_FAIL;
goto CLEANUPANDEXIT;
}
hr = m_TSClient->get_AdvancedSettings2(&pAdvSettings);
if (hr != S_OK) {
TRC_ERR((TB, L"get_AdvancedSettings2: %08X", hr));
goto CLEANUPANDEXIT;
}
hr = pAdvSettings->get_SmartSizing(&vb);
*pVal = (vb != 0);
pAdvSettings->Release();
CLEANUPANDEXIT:
DC_END_FN();
return hr;
}
STDMETHODIMP
CTSRDPRemoteDesktopClient::put_ChannelMgr(
ISAFRemoteDesktopChannelMgr *newVal
)
/*++
Routine Description:
Assign the data channel manager interface.
Arguments:
newVal - Data Channel Manager
Return Value:
S_OK on success. Otherwise, an error code is returned.
--*/
{
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::put_ChannelMgr");
HRESULT hr = S_OK;
//
// We should get called one time.
//
ASSERT(m_ChannelMgr == NULL);
m_ChannelMgr = newVal;
//
// Register the Remote Desktop control channel
//
hr = m_ChannelMgr->OpenDataChannel(
REMOTEDESKTOP_RC_CONTROL_CHANNEL, &m_CtlChannel
);
if (!SUCCEEDED(hr)) {
goto CLEANUPANDEXIT;
}
//
// Register an event sink with the channel manager.
//
m_CtlChannelEventSink.m_Obj = this;
//
// Add the event sink.
//
hr = m_CtlChannelEventSink.DispEventAdvise(m_CtlChannel);
if (!SUCCEEDED(hr)) {
TRC_ERR((TB, L"DispEventAdvise: %08X", hr));
}
CLEANUPANDEXIT:
return hr;
}
HRESULT
CTSRDPRemoteDesktopClient::ConnectServerWithOpenedSocket()
/*++
Routine Description:
Connects the client component to the server-side Remote Desktop Host COM
Object with already opened socket.
Arguments:
None.
Returns:
S_OK or error code
--*/
{
HRESULT hr = S_OK;
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::ConnectServerWithSocket");
IMsRdpClientAdvancedSettings* ptsAdvSettings = NULL;
TRC_NRM((TB, L"ConnectServerWithOpenedSocket"));
ASSERT( INVALID_SOCKET != m_TSConnectSocket );
//
// Direct the MSTSCAX control to connect.
//
hr = m_TSClient->put_Server( m_ConnectedServer );
if (!SUCCEEDED(hr)) {
TRC_ERR((TB, L"put_Server: %ld", hr));
goto CLEANUPANDEXIT;
}
hr = m_TSClient->get_AdvancedSettings2( &ptsAdvSettings );
if( SUCCEEDED(hr) && ptsAdvSettings ) {
VARIANT var;
VariantClear(&var);
var.vt = VT_BYREF;
var.byref = (PVOID)m_TSConnectSocket;
hr = ptsAdvSettings->put_ConnectWithEndpoint( &var );
if( FAILED(hr) ) {
TRC_ERR((TB, _T("put_ConnectWithEndpoint failed - GLE:%x"), hr));
}
VariantClear(&var);
ptsAdvSettings->Release();
}
if( FAILED(hr) ) {
goto CLEANUPANDEXIT;
}
//
// mstscax owns this socket and will close it
//
m_TSConnectSocket = INVALID_SOCKET;
hr = m_TSClient->Connect();
if( FAILED(hr) ) {
TRC_ERR((TB, L"Connect: 0x%08x", hr));
goto CLEANUPANDEXIT;
}
CLEANUPANDEXIT:
DC_END_FN();
return hr;
}
HRESULT
CTSRDPRemoteDesktopClient::ConnectServerPort(
BSTR bstrServer,
LONG portNumber
)
/*++
Routine Description:
Connects the client component to the server-side Remote Desktop Host COM
Object with specific port number
Arguments:
bstrServer : Name or IP address of server.
portNumber : optional port number.
Return Value:
S_OK on success. Otherwise, an error code is returned.
--*/
{
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::ConnectServerPort");
HRESULT hr;
IMsRdpClientAdvancedSettings* ptsAdvSettings = NULL;
TRC_NRM((TB, L"ConnectServerPort %s %d", bstrServer, portNumber));
//
// Direct the MSTSCAX control to connect.
//
hr = m_TSClient->put_Server( bstrServer );
if (!SUCCEEDED(hr)) {
TRC_ERR((TB, L"put_Server: %ld", hr));
goto CLEANUPANDEXIT;
}
hr = m_TSClient->get_AdvancedSettings2( &ptsAdvSettings );
if( SUCCEEDED(hr) && ptsAdvSettings ) {
//
// Previous ConnectServerPort() might have set this port number
// other than 3389
//
hr = ptsAdvSettings->put_RDPPort(
(0 != portNumber) ? portNumber : TERMSRV_TCPPORT
);
if (FAILED(hr) ) {
TRC_ERR((TB, L"put_RDPPort failed: 0x%08x", hr));
}
ptsAdvSettings->Release();
}
else {
TRC_ERR((TB, L"get_AdvancedSettings2 failed: 0x%08x", hr));
}
//
// Failed the connection if we can't set the port number
//
if( FAILED(hr) )
{
goto CLEANUPANDEXIT;
}
m_ConnectedServer = bstrServer;
m_ConnectedPort = (0 != portNumber) ? portNumber : TERMSRV_TCPPORT;
hr = m_TSClient->Connect();
if( FAILED(hr) ) {
TRC_ERR((TB, L"Connect: 0x%08x", hr));
}
CLEANUPANDEXIT:
DC_END_FN();
return hr;
}
HRESULT
CTSRDPRemoteDesktopClient::SetupConnectionInfo(
BOOL bListenConnectInfo,
BSTR bstrExpertBlob
)
/*++
Routine Description:
Connects the client component to the server-side Remote Desktop Host COM
Object.
Arguments:
bstrExpertBlob : Optional parameter to be transmitted to SAF resolver.
Return Value:
S_OK on success. Otherwise, an error code is returned.
--*/
{
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::SetupConnectionInfo");
HRESULT hr = S_OK;
DWORD result;
DWORD protocolType;
IMsTscNonScriptable* ptsns = NULL;
IMsRdpClientAdvancedSettings* ptsAdvSettings = NULL;
IMsRdpClientSecuredSettings* ptsSecuredSettings = NULL;
CComBSTR bstrAssistantAccount;
CComBSTR bstrAccountDomainName;
CComBSTR machineAddressList;
VARIANT_BOOL bNotifyTSPublicKey;
//
// Parse the connection parameters.
//
result = ParseConnectParmsString(
m_ConnectParms,
&m_ConnectParmVersion,
&protocolType,
machineAddressList,
bstrAssistantAccount,
m_AssistantAccountPwd,
m_HelpSessionID,
m_HelpSessionName,
m_HelpSessionPwd,
m_TSSecurityBlob
);
if (result != ERROR_SUCCESS) {
hr = HRESULT_FROM_WIN32(result);
goto CLEANUPANDEXIT;
}
//
// If the protocol type doesn't match, then fail.
//
if (protocolType != REMOTEDESKTOP_TSRDP_PROTOCOL) {
TRC_ERR((TB, L"Invalid connection protocol %ld", protocolType));
hr = HRESULT_FROM_WIN32(ERROR_INVALID_USER_BUFFER);
goto CLEANUPANDEXIT;
}
if (bListenConnectInfo) {
m_ServerAddressList.clear();
}
else {
//
// Parse address list in connect parm.
//
result = ParseAddressList( machineAddressList, m_ServerAddressList );
if( ERROR_SUCCESS != result ) {
TRC_ERR((TB, L"Invalid address list 0x%08x", result));
hr = HRESULT_FROM_WIN32(result);
goto CLEANUPANDEXIT;
}
if( 0 == m_ServerAddressList.size() ) {
TRC_ERR((TB, L"Invalid connection address list"));
hr = HRESULT_FROM_WIN32(ERROR_INVALID_USER_BUFFER);
goto CLEANUPANDEXIT;
}
}
hr = m_TSClient->put_UserName(SALEMHELPASSISTANTACCOUNT_NAME);
if (!SUCCEEDED(hr)) {
TRC_ERR((TB, L"put_UserName: %ld", hr));
goto CLEANUPANDEXIT;
}
hr = m_TSClient->get_AdvancedSettings2( &ptsAdvSettings );
if( SUCCEEDED(hr) && ptsAdvSettings ) {
hr = ptsAdvSettings->put_DisableRdpdr( TRUE );
if (FAILED(hr) ) {
TRC_ERR((TB, L"put_DisableRdpdr failed: 0x%08x", hr));
}
// older version does not have security blob so don't notify us about
// TS public key.
bNotifyTSPublicKey = (VARIANT_BOOL)(m_ConnectParmVersion >= SALEM_CONNECTPARM_SECURITYBLOB_VERSION);
// tell activeX control to notify us TS public key
hr = ptsAdvSettings->put_NotifyTSPublicKey(bNotifyTSPublicKey);
if (FAILED(hr) ) {
TRC_ERR((TB, L"put_NotifyTSPublicKey failed: 0x%08x", hr));
goto CLEANUPANDEXIT;
}
ptsAdvSettings->Release();
}
else {
TRC_ERR((TB, L"QueryInterface IID_IMsRdpClientAdvancedSettings: %ld", hr));
}
//
// Setting connection timeout, ICS might take sometime to routine
// opened port to actual TS server, neither is critical error.
//
hr = ptsAdvSettings->put_singleConnectionTimeout( 60 * 2 ); // try two mins timeout
if( FAILED(hr) ) {
TRC_ERR((TB, L"put_singleConnectionTimeout : 0x%x", hr));
}
hr = ptsAdvSettings->put_overallConnectionTimeout( 60 * 2 );
if( FAILED(hr) ) {
TRC_ERR((TB, L"put_overallConnectionTimeout : 0x%x", hr));
}
// Password encryption is based on encyption cycle key + help session ID
hr = m_TSClient->get_SecuredSettings2( &ptsSecuredSettings );
if( FAILED(hr) || !ptsSecuredSettings ) {
TRC_ERR((TB, L"get_IMsTscSecuredSettings : 0x%08x", hr));
goto CLEANUPANDEXIT;
}
//
// TermSrv invoke sessmgr to check if help session is valid
// before kicking off rdsaddin.exe, we need to send over
// help session ID and password, only place available and big
// enough is on WorkDir and StartProgram property, TermSrv will
// ignore these and fill appropriate value for it
//
hr = ptsSecuredSettings->put_WorkDir( m_HelpSessionID );
if( FAILED(hr) ) {
TRC_ERR((TB, L"put_WorkDir: 0x%08x", hr));
goto CLEANUPANDEXIT;
}
hr = ptsSecuredSettings->put_StartProgram( m_HelpSessionPwd );
if( FAILED(hr) ) {
TRC_ERR((TB, L"put_StartProgram: 0x%08x", hr));
goto CLEANUPANDEXIT;
}
ptsSecuredSettings->Release();
// we only use this to disable redirection, not a critical
// error, just ugly
hr = m_TSClient->QueryInterface(IID_IMsTscNonScriptable,
(void**)&ptsns);
if(!SUCCEEDED(hr) || !ptsns){
TRC_ERR((TB, L"QueryInterface IID_IMsTscNonScriptable: %ld", hr));
goto CLEANUPANDEXIT;
}
// password in workdir, stuff something into password field
hr = ptsns->put_ClearTextPassword( m_AssistantAccountPwd );
if (!SUCCEEDED(hr)) {
TRC_ERR((TB, L"put_ClearTextPassword: 0x%08x", hr));
goto CLEANUPANDEXIT;
}
m_ExpertBlob = bstrExpertBlob;
//
// Instruct mstscax to connect with certain screen resolution,
// mstscax will default to 200x20 (???), min. is VGA size.
//
{
RECT rect;
LONG cx;
LONG cy;
GetClientRect(&rect);
cx = rect.right - rect.left;
cy = rect.bottom - rect.top;
if( cx < 640 || cy < 480 )
{
cx = 640;
cy = 480;
}
m_TSClient->put_DesktopWidth(cx);
m_TSClient->put_DesktopHeight(cy);
}
CLEANUPANDEXIT:
if(ptsns)
{
ptsns->Release();
ptsns = NULL;
}
DC_END_FN();
return hr;
}
STDMETHODIMP
CTSRDPRemoteDesktopClient::AcceptListenConnection(
BSTR bstrExpertBlob
)
/*++
Routine Description:
Establish reverse connection with TS server, TS server must be connected
wia reverse connection.
Parameters:
bstrExpertBlob : Same as ConnectToServer().
Returns:
S_OK or error code.
--*/
{
HRESULT hr = S_OK;
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::AcceptListenConnection");
//
// If we are already connected or not valid, then just
// return.
//
if (!IsValid()) {
ASSERT(FALSE);
hr = E_FAIL;
goto CLEANUPANDEXIT;
}
if (m_ConnectedToServer || m_ConnectionInProgress) {
TRC_ERR((TB, L"Connection active"));
hr = HRESULT_FROM_WIN32(ERROR_CONNECTION_ACTIVE);
goto CLEANUPANDEXIT;
}
if( !ListenConnectInProgress() ) {
TRC_ERR((TB, L"Connection in-active"));
hr = HRESULT_FROM_WIN32(ERROR_CONNECTION_INVALID);
goto CLEANUPANDEXIT;
}
if( INVALID_SOCKET == m_TSConnectSocket ) {
TRC_ERR((TB, L"Socket is not connected"));
hr = HRESULT_FROM_WIN32(ERROR_CONNECTION_INVALID);
goto CLEANUPANDEXIT;
}
hr = SetupConnectionInfo(TRUE, bstrExpertBlob);
if( FAILED(hr) ) {
TRC_ERR((TB, L"SetupConnectionInfo() failed with 0x%08x", hr));
goto CLEANUPANDEXIT;
}
hr = ConnectServerWithOpenedSocket();
CLEANUPANDEXIT:
m_ConnectionInProgress = SUCCEEDED(hr);
DC_END_FN();
return hr;
}
STDMETHODIMP
CTSRDPRemoteDesktopClient::ConnectToServer(BSTR bstrExpertBlob)
/*++
Routine Description:
Connects the client component to the server-side Remote Desktop Host COM
Object.
Arguments:
bstrExpertBlob : Optional parameter to be transmitted to SAF resolver.
Return Value:
S_OK on success. Otherwise, an error code is returned.
Params--*/
{
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::ConnectToServer");
HRESULT hr = S_OK;
ServerAddress address;
//
// If we are already connected or not valid, then just
// return.
//
if (!IsValid()) {
ASSERT(FALSE);
hr = E_FAIL;
goto CLEANUPANDEXIT;
}
if (m_ConnectedToServer || m_ConnectionInProgress) {
TRC_ERR((TB, L"Connection active"));
hr = HRESULT_FROM_WIN32(ERROR_CONNECTION_ACTIVE);
goto CLEANUPANDEXIT;
}
hr = SetupConnectionInfo(FALSE, bstrExpertBlob);
address = m_ServerAddressList.front();
m_ServerAddressList.pop_front();
hr = ConnectServerPort(address.ServerName, address.portNumber);
if (FAILED(hr)) {
TRC_ERR((TB, L"ConnectServerPort: %08X", hr));
}
CLEANUPANDEXIT:
//
// If we succeeded, remember that we are in a state of connecting.
//
m_ConnectionInProgress = SUCCEEDED(hr);
DC_END_FN();
return hr;
}
STDMETHODIMP
CTSRDPRemoteDesktopClient::DisconnectFromServer()
/*++
Routine Description:
Disconnects the client from the server to which we are currently
connected.
Arguments:
Return Value:
S_OK on success. Otherwise, an error code is returned.
--*/
{
return DisconnectFromServerInternal(
SAFERROR_LOCALNOTERROR
);
}
STDMETHODIMP
CTSRDPRemoteDesktopClient::DisconnectFromServerInternal(
LONG errorCode
)
/*++
Routine Description:
Disconnects the client from the server to which we are currently
connected.
Arguments:
reason - Reason for disconnect.
Return Value:
S_OK on success. Otherwise, an error code is returned.
--*/
{
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::DisconnectFromServerInternal");
HRESULT hr;
//
// Make sure our window is hidden.
//
//ShowWindow(SW_HIDE);
ListenConnectCleanup();
if (m_ConnectedToServer || m_ConnectionInProgress) {
hr = m_TSClient->Disconnect();
if (SUCCEEDED(hr)) {
m_ConnectionInProgress = FALSE;
m_ConnectedToServer = FALSE;
if (m_RemoteControlRequestInProgress) {
m_RemoteControlRequestInProgress = FALSE;
Fire_RemoteControlRequestComplete(SAFERROR_SHADOWEND_UNKNOWN);
}
//
// Fire the server disconnect event.
//
Fire_Disconnected(errorCode);
}
}
else {
TRC_NRM((TB, L"Not connected."));
hr = S_OK;
}
DC_END_FN();
return hr;
}
STDMETHODIMP
CTSRDPRemoteDesktopClient::ConnectRemoteDesktop()
/*++
Routine Description:
Once "remote desktop mode" has been enabled for the server-side Remote
Desktop Host COM Object and we are connected to the server, the
ConnectRemoteDesktop method can be invoked to take control of the remote
user's desktop.
Arguments:
Return Value:
S_OK on success. Otherwise, an error code is returned.
--*/
{
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::ConnectRemoteDesktop");
HRESULT hr = S_OK;
DWORD result;
BSTR rcRequest = NULL;
//
// Fail if we are not valid or not connected to the server.
//
if (!IsValid()) {
ASSERT(FALSE);
hr = E_FAIL;
goto CLEANUPANDEXIT;
}
if (!m_ConnectedToServer) {
hr = HRESULT_FROM_WIN32(ERROR_DEVICE_NOT_CONNECTED);
goto CLEANUPANDEXIT;
}
//
// Succeed if a remote control request is already in progress.
//
if (m_RemoteControlRequestInProgress) {
hr = S_OK;
goto CLEANUPANDEXIT;
}
//
// Generate the remote control connect request message.
//
hr = GenerateRCRequest(&rcRequest);
if (!SUCCEEDED(hr)) {
goto CLEANUPANDEXIT;
}
//
// Send it.
//
hr = m_CtlChannel->SendChannelData(rcRequest);
if (!SUCCEEDED(hr)) {
goto CLEANUPANDEXIT;
}
//
// A request is in progress, if we successfully sent the request.
//
m_RemoteControlRequestInProgress = TRUE;
CLEANUPANDEXIT:
if (rcRequest != NULL) {
SysFreeString(rcRequest);
}
DC_END_FN();
return hr;
}
STDMETHODIMP
CTSRDPRemoteDesktopClient::DisconnectRemoteDesktop()
/*++
Routine Description:
Once "remote desktop mode" has been enabled for the server-side Remote
Desktop Host COM Object and we are connected to the server, the
ConnectRemoteDesktop method can be invoked to take control of the remote
user's desktop.
Arguments:
Return Value:
S_OK on success. Otherwise, an error code is returned.
--*/
{
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::DisconnectRemoteDesktop");
HRESULT hr = S_OK;
CComBSTR rcRequest;
//
// Fail if we are not valid or not connected.
//
if (!IsValid()) {
ASSERT(FALSE);
hr = E_FAIL;
goto CLEANUPANDEXIT;
}
if (!m_ConnectedToServer) {
hr = HRESULT_FROM_WIN32(ERROR_DEVICE_NOT_CONNECTED);
goto CLEANUPANDEXIT;
}
//
// Generate the terminate remote control key sequence and sent it to the
// server.
//
if (m_RemoteControlRequestInProgress) {
hr = SendTerminateRCKeysToServer();
}
CLEANUPANDEXIT:
DC_END_FN();
return hr;
}
//
// ISAFRemoteDesktopTestExtension
//
STDMETHODIMP
CTSRDPRemoteDesktopClient::put_TestExtDllName(/*[in]*/ BSTR newVal)
{
HRESULT hr = E_NOTIMPL;
IMsTscAdvancedSettings *pMstscAdvSettings = NULL;
IMsTscDebug *pMstscDebug = NULL;
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::put_TestExtDllName" );
if ( NULL == m_TSClient )
{
TRC_ERR((TB, L"m_TSClient is NULL" ));
hr = E_NOINTERFACE;
goto CLEANUPANDEXIT;
}
hr = m_TSClient->get_AdvancedSettings( &pMstscAdvSettings );
if (FAILED( hr ))
{
TRC_ERR((TB, L"m_TSClient->get_AdvancedSettings failed %08X", hr ));
goto CLEANUPANDEXIT;
}
hr = m_TSClient->get_Debugger( &pMstscDebug );
if ( FAILED( hr ))
{
TRC_ERR((TB, L"m_TSClient->get_Debugger failed %08X", hr ));
goto CLEANUPANDEXIT;
}
hr = pMstscAdvSettings->put_allowBackgroundInput( 1 );
if (FAILED( hr ))
{
TRC_ERR((TB, L"put_allowBackgroundInput failed %08X", hr ));
}
pMstscDebug->put_CLXDll( newVal );
CLEANUPANDEXIT:
if ( NULL != pMstscAdvSettings )
pMstscAdvSettings->Release();
if ( NULL != pMstscDebug )
pMstscDebug->Release();
DC_END_FN();
return hr;
}
STDMETHODIMP
CTSRDPRemoteDesktopClient::put_TestExtParams(/*[in]*/ BSTR newVal)
{
HRESULT hr = E_NOTIMPL;
IMsTscDebug *pMstscDebug = NULL;
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::put_TestExtParams" );
if ( NULL == m_TSClient )
{
TRC_ERR((TB, L"m_TSClient is NULL" ));
hr = E_NOINTERFACE;
goto CLEANUPANDEXIT;
}
hr = m_TSClient->get_Debugger( &pMstscDebug );
if (FAILED( hr ))
{
TRC_ERR((TB, L"m_TSClient->get_Debugger failed %08X", hr ));
goto CLEANUPANDEXIT;
}
hr = pMstscDebug->put_CLXCmdLine( newVal );
CLEANUPANDEXIT:
if ( NULL != pMstscDebug )
pMstscDebug->Release();
DC_END_FN();
return hr;
}
VOID
CTSRDPRemoteDesktopClient::OnMSTSCReceiveData(
BSTR data
)
/*++
Routine Description:
Handle Remote Control Control Channel messages.
Arguments:
Return Value:
--*/
{
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::OnMSTSCReceiveData");
//
//we got some data, so we must be connected, update timer
//
m_PrevTimer = GetTickCount();
//
// Fire the data ready event.
//
Fire_DataReady(data);
DC_END_FN();
}
VOID
CTSRDPRemoteDesktopClient::HandleControlChannelMsg()
/*++
Routine Description:
Handle Remote Control Control Channel messages.
Arguments:
Return Value:
--*/
{
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::HandleControlChannelMsg");
PREMOTEDESKTOP_CTL_BUFHEADER msgHeader;
BSTR msg = NULL;
LONG *pResult;
BSTR authenticateReq = NULL;
BSTR versionInfoPacket = NULL;
HRESULT hr;
DWORD result;
ASSERT(IsValid());
//
// Read the next message.
//
result = m_CtlChannel->ReceiveChannelData(&msg);
if (result != ERROR_SUCCESS) {
goto CLEANUPANDEXIT;
}
//
// Dispatch, based on the message type.
//
msgHeader = (PREMOTEDESKTOP_CTL_BUFHEADER)msg;
//
// If the server-side of the VC link is alive.
//
//
if ((msgHeader->msgType == REMOTEDESKTOP_CTL_SERVER_ANNOUNCE) &&
m_ConnectionInProgress) {
//
// Send version information to the server.
//
hr = GenerateVersionInfoPacket(
&versionInfoPacket
);
if (!SUCCEEDED(hr)) {
goto CLEANUPANDEXIT;
}
hr = m_CtlChannel->SendChannelData(versionInfoPacket);
if (!SUCCEEDED(hr)) {
goto CLEANUPANDEXIT;
}
//
// Request client authentication.
//
hr = GenerateClientAuthenticateRequest(
&authenticateReq
);
if (!SUCCEEDED(hr)) {
goto CLEANUPANDEXIT;
}
hr = m_CtlChannel->SendChannelData(authenticateReq);
}
//
// If the message is from the server, indicating that it is
// disconnecting. This can happen if the RDSRemoteDesktopServer
// is directed to exit listening mode.
//
else if (msgHeader->msgType == REMOTEDESKTOP_CTL_DISCONNECT) {
TRC_NRM((TB, L"Server indicated a disconnect."));
DisconnectFromServerInternal(SAFERROR_BYSERVER);
}
//
// If the message is a message result.
//
else if (msgHeader->msgType == REMOTEDESKTOP_CTL_RESULT) {
pResult = (LONG *)(msgHeader+1);
//
// If a remote control request is in progress, then we should check
// for a remote control complete status.
//
if (m_RemoteControlRequestInProgress && ISRCSTATUSCODE(*pResult)) {
TRC_ERR((TB, L"Received RC terminate status code."));
m_RemoteControlRequestInProgress = FALSE;
Fire_RemoteControlRequestComplete(*pResult);
}
//
// Otherwise, if a connection is in progress, then the client
// authentication request must have succeeded.
//
else if (m_ConnectionInProgress) {
//
// Should not be getting a remote control status here.
//
ASSERT(!ISRCSTATUSCODE(*pResult));
//
// Fire connect request succeeded message.
//
if (*pResult == SAFERROR_NOERROR ) {
m_ConnectedToServer = TRUE;
m_ConnectionInProgress = FALSE;
//
//set the timer to check if the user is still connected
//ignore errors, worst case - the ui is up even after the user disconnects
//
if( m_RdcConnCheckTimeInterval )
m_TimerId = SetTimer(WM_CONNECTCHECK_TIMER, m_RdcConnCheckTimeInterval);
//
// Not in progress once connected
//
m_ListenConnectInProgress = FALSE;
m_TSConnectSocket = INVALID_SOCKET;
Fire_Connected();
}
//
// Otherwise, fire a disconnected event.
//
else {
DisconnectFromServerInternal(*pResult);
m_ConnectionInProgress = FALSE;
}
}
}
//
// We will ignore other packets to support forward compatibility.
//
CLEANUPANDEXIT:
//
// Release the message.
//
if (msg != NULL) {
SysFreeString(msg);
}
if (versionInfoPacket != NULL) {
SysFreeString(versionInfoPacket);
}
if (authenticateReq != NULL) {
SysFreeString(authenticateReq);
}
DC_END_FN();
}
HRESULT
CTSRDPRemoteDesktopClient::GenerateRCRequest(
BSTR *rcRequest
)
/*++
Routine Description:
Generate a remote control request message for the
server.
TODO: We might need to be able to push this up
to the parent class, if it makes sense for
NetMeeting.
Arguments:
rcRequest - Returned request message.
Return Value:
S_OK on success. Otherwise, an error code is returned.
--*/
{
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::GenerateRCRequest");
PREMOTEDESKTOP_CTL_BUFHEADER msgHeader;
PBYTE ptr;
HRESULT hr;
DWORD len;
len = sizeof(REMOTEDESKTOP_CTL_BUFHEADER) + ((m_ConnectParms.Length()+1) * sizeof(WCHAR));
msgHeader = (PREMOTEDESKTOP_CTL_BUFHEADER)SysAllocStringByteLen(NULL, len);
if (msgHeader != NULL) {
msgHeader->msgType = REMOTEDESKTOP_CTL_REMOTE_CONTROL_DESKTOP;
ptr = (PBYTE)(msgHeader + 1);
memcpy(ptr, (BSTR)m_ConnectParms,
((m_ConnectParms.Length()+1) * sizeof(WCHAR)));
*rcRequest = (BSTR)msgHeader;
hr = S_OK;
}
else {
TRC_ERR((TB, L"SysAllocStringByteLen failed for %ld bytes", len));
hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
}
DC_END_FN();
return hr;
}
HRESULT
CTSRDPRemoteDesktopClient::GenerateClientAuthenticateRequest(
BSTR *authenticateReq
)
/*++
Routine Description:
Generate a 'client authenticate' request.
TODO: We might need to be able to push this up
to the parent class, if it makes sense for
NetMeeting.
Arguments:
rcRequest - Returned request message.
Return Value:
S_OK on success. Otherwise, an error code is returned.
--*/
{
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::GenerateClientAuthenticateRequest");
PREMOTEDESKTOP_CTL_BUFHEADER msgHeader;
PBYTE ptr;
HRESULT hr;
DWORD len;
len = sizeof(REMOTEDESKTOP_CTL_BUFHEADER) + ((m_ConnectParms.Length()+1) * sizeof(WCHAR));
#if FEATURE_USERBLOBS
if( m_ExpertBlob.Length() > 0 ) {
len += ((m_ExpertBlob.Length() + 1) * sizeof(WCHAR));
}
#endif
msgHeader = (PREMOTEDESKTOP_CTL_BUFHEADER)SysAllocStringByteLen(NULL, len);
if (msgHeader != NULL) {
msgHeader->msgType = REMOTEDESKTOP_CTL_AUTHENTICATE;
ptr = (PBYTE)(msgHeader + 1);
memcpy(ptr, (BSTR)m_ConnectParms,
((m_ConnectParms.Length()+1) * sizeof(WCHAR)));
#if FEATURE_USERBLOBS
if( m_ExpertBlob.Length() > 0 ) {
ptr += ((m_ConnectParms.Length()+1) * sizeof(WCHAR));
memcpy(ptr, (BSTR)m_ExpertBlob,
((m_ExpertBlob.Length()+1) * sizeof(WCHAR)));
}
#endif
*authenticateReq = (BSTR)msgHeader;
hr = S_OK;
}
else {
TRC_ERR((TB, L"SysAllocStringByteLen failed for %ld bytes", len));
hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
}
DC_END_FN();
return hr;
}
HRESULT
CTSRDPRemoteDesktopClient::GenerateVersionInfoPacket(
BSTR *versionInfoPacket
)
/*++
Routine Description:
Generate a version information packet.
Arguments:
versionInfoPacket - Version Information Returned Packet
Return Value:
S_OK on success. Otherwise, an error code is returned.
--*/
{
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::GenerateVersionInfoPacket");
PREMOTEDESKTOP_CTL_BUFHEADER msgHeader;
PDWORD ptr;
HRESULT hr;
DWORD len;
len = sizeof(REMOTEDESKTOP_CTL_BUFHEADER) + (sizeof(DWORD) * 2);
msgHeader = (PREMOTEDESKTOP_CTL_BUFHEADER)SysAllocStringByteLen(NULL, len);
if (msgHeader != NULL) {
msgHeader->msgType = REMOTEDESKTOP_CTL_VERSIONINFO;
ptr = (PDWORD)(msgHeader + 1);
*ptr = REMOTEDESKTOP_VERSION_MAJOR; ptr++;
*ptr = REMOTEDESKTOP_VERSION_MINOR;
*versionInfoPacket = (BSTR)msgHeader;
hr = S_OK;
}
else {
TRC_ERR((TB, L"SysAllocStringByteLen failed for %ld bytes", len));
hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
}
DC_END_FN();
return hr;
}
VOID
CTSRDPRemoteDesktopClient::OnReceivedTSPublicKey(BSTR bstrPublicKey, VARIANT_BOOL* pfContinue)
{
DWORD dwStatus;
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::OnReceivedPublicKey");
CComBSTR bstrTSPublicKey;
if( m_ConnectParmVersion >= SALEM_CONNECTPARM_SECURITYBLOB_VERSION ) {
//
// hash TS public key send from client activeX control, reverse
// hashing from what we got in connect parm might not give us
// back the original value.
//
dwStatus = HashSecurityData(
(PBYTE) bstrPublicKey,
::SysStringByteLen(bstrPublicKey),
bstrTSPublicKey
);
if( ERROR_SUCCESS != dwStatus )
{
TRC_ERR((TB, L"Hashed Public Key Send from TS %s", bstrPublicKey));
TRC_ERR((TB, L"Hashed public Key in parm %s", m_TSSecurityBlob));
TRC_ERR((TB, L"HashSecurityData() failed with %d", dwStatus));
*pfContinue = FALSE;
}
else if( !(bstrTSPublicKey == m_TSSecurityBlob) )
{
TRC_ERR((TB, L"Hashed Public Key Send from TS %s", bstrPublicKey));
TRC_ERR((TB, L"Hashed public Key in parm %s", m_TSSecurityBlob));
*pfContinue = FALSE;
}
else
{
*pfContinue = TRUE;
}
}
else {
*pfContinue = TRUE;
}
DC_END_FN();
}
VOID
CTSRDPRemoteDesktopClient::OnRDPConnected()
{
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::OnRDPConnected");
Fire_BeginConnect();
DC_END_FN();
}
VOID
CTSRDPRemoteDesktopClient::OnLoginComplete()
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::OnLoginComplete");
//
// Clear server address list
//
m_ServerAddressList.clear();
//
// We got some event from the mstsc, so we must be connected, update timer
//
m_PrevTimer = GetTickCount();
CLEANUPANDEXIT:
DC_END_FN();
}
LONG
CTSRDPRemoteDesktopClient::TranslateMSTSCDisconnectCode(
DisconnectReasonCode disconReason,
ExtendedDisconnectReasonCode extendedReasonCode
)
/*++
Routine Description:
Translate an MSTSC disconnect code into a Salem disconnect
code.
Arguments:
disconReason - Disconnect Reason
extendedReasonCode - MSTSCAX Extended Reason Code
Return Value:
Salem disconnect code.
--*/
{
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::TranslateMSTSCDisconnectCode");
LONG ret;
BOOL handled;
//
// First check the extended error information.
// TODO: Need to keep track of additional values added by NadimA
// and company, here, before we ship.
//
if (extendedReasonCode != exDiscReasonNoInfo) {
//
// Record the extended error code, if given. Note that this may be
// overridden below if we have better information.
//
m_LastExtendedErrorInfo = extendedReasonCode;
//
// Check for a protocol error.
//
if ((extendedReasonCode >= exDiscReasonProtocolRangeStart) &&
(extendedReasonCode <= exDiscReasonProtocolRangeEnd)) {
ret = SAFERROR_RCPROTOCOLERROR;
goto CLEANUPANDEXIT;
}
}
//
// If the extended error information didn't help us.
//
switch(disconReason)
{
case disconnectReasonNoInfo : ret = SAFERROR_NOINFO;
break;
case 0xb08: // UI_ERR_NORMAL_DISCONNECT not defined in mstsax.idl
case disconnectReasonLocalNotError : ret = SAFERROR_LOCALNOTERROR;
break;
case disconnectReasonRemoteByUser : ret = SAFERROR_REMOTEBYUSER;
break;
case disconnectReasonByServer : ret = SAFERROR_BYSERVER;
break;
case disconnectReasonDNSLookupFailed2 : m_LastExtendedErrorInfo = disconReason;
case disconnectReasonDNSLookupFailed : ret = SAFERROR_DNSLOOKUPFAILED;
break;
case disconnectReasonOutOfMemory3 :
case disconnectReasonOutOfMemory2 : m_LastExtendedErrorInfo = disconReason;
case disconnectReasonOutOfMemory : ret = SAFERROR_OUTOFMEMORY;
break;
case disconnectReasonConnectionTimedOut : ret = SAFERROR_CONNECTIONTIMEDOUT;
break;
case disconnectReasonSocketConnectFailed :
case 0x904 : ret = SAFERROR_SOCKETCONNECTFAILED; // NadimA is adding to
// MSTSCAX IDL. This is a
// NL_ERR_TDFDCLOSE error.
break;
case disconnectReasonHostNotFound : ret = SAFERROR_HOSTNOTFOUND;
break;
case disconnectReasonWinsockSendFailed : ret = SAFERROR_WINSOCKSENDFAILED;
break;
case disconnectReasonInvalidIP : m_LastExtendedErrorInfo = disconReason;
case disconnectReasonInvalidIPAddr : ret = SAFERROR_INVALIDIPADDR;
break;
case disconnectReasonSocketRecvFailed : ret = SAFERROR_SOCKETRECVFAILED;
break;
case disconnectReasonInvalidEncryption : ret = SAFERROR_INVALIDENCRYPTION;
break;
case disconnectReasonGetHostByNameFailed : ret = SAFERROR_GETHOSTBYNAMEFAILED;
break;
case disconnectReasonLicensingFailed : m_LastExtendedErrorInfo = disconReason;
case disconnectReasonLicensingTimeout : ret = SAFERROR_LICENSINGFAILED;
break;
case disconnectReasonDecryptionError : ret = SAFERROR_DECRYPTIONERROR;
break;
case disconnectReasonServerCertificateUnpackErr : ret = SAFERROR_MISMATCHPARMS;
break;
default: ret = SAFERROR_RCUNKNOWNERROR;
m_LastExtendedErrorInfo = disconReason;
ASSERT(FALSE);
}
CLEANUPANDEXIT:
DC_END_FN();
return ret;
}
VOID
CTSRDPRemoteDesktopClient::OnDisconnected(
long disconReason
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::OnDisconnected");
HRESULT hr = E_HANDLE; // initialize an error code.
long clientReturnCode;
ExtendedDisconnectReasonCode extendedClientCode;
TRC_ERR((TB, L"Disconnected because %ld", disconReason));
m_TSClient->get_ExtendedDisconnectReason(&extendedClientCode);
clientReturnCode = TranslateMSTSCDisconnectCode(
(DisconnectReasonCode)disconReason,
extendedClientCode
);
// Go thru all remaining server:port, mstscax might return some
// error code that we don't understand.
if( m_ServerAddressList.size() > 0 ) {
ServerAddress address;
address = m_ServerAddressList.front();
m_ServerAddressList.pop_front();
hr = ConnectServerPort( address.ServerName, address.portNumber );
if (FAILED(hr)) {
TRC_ERR((TB, L"ConnectServerPort: %08X", hr));
}
}
//
// Return the error code from connecting to 'last' server to client
//
if( FAILED(hr) ) {
m_ServerAddressList.clear();
//
// Fire the server disconnect event, if we are really connected or
// we have a connection in progress.
//
if (m_ConnectedToServer || m_ConnectionInProgress) {
Fire_Disconnected(clientReturnCode);
}
m_ConnectedToServer = FALSE;
m_ConnectionInProgress = FALSE;
ListenConnectCleanup();
//
// Fire the remote control request failure event, if appropriate.
//
if (m_RemoteControlRequestInProgress) {
ASSERT(clientReturnCode != SAFERROR_NOERROR);
Fire_RemoteControlRequestComplete(SAFERROR_SHADOWEND_UNKNOWN);
m_RemoteControlRequestInProgress = FALSE;
}
}
DC_END_FN();
}
HRESULT
CTSRDPRemoteDesktopClient::SendTerminateRCKeysToServer()
/*++
Routine Description:
Send the terminate shadowing key sequence to the server.
Arguments:
Return Value:
S_OK on success. Otherwise, an error status is returned.
--*/
{
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::SendTerminateRCKeysToServer");
HRESULT hr = S_OK;
IMsRdpClientNonScriptable* pTscNonScript = NULL;
VARIANT_BOOL keyUp[] = {
VARIANT_FALSE, VARIANT_FALSE, VARIANT_TRUE, VARIANT_TRUE
};
LONG keyData[] = {
MapVirtualKey(VK_CONTROL, 0), // these are SCANCODES.
MapVirtualKey(VK_MULTIPLY, 0),
MapVirtualKey(VK_MULTIPLY, 0),
MapVirtualKey(VK_CONTROL, 0),
};
//
// Send the terminate keys to the server.
//
hr = m_TSClient->QueryInterface(
IID_IMsRdpClientNonScriptable,
(void**)&pTscNonScript
);
if (hr != S_OK) {
TRC_ERR((TB, L"QI: %08X", hr));
goto CLEANUPANDEXIT;
}
pTscNonScript->NotifyRedirectDeviceChange(0, 0);
pTscNonScript->SendKeys(4, keyUp, keyData);
if (hr != S_OK) {
TRC_ERR((TB, L"SendKeys, QI: %08X", hr));
}
pTscNonScript->Release();
CLEANUPANDEXIT:
DC_END_FN();
return hr;
}
HWND CTSRDPRemoteDesktopClient::SearchForWindow(
HWND hwndParent,
LPTSTR srchCaption,
LPTSTR srchClass
)
/*++
Routine Description:
Search for a child window of the specified parent window.
Arguments:
srchCaption - Window caption for which to search. NULL is
considered a wildcard.
srchClass - Window class for which to search. NULL is
considred a wildcard.
Return Value:
S_OK on success. Otherwise, an error status is returned.
--*/
{
WINSEARCH srch;
srch.foundWindow = NULL;
srch.srchCaption = srchCaption;
srch.srchClass = srchClass;
BOOL result = EnumChildWindows(
hwndParent,
(WNDENUMPROC)_WindowSrchProc,
(LPARAM)&srch
);
return srch.foundWindow;
}
BOOL CALLBACK
CTSRDPRemoteDesktopClient::_WindowSrchProc(HWND hwnd, PWINSEARCH srch)
{
TCHAR classname[128];
TCHAR caption[128];
if (srch->srchClass && !GetClassName(hwnd, classname, sizeof(classname) / sizeof(TCHAR)))
{
return TRUE;
}
if (srch->srchCaption && !::GetWindowText(hwnd, caption, sizeof(caption)/sizeof(TCHAR)))
{
return TRUE;
}
if ((!srch->srchClass || !_tcscmp(classname, srch->srchClass)
&&
(!srch->srchCaption || !_tcscmp(caption, srch->srchCaption)))
)
{
srch->foundWindow = hwnd;
return FALSE;
}
return TRUE;
}
HRESULT
CTSRDPRemoteDesktopClient::GenerateNullData( BSTR* pbstrData )
{
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::GenerateNullData");
HRESULT hr;
DWORD len;
PREMOTEDESKTOP_CTL_BUFHEADER msgHeader = NULL;
len = sizeof( REMOTEDESKTOP_CTL_BUFHEADER );
msgHeader = (PREMOTEDESKTOP_CTL_BUFHEADER)SysAllocStringByteLen(NULL, len);
if (msgHeader != NULL) {
msgHeader->msgType = REMOTEDESKTOP_CTL_ISCONNECTED;
//nothing else other than the message
*pbstrData = (BSTR)msgHeader;
hr = S_OK;
}
else {
TRC_ERR((TB, L"SysAllocStringByteLen failed for %ld bytes", len));
hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
}
DC_END_FN();
return hr;
}
LRESULT CTSRDPRemoteDesktopClient::OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::OnCheckConnectTimer");
BSTR bstrMsg = NULL;
if( WM_LISTENTIMEOUT_TIMER == wParam ) {
bHandled = TRUE;
if( TRUE == ListenConnectInProgress() ) {
StopListen();
Fire_ListenConnect( SAFERROR_CONNECTIONTIMEDOUT );
}
}
else if ( WM_CONNECTCHECK_TIMER == wParam ) {
DWORD dwCurTimer = GetTickCount();
bHandled = TRUE;
if(m_ConnectedToServer) {
//
//see if the timer wrapped around to zero (does so if the system was up 49.7 days or something)
//if so reset it
//
if( dwCurTimer > m_PrevTimer && ( dwCurTimer - m_PrevTimer >= m_RdcConnCheckTimeInterval )) {
//
//time to send a null data
//
if(SUCCEEDED(GenerateNullData(&bstrMsg))) {
if(!SUCCEEDED(m_CtlChannel->SendChannelData(bstrMsg))) {
//
//could not send data, assume disconnected
//
DisconnectFromServer();
//
//don't need the timer anymore, kill it
//
KillTimer( m_TimerId );
m_TimerId = 0;
}
}
}
} //m_ConnectedToServer
//
//update the timer
//
m_PrevTimer = dwCurTimer;
if( NULL != bstrMsg ) {
SysFreeString(bstrMsg);
}
}
DC_END_FN();
return 0;
}
STDMETHODIMP
CTSRDPRemoteDesktopClient::CreateListenEndpoint(
IN LONG port,
OUT BSTR* pConnectParm
)
/*++
Description:
Routine to create a listening socket and return connection parameter to this 'listen' socket.
Parameters:
port : Port that socket should listen on.
pConnectParm : Return connection parameter to this listening socket.
returns:
S_OK or error code.
Notes:
Function is async, return code, if error, is for listening thread set up, caller is notified of
successful or error in network connection via ListenConnect event.
--*/
{
HRESULT hr = S_OK;
SOCKET hListenSocket = INVALID_SOCKET;
IMsRdpClientAdvancedSettings* pAdvSettings;
LONG rdpPort = 0;
int intRC;
int lastError;
SOCKADDR_IN sockAddr;
int sockAddrSize;
int optvalue;
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::CreateListenEndpoint");
if( NULL == pConnectParm )
{
hr = E_POINTER;
return hr;
}
if (!IsValid()) {
ASSERT(FALSE);
hr = E_FAIL;
return hr;
}
//
// Return error if we are in progress of connect or connected.
//
if( TRUE == ListenConnectInProgress() || // Listen already started.
TRUE == m_ConnectionInProgress || // Connection already in progress
TRUE == m_ConnectedToServer ) { // Already connected to server
hr = HRESULT_FROM_WIN32( ERROR_SHARING_VIOLATION );
TRC_ERR((TB, L"StartListen() already in listen"));
goto CLEANUPANDEXIT;
}
//
// Initialize Winsock and ICS library if not yet initialized.
// InitListeningLibrary() will only add ref. count
// if library already initialized by other instance.
//
if( FALSE == m_InitListeningLibrary ) {
hr = InitListeningLibrary();
if( FAILED(hr) ) {
TRC_ERR((TB, L"InitListeningLibrary() failed : %08X", hr));
goto CLEANUPANDEXIT;
}
m_InitListeningLibrary = TRUE;
}
//
// mstscax will close the socket once connection is ended.
//
m_TSConnectSocket = INVALID_SOCKET;
//
// Create a listening socket
//
m_ListenSocket = socket(AF_INET, SOCK_STREAM, 0);
if( INVALID_SOCKET == m_ListenSocket ) {
intRC = WSAGetLastError();
TRC_ERR((TB, _T("socket failed %d"), intRC));
hr = HRESULT_FROM_WIN32(intRC);
goto CLEANUPANDEXIT;
}
//
// Disable NAGLE algorithm and enable don't linger option.
//
optvalue = 1;
setsockopt( m_ListenSocket, IPPROTO_TCP, TCP_NODELAY, (char *)&optvalue, sizeof(optvalue) );
optvalue = 1;
setsockopt( m_ListenSocket, SOL_SOCKET, SO_DONTLINGER, (char *)&optvalue, sizeof(optvalue) );
//
// Request async notifications to send to our window
//
intRC = WSAAsyncSelect(
m_ListenSocket,
m_hWnd,
WM_TSCONNECT,
FD_ACCEPT
);
if(SOCKET_ERROR == intRC) {
intRC = WSAGetLastError();
TRC_ERR((TB, _T("WSAAsyncSelect failed %d"), intRC));
hr = HRESULT_FROM_WIN32(intRC);
goto CLEANUPANDEXIT;
}
sockAddr.sin_family = AF_INET;
sockAddr.sin_port = htons(port);
sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);;
intRC = bind( m_ListenSocket, (struct sockaddr *) &sockAddr, sizeof(sockAddr) );
if( SOCKET_ERROR == intRC ) {
lastError = WSAGetLastError();
TRC_ERR((TB, _T("bind failed - %d"), lastError));
hr = HRESULT_FROM_WIN32( lastError );
goto CLEANUPANDEXIT;
}
if( 0 == port ) {
//
// Retrieve which port we are listening
//
sockAddrSize = sizeof( sockAddr );
intRC = getsockname(
m_ListenSocket,
(struct sockaddr *)&sockAddr,
&sockAddrSize
);
if( SOCKET_ERROR == intRC )
{
lastError = WSAGetLastError();
TRC_ERR((TB, _T("getsockname failed - GLE:%d"),lastError));
hr = HRESULT_FROM_WIN32( lastError );
goto CLEANUPANDEXIT;
}
m_ConnectedPort = ntohs(sockAddr.sin_port);
}
else {
m_ConnectedPort = port;
}
TRC_ERR((TB, _T("Listenin on port %d"),m_ConnectedPort));
//
// Tell ICS library to punch a hole thru ICS, no-op
// if not ICS configuration.
//
m_ICSPort = OpenPort( m_ConnectedPort );
//
// Retrieve connection parameters for this client (expert).
//
hr = RetrieveUserConnectParm( pConnectParm );
if( FAILED(hr) ) {
TRC_ERR((TB, _T("RetrieveUserConnectParm failed - 0x%08x"),hr));
}
CLEANUPANDEXIT:
if( FAILED(hr) ) {
StopListen();
}
DC_END_FN();
return hr;
}
STDMETHODIMP
CTSRDPRemoteDesktopClient::StopListen()
/*++
Description:
Stop listening waiting for TS server (helpee, user) to connect.
Parameters:
None.
Returns:
S_OK or error code.
--*/
{
HRESULT hr = S_OK;
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::StopListen");
if (!IsValid()) {
ASSERT(FALSE);
hr = E_FAIL;
return hr;
}
// End listening, either we are actually issued a listen() to socket
// or we just created the listen socket but not yet start listening
if( TRUE == ListenConnectInProgress() || INVALID_SOCKET != m_ListenSocket ) {
ListenConnectCleanup();
Fire_ListenConnect( SAFERROR_STOPLISTENBYUSER );
}
else {
TRC_ERR((TB, _T("StopListen called while not in listen mode")));
hr = HRESULT_FROM_WIN32( WSANOTINITIALISED );
}
DC_END_FN();
return hr;
}
LRESULT
CTSRDPRemoteDesktopClient::OnTSConnect(
UINT uMsg,
WPARAM wParam,
LPARAM lParam,
BOOL& bHandled
)
/*++
Routine Description:
Window Message Handler FD_ACCEPT from async. winsock.
Parameters:
Refer to async. winsock FD_ACCEPT.
Returns:
--*/
{
WORD eventWSA;
WORD errorWSA;
HRESULT hr = S_OK;
SOCKADDR_IN inSockAddr;
int inSockAddrSize;
SOCKET s;
DWORD dwStatus;
DWORD SafErrorCode = SAFERROR_NOERROR;
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::OnTSConnect");
eventWSA = WSAGETSELECTEVENT(lParam);
errorWSA = WSAGETSELECTERROR(lParam);
//
// MSDN : Message might already in our queue before we stop listen.
//
if( INVALID_SOCKET == m_ListenSocket || FALSE == ListenConnectInProgress() ) {
bHandled = TRUE;
return 0;
}
//
// we are not expecting event other than FD_CONNECT
//
if( eventWSA != FD_ACCEPT ) {
TRC_ERR((TB, _T("Expecting event %d got got %d"), FD_CONNECT, eventWSA));
return 0;
}
//
// Make sure we don't do anything other than our own socket
//
if( (SOCKET)wParam != m_ListenSocket ) {
TRC_ERR((TB, _T("Expecting listening socket %d got %d"), m_ListenSocket, wParam));
return 0;
}
//
// We handle the message
//
bHandled = TRUE;
//
// Error occurred, fire a error event.
//
if( 0 != errorWSA ) {
TRC_ERR((TB, _T("WSA socket listen failed : %d"), errorWSA));
hr = HRESULT_FROM_WIN32( errorWSA );
SafErrorCode = SAFERROR_SOCKETCONNECTFAILED;
goto CLEANUPANDEXIT;
}
inSockAddrSize = sizeof(inSockAddr);
m_TSConnectSocket = accept( m_ListenSocket,
(struct sockaddr DCPTR)&inSockAddr,
&inSockAddrSize
);
if( INVALID_SOCKET == m_TSConnectSocket ) {
dwStatus = WSAGetLastError();
hr = HRESULT_FROM_WIN32(dwStatus);
TRC_ERR((TB, _T("accept failed : %d"), dwStatus));
SafErrorCode = SAFERROR_SOCKETCONNECTFAILED;
goto CLEANUPANDEXIT;
}
//
// Cached connecting TS server IP address.
// m_ConnectPort is set at the time we bind socket
//
m_ConnectedServer = inet_ntoa(inSockAddr.sin_addr);
//
// Stop async. event notification now, accepted socket
// has same properties as original listening socket.
//
dwStatus = WSAAsyncSelect(
m_TSConnectSocket,
m_hWnd,
0,
0
);
//
// Not critical,
// listening socket.
//
if((DWORD)SOCKET_ERROR == dwStatus) {
TRC_ERR((TB, _T("WSAAsyncSelect resetting notification failed : %d"), dwStatus));
}
CLEANUPANDEXIT:
//
// Close listening socket and kill timer.
//
if( (UINT_PTR)0 != m_ListenTimeoutTimerID )
{
KillTimer( m_ListenTimeoutTimerID );
m_ListenTimeoutTimerID = (UINT_PTR)0;
}
if( INVALID_SOCKET != m_ListenSocket )
{
closesocket( m_ListenSocket );
m_ListenSocket = INVALID_SOCKET;
}
//
// Successfully established connection, terminate listening socket
//
Fire_ListenConnect( SafErrorCode );
DC_END_FN();
return 0;
}
STDMETHODIMP
CTSRDPRemoteDesktopClient::StartListen(
/*[in]*/ LONG timeout
)
/*++
Routine Description:
Put client into listen mode with optionally timeout.
Parameters:
timeout : Listen wait timeout, 0 for infinite.
Returns:
S_OK or error code.
--*/
{
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::OnTSConnect");
HRESULT hr = S_OK;
int intRC;
int lastError;
if( FALSE == IsValid() ) {
ASSERT(FALSE);
hr = E_FAIL;
goto CLEANUPANDEXIT;
}
if( INVALID_SOCKET == m_ListenSocket ) {
ASSERT(FALSE);
hr = E_FAIL;
goto CLEANUPANDEXIT;
}
//
// Start listening on the port
//
intRC = listen( m_ListenSocket, SOMAXCONN );
if( SOCKET_ERROR == intRC )
{
lastError = WSAGetLastError();
TRC_ERR((TB, _T("listen failed - GLE:%d"), lastError));
hr = HRESULT_FROM_WIN32( lastError );
goto CLEANUPANDEXIT;
}
//
// we are in listening now.
//
m_ListenConnectInProgress = TRUE;
//
// Start listening timer
//
if( 0 != timeout )
{
m_ListenTimeoutTimerID = SetTimer( (UINT_PTR)WM_LISTENTIMEOUT_TIMER, (UINT)(timeout * 1000) );
if( (UINT_PTR)0 == m_ListenTimeoutTimerID )
{
DWORD dwStatus;
// Failed to create a timer
dwStatus = GetLastError();
TRC_ERR((TB, _T("SetTimer failed - %d"),dwStatus));
hr = HRESULT_FROM_WIN32( dwStatus );
}
}
else
{
m_ListenTimeoutTimerID = (UINT_PTR)0;
}
CLEANUPANDEXIT:
if( FAILED(hr) ) {
StopListen();
}
DC_END_FN();
return hr;
}
HRESULT
CTSRDPRemoteDesktopClient::RetrieveUserConnectParm(
BSTR* pConnectParm
)
/*++
Routine Description:
Retrieve Salem connection parameter to this expert.
Parameters:
pConnectParm : Pointer to BSTR to receive connect parm.
Returns:
S_OK or error code.
--*/
{
LPTSTR pszAddress = NULL;
int BufSize = 0;
CComBSTR bstrConnParm;
DWORD dwRetry;
HRESULT hRes;
DWORD dwBufferRequire;
DWORD dwNumChars;
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::RetrieveUserConnectParm");
if( NULL == pConnectParm )
{
hRes = E_POINTER;
goto CLEANUPANDEXIT;
}
//
// Address might have change which might require bigger buffer, retry
//
//
for(dwRetry=0; dwRetry < MAX_FETCHIPADDRESSRETRY; dwRetry++) {
if( NULL != pszAddress ) {
LocalFree( pszAddress );
}
//
// Fetch all address on local machine.
//
dwBufferRequire = FetchAllAddresses( NULL, 0 );
if( 0 == dwBufferRequire ) {
hRes = E_UNEXPECTED;
ASSERT(FALSE);
goto CLEANUPANDEXIT;
}
pszAddress = (LPTSTR) LocalAlloc( LPTR, sizeof(TCHAR)*(dwBufferRequire+1) );
if( NULL == pszAddress ) {
hRes = E_OUTOFMEMORY;
goto CLEANUPANDEXIT;
}
dwNumChars = FetchAllAddresses( pszAddress, dwBufferRequire );
ASSERT( dwNumChars <= dwBufferRequire );
if( dwNumChars <= dwBufferRequire ) {
break;
}
}
if( NULL == pszAddress ) {
hRes = E_UNEXPECTED;
goto CLEANUPANDEXIT;
}
bstrConnParm = pszAddress;
*pConnectParm = bstrConnParm.Copy();
if( NULL == *pConnectParm ) {
hRes = E_OUTOFMEMORY;
}
CLEANUPANDEXIT:
if( NULL != pszAddress ) {
LocalFree(pszAddress);
}
DC_END_FN();
return hRes;
}
STDMETHODIMP
CTSRDPRemoteDesktopClient::put_ColorDepth(
LONG val
)
/*++
Routine Description:
Set Color depth
Arguments:
val - Value in bits perpel to set
Return Value:
S_OK on success. Otherwise, an error code is returned.
--*/
{
HRESULT hr;
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::put_ColorDepth");
if (!IsValid()) {
ASSERT(FALSE);
hr = E_FAIL;
goto CLEANUPANDEXIT;
}
hr = m_TSClient->put_ColorDepth(val);
if (hr != S_OK) {
TRC_ERR((TB, L"put_ColorDepth: %08X", hr));
goto CLEANUPANDEXIT;
}
CLEANUPANDEXIT:
DC_END_FN();
return hr;
}
STDMETHODIMP
CTSRDPRemoteDesktopClient::get_ColorDepth(
LONG *pVal
)
/*++
Routine Description:
Get Color depth
Arguments:
pVal - address to place the colordepth value in
Return Value:
S_OK on success. Otherwise, an error code is returned.
--*/
{
HRESULT hr;
IMsRdpClientAdvancedSettings *pAdvSettings = NULL;
DC_BEGIN_FN("CTSRDPRemoteDesktopClient::get_ColorDepth");
if (!IsValid()) {
ASSERT(FALSE);
hr = E_FAIL;
goto CLEANUPANDEXIT;
}
hr = m_TSClient->get_ColorDepth(pVal);
if (hr != S_OK) {
TRC_ERR((TB, L"get_ColorDepth: %08X", hr));
goto CLEANUPANDEXIT;
}
CLEANUPANDEXIT:
DC_END_FN();
return hr;
}