You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1316 lines
34 KiB
1316 lines
34 KiB
//comp.cpp: ts mmc snapin implementaion of IComponent
|
|
//Copyright (c) 1999 - 2000 Microsoft Corporation
|
|
//nadima
|
|
//
|
|
|
|
#include "stdafx.h"
|
|
#include "tsmmc.h"
|
|
#include "compdata.h"
|
|
#include "comp.h"
|
|
#include "connode.h"
|
|
#include "property.h"
|
|
|
|
#define MSTSC_MULTI_HOST_CONTROL L"{85C67146-6932-427C-A6F2-43FDBADF2BFC}"
|
|
#define IMAGE_MACHINE 1
|
|
#define IMAGE_CONNECTED_MACHINE 2
|
|
#define IMAGE_MACHINES 3
|
|
|
|
CComp::CComp()
|
|
{
|
|
m_pConsole = NULL;
|
|
m_pCompdata = NULL;
|
|
m_bFlag = FALSE;
|
|
m_pImageResult = NULL;
|
|
m_pConsoleVerb = NULL;
|
|
m_pDisplayHelp = NULL;
|
|
m_bTriggeredFirstAutoConnect = FALSE;
|
|
}
|
|
|
|
//
|
|
// Destructor
|
|
//
|
|
CComp::~CComp()
|
|
{
|
|
}
|
|
|
|
STDMETHODIMP CComp::Initialize( LPCONSOLE pConsole)
|
|
{
|
|
HRESULT hr;
|
|
USES_CONVERSION;
|
|
|
|
if (m_pConsole) {
|
|
m_pConsole->Release();
|
|
}
|
|
m_pConsole = pConsole;
|
|
m_pConsole->AddRef();
|
|
|
|
if (FAILED((hr = m_pConsole->QueryResultImageList( &m_pImageResult ))))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
if ( FAILED((hr = m_pConsole->QueryConsoleVerb( &m_pConsoleVerb))))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
if( FAILED((hr = m_pConsole->QueryInterface( IID_IDisplayHelp, (LPVOID *)&m_pDisplayHelp))))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Load connecting text
|
|
//
|
|
TCHAR sz[MAX_PATH];
|
|
if(!LoadString(_Module.GetResourceInstance(),
|
|
IDS_STATUS_CONNECTING,
|
|
sz,
|
|
SIZE_OF_BUFFER( m_wszConnectingStatus )))
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
OLECHAR* wszConnecting = T2OLE(sz);
|
|
if(wszConnecting)
|
|
{
|
|
wcsncpy(m_wszConnectingStatus, wszConnecting,
|
|
SIZE_OF_BUFFER( m_wszConnectingStatus ));
|
|
}
|
|
else
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
//
|
|
// Load connected text
|
|
//
|
|
if(!LoadString(_Module.GetResourceInstance(),
|
|
IDS_STATUS_CONNECTED,
|
|
sz,
|
|
SIZE_OF_BUFFER( m_wszConnectedStatus )))
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
OLECHAR* wszConnected = T2OLE(sz);
|
|
if(wszConnected)
|
|
{
|
|
wcsncpy(m_wszConnectedStatus, wszConnected,
|
|
SIZE_OF_BUFFER( m_wszConnectedStatus ));
|
|
}
|
|
else
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
//
|
|
// Load disconnected text
|
|
//
|
|
if(!LoadString(_Module.GetResourceInstance(),
|
|
IDS_STATUS_DISCONNECTED,
|
|
sz,
|
|
SIZE_OF_BUFFER( m_wszDisconnectedStatus )))
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
OLECHAR* wszDiscon = T2OLE(sz);
|
|
if(wszDiscon)
|
|
{
|
|
wcsncpy(m_wszDisconnectedStatus, wszDiscon,
|
|
SIZE_OF_BUFFER( m_wszDisconnectedStatus ));
|
|
}
|
|
else
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
STDMETHODIMP CComp::Notify( LPDATAOBJECT pDataObj , MMC_NOTIFY_TYPE event, LPARAM arg , LPARAM )
|
|
{
|
|
switch ( event )
|
|
{
|
|
case MMCN_ACTIVATE:
|
|
ODS( L"IComponent -- MMCN_ACTIVATE\n" );
|
|
break;
|
|
|
|
case MMCN_ADD_IMAGES:
|
|
ODS( L"IComponent -- MMCN_ADD_IMAGES\n" );
|
|
OnAddImages( );
|
|
break;
|
|
|
|
case MMCN_BTN_CLICK:
|
|
ODS( L"IComponent -- MMCN_BTN_CLICK\n" );
|
|
break;
|
|
|
|
case MMCN_CLICK:
|
|
ODS( L"IComponent -- MMCN_CLICK\n" );
|
|
break;
|
|
|
|
case MMCN_DBLCLICK:
|
|
ODS( L"IComponent -- MMCN_DBLCLICK\n" );
|
|
return S_FALSE;
|
|
|
|
case MMCN_DELETE:
|
|
ODS( L"IComponent -- MMCN_DELETE\n" );
|
|
break;
|
|
|
|
case MMCN_EXPAND:
|
|
ODS( L"IComponent -- MMCN_EXPAND\n" );
|
|
break;
|
|
|
|
case MMCN_MINIMIZED:
|
|
ODS( L"IComponent -- MMCN_MINIMIZED\n" );
|
|
break;
|
|
|
|
case MMCN_PROPERTY_CHANGE:
|
|
ODS( L"IComponent -- MMCN_PROPERTY_CHANGE\n" );
|
|
break;
|
|
|
|
case MMCN_REMOVE_CHILDREN:
|
|
ODS( L"IComponent -- MMCN_REMOVE_CHILDREN\n" );
|
|
break;
|
|
|
|
case MMCN_REFRESH:
|
|
ODS( L"IComponent -- MMCN_REFRESH\n" );
|
|
break;
|
|
|
|
case MMCN_RENAME:
|
|
ODS( L"IComponent -- MMCN_RENAME\n" );
|
|
break;
|
|
|
|
case MMCN_SELECT:
|
|
ODS( L"IComponent -- MMCN_SELECT\n" );
|
|
if(!IS_SPECIAL_DATAOBJECT(pDataObj))
|
|
{
|
|
OnSelect( pDataObj , ( BOOL )LOWORD( arg ) , ( BOOL )HIWORD( arg ) );
|
|
}
|
|
break;
|
|
|
|
case MMCN_SHOW:
|
|
OnShow( pDataObj , ( BOOL )arg );
|
|
ODS( L"IComponent -- MMCN_SHOW\n" );
|
|
break;
|
|
|
|
case MMCN_VIEW_CHANGE:
|
|
ODS( L"IComponent -- MMCN_VIEW_CHANGE\n" );
|
|
break;
|
|
|
|
case MMCN_CONTEXTHELP:
|
|
ODS( L"IComponent -- MMCN_CONTEXTHELP\n" );
|
|
OnHelp( pDataObj );
|
|
break;
|
|
|
|
case MMCN_SNAPINHELP:
|
|
ODS( L"IComponent -- MMCN_SNAPINHELP\n" );
|
|
break;
|
|
|
|
default:
|
|
ODS( L"CComp::Notify - event not registered\n" );
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
STDMETHODIMP CComp::Destroy( MMC_COOKIE )
|
|
{
|
|
if (m_pConsole) {
|
|
m_pConsole->Release();
|
|
m_pConsole = NULL;
|
|
}
|
|
|
|
if (m_pConsoleVerb) {
|
|
m_pConsoleVerb->Release();
|
|
m_pConsoleVerb = NULL;
|
|
}
|
|
|
|
if( m_pDisplayHelp != NULL ) {
|
|
m_pDisplayHelp->Release();
|
|
m_pDisplayHelp = NULL;
|
|
}
|
|
|
|
if (m_pImageResult) {
|
|
m_pImageResult->Release();
|
|
m_pImageResult = NULL;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
STDMETHODIMP CComp::GetResultViewType( MMC_COOKIE ck , LPOLESTR *ppOlestr , PLONG plView )
|
|
{
|
|
//
|
|
// For the connection nodes return the MSTSC activex multi-host client.
|
|
// No view for the root node
|
|
//
|
|
CBaseNode* pNode = (CBaseNode*) ck;
|
|
if (!ck || pNode->GetNodeType() == MAIN_NODE)
|
|
{
|
|
//
|
|
// Root node
|
|
//
|
|
*plView = MMC_VIEW_OPTIONS_NONE;
|
|
|
|
//
|
|
// indicate a standard list view should be used.
|
|
//
|
|
return S_FALSE;
|
|
}
|
|
else
|
|
{
|
|
TCHAR tchGUID[] = MSTSC_MULTI_HOST_CONTROL;
|
|
*ppOlestr = ( LPOLESTR )CoTaskMemAlloc( sizeof( tchGUID ) + sizeof( TCHAR ) );
|
|
ASSERT(*ppOlestr);
|
|
if(!*ppOlestr)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
lstrcpy( ( LPTSTR )*ppOlestr , tchGUID );
|
|
*plView = MMC_VIEW_OPTIONS_NOLISTVIEWS;
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
STDMETHODIMP CComp::QueryDataObject( MMC_COOKIE ck , DATA_OBJECT_TYPES dtype , LPDATAOBJECT *ppDataObject )
|
|
{
|
|
if ( dtype == CCT_RESULT )
|
|
{
|
|
*ppDataObject = reinterpret_cast< LPDATAOBJECT >( ck );
|
|
if ( *ppDataObject != NULL )
|
|
{
|
|
( ( LPDATAOBJECT )*ppDataObject)->AddRef( );
|
|
}
|
|
}
|
|
else if ( m_pCompdata != NULL )
|
|
{
|
|
return m_pCompdata->QueryDataObject( ck , dtype , ppDataObject );
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
STDMETHODIMP CComp::GetDisplayInfo( LPRESULTDATAITEM pItem )
|
|
{
|
|
CBaseNode* pNode = (CBaseNode*) pItem->lParam;
|
|
if ( pNode->GetNodeType() == CONNECTION_NODE )
|
|
{
|
|
CConNode* conNode = (CConNode*) pNode;
|
|
if ( pItem->mask & RDI_STR )
|
|
{
|
|
pItem->str = conNode->GetDescription();
|
|
}
|
|
if (pItem->mask & RDI_IMAGE)
|
|
{
|
|
pItem->nImage = IMAGE_MACHINE;
|
|
}
|
|
}
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
BOOL CComp::OnAddImages( )
|
|
{
|
|
HRESULT hr;
|
|
HICON hiconMachine = LoadIcon( _Module.GetResourceInstance( ) , MAKEINTRESOURCE( IDI_ICON_MACHINE ) );
|
|
HICON hiconConnectedMachine = LoadIcon( _Module.GetResourceInstance( ) , MAKEINTRESOURCE( IDI_ICON_CONNECTED_MACHINE ) );
|
|
HICON hiconMachines = LoadIcon( _Module.GetResourceInstance( ) , MAKEINTRESOURCE( IDI_ICON_MACHINES ) );
|
|
|
|
ASSERT(m_pImageResult);
|
|
if(!m_pImageResult)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
hr = m_pImageResult->ImageListSetIcon( ( PLONG_PTR )hiconMachine , IMAGE_MACHINE );
|
|
if (FAILED(hr))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
hr = m_pImageResult->ImageListSetIcon( ( PLONG_PTR )hiconConnectedMachine , IMAGE_CONNECTED_MACHINE );
|
|
if (FAILED(hr))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
hr = m_pImageResult->ImageListSetIcon( ( PLONG_PTR )hiconMachines , IMAGE_MACHINES );
|
|
if (FAILED(hr))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
BOOL CComp::OnHelp( LPDATAOBJECT pDo )
|
|
{
|
|
TCHAR tchTopic[ 80 ];
|
|
|
|
HRESULT hr = E_FAIL;
|
|
|
|
if( pDo == NULL || m_pDisplayHelp == NULL )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
if(LoadString(_Module.GetResourceInstance(),
|
|
IDS_TSCMMCHELPTOPIC,
|
|
tchTopic,
|
|
SIZE_OF_BUFFER( tchTopic )))
|
|
{
|
|
hr = m_pDisplayHelp->ShowTopic( tchTopic );
|
|
}
|
|
return ( SUCCEEDED( hr ) ? TRUE : FALSE );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
STDMETHODIMP CComp::CompareObjects( LPDATAOBJECT , LPDATAOBJECT )
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
HRESULT CComp::InsertItemsinResultPane( )
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
HRESULT CComp::AddSettingsinResultPane( )
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
HRESULT CComp::OnSelect( LPDATAOBJECT pdo , BOOL bScope , BOOL bSelected )
|
|
{
|
|
UNREFERENCED_PARAMETER(bScope);
|
|
CBaseNode *pNode = static_cast< CBaseNode * >( pdo );
|
|
if ( pNode == NULL )
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
|
|
ASSERT(!IS_SPECIAL_DATAOBJECT(pdo));
|
|
if(IS_SPECIAL_DATAOBJECT(pdo))
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
if ( m_pConsoleVerb == NULL )
|
|
{
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
// Item is being deselected and we're not interested currently
|
|
if ( !bSelected )
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
if ( pNode->GetNodeType() == CONNECTION_NODE)
|
|
{
|
|
//
|
|
// Enable the delete verb for connection nodes
|
|
//
|
|
HRESULT hr;
|
|
hr=m_pConsoleVerb->SetVerbState( MMC_VERB_DELETE , ENABLED , TRUE );
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
hr=m_pConsoleVerb->SetVerbState( MMC_VERB_PROPERTIES , ENABLED , TRUE );
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
hr=m_pConsoleVerb->SetDefaultVerb(MMC_VERB_PROPERTIES);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
HRESULT CComp::SetCompdata( CCompdata *pCompdata )
|
|
{
|
|
m_pCompdata = pCompdata;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//
|
|
// Defered callback to async trigger a connection
|
|
// This works because the DeferredCallBackProc is called on MMC's main
|
|
// thread and MMC is APARTMENT threaded so we can make calls on MMC
|
|
// interfaces form this thread.
|
|
//
|
|
// This whole DeferredCallBack thing is a hack to fix #203217. Basically
|
|
// on autolaunch MMC loads the snapin and then maximizes the window which
|
|
// means we would connect at the wrong size (if option to match container
|
|
// size was chosen). This Deferred mechanism ensures MMC has the time
|
|
// to size the result pane correctly first.
|
|
//
|
|
//
|
|
//
|
|
// OnShow below sneaks a pointer to deferd connection info in idEvent
|
|
//
|
|
VOID CALLBACK DeferredCallBackProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
|
|
{
|
|
PTSSNAPIN_DEFER_CONNECT pDeferredConnectInfo = NULL;
|
|
IMsRdpClient* pTs = NULL;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
KillTimer( hwnd, idEvent);
|
|
|
|
if(idEvent)
|
|
{
|
|
pDeferredConnectInfo = (PTSSNAPIN_DEFER_CONNECT) idEvent;
|
|
if(pDeferredConnectInfo)
|
|
{
|
|
ASSERT(pDeferredConnectInfo->pComponent);
|
|
ASSERT(pDeferredConnectInfo->pConnectionNode);
|
|
|
|
DBGMSG(L"Triggering deferred connection on connode %p",
|
|
pDeferredConnectInfo->pConnectionNode);
|
|
|
|
pTs = pDeferredConnectInfo->pConnectionNode->GetTsClient();
|
|
if(pTs)
|
|
{
|
|
hr = pDeferredConnectInfo->pComponent->ConnectWithNewSettings(
|
|
pTs, pDeferredConnectInfo->pConnectionNode);
|
|
}
|
|
|
|
//
|
|
// Done with the defered connection info, free it
|
|
//
|
|
LocalFree( pDeferredConnectInfo );
|
|
pDeferredConnectInfo = NULL;
|
|
}
|
|
}
|
|
|
|
if(pTs)
|
|
{
|
|
pTs->Release();
|
|
pTs = NULL;
|
|
}
|
|
|
|
DBGMSG(L"DeferredConnect status: 0x%x",hr);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Called when a node is selected. Manages activation of new TS client instances
|
|
// and once they are 'hot' switching back to a running instance if a node is
|
|
// reselected.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
HRESULT CComp::OnShow( LPDATAOBJECT pDataobject , BOOL bSelect )
|
|
{
|
|
HRESULT hr = S_FALSE;
|
|
IUnknown* pUnk = NULL;
|
|
IMstscMhst* pTsMultiHost = NULL;
|
|
IMsRdpClient* pTS = NULL;
|
|
PTSSNAPIN_DEFER_CONNECT pDeferredConnectInfo = NULL;
|
|
HWND hwndMain = NULL;
|
|
|
|
USES_CONVERSION;
|
|
ODS(L"OnShow\n");
|
|
|
|
if(!bSelect)
|
|
{
|
|
//
|
|
// Don't need to do any processing for deselect
|
|
//
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// Only do this for connection nodes
|
|
//
|
|
if (((CBaseNode*) pDataobject)->GetNodeType() == MAIN_NODE)
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
|
|
CConNode* pConNode = (CConNode*) pDataobject;
|
|
ASSERT(pConNode);
|
|
if (!pConNode)
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
|
|
if ( m_pConsole != NULL )
|
|
{
|
|
hr= m_pConsole->QueryResultView( ( LPUNKNOWN * )&pUnk );
|
|
if(FAILED(hr))
|
|
{
|
|
goto FN_EXIT_POINT;
|
|
}
|
|
|
|
pTsMultiHost = pConNode->GetMultiHostCtl();
|
|
if(NULL == pTsMultiHost)
|
|
{
|
|
hr = pUnk->QueryInterface( __uuidof(IMstscMhst), (LPVOID*) &pTsMultiHost);
|
|
if(FAILED(hr))
|
|
{
|
|
goto FN_EXIT_POINT;
|
|
}
|
|
|
|
pConNode->SetMultiHostCtl( pTsMultiHost);
|
|
}
|
|
|
|
//We're done with the pUnk to the result view
|
|
pUnk->Release();
|
|
pUnk = NULL;
|
|
|
|
ASSERT(NULL != pTsMultiHost);
|
|
if(NULL == pTsMultiHost)
|
|
{
|
|
hr = E_FAIL;
|
|
goto FN_EXIT_POINT;
|
|
}
|
|
|
|
//
|
|
// If the con node is being selected then connect
|
|
// or switch to already running instance
|
|
//
|
|
//
|
|
// Connect
|
|
//
|
|
ODS(L"Connection node Selected...\n");
|
|
|
|
pTS = pConNode->GetTsClient();
|
|
if(NULL == pTS)
|
|
{
|
|
//Create new instance
|
|
hr = pTsMultiHost->Add( &pTS);
|
|
if(FAILED(hr))
|
|
{
|
|
goto FN_EXIT_POINT;
|
|
}
|
|
|
|
pConNode->SetTsClient( pTS);
|
|
|
|
//
|
|
// Initialize the disconnected message
|
|
//
|
|
hr = pTS->put_DisconnectedText(m_wszDisconnectedStatus);
|
|
if(FAILED(hr))
|
|
{
|
|
goto FN_EXIT_POINT;
|
|
}
|
|
}
|
|
|
|
ASSERT(NULL != pTS);
|
|
if(NULL == pTS)
|
|
{
|
|
hr = E_FAIL;
|
|
goto FN_EXIT_POINT;
|
|
}
|
|
|
|
hr = pTsMultiHost->put_ActiveClient( pTS);
|
|
if(FAILED(hr))
|
|
{
|
|
goto FN_EXIT_POINT;
|
|
}
|
|
|
|
//
|
|
//If this is the first time through and we are not connected
|
|
//then connect
|
|
//
|
|
if(!pConNode->IsConnected() && !pConNode->IsConnInitialized())
|
|
{
|
|
if(m_bTriggeredFirstAutoConnect)
|
|
{
|
|
//
|
|
// Just connect
|
|
//
|
|
hr = ConnectWithNewSettings( pTS, pConNode);
|
|
if(FAILED(hr))
|
|
{
|
|
goto FN_EXIT_POINT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// HACK!
|
|
// Queue a defered connection
|
|
// to work around MMC's annoying behaviour
|
|
// of loading the snapin before it sizes itself
|
|
// which means we connect at the wrong window
|
|
// size.
|
|
//
|
|
m_bTriggeredFirstAutoConnect = TRUE;
|
|
pDeferredConnectInfo = (PTSSNAPIN_DEFER_CONNECT)
|
|
LocalAlloc(LPTR, sizeof(TSSNAPIN_DEFER_CONNECT));
|
|
if(pDeferredConnectInfo)
|
|
{
|
|
pDeferredConnectInfo->pComponent = this;
|
|
pDeferredConnectInfo->pConnectionNode = pConNode;
|
|
hwndMain = GetMMCMainWindow();
|
|
if(hwndMain)
|
|
{
|
|
//
|
|
// Note the delay is arbitrary the key thing
|
|
// is that timer messages are low priority so the
|
|
// MMC size messages should make it through first
|
|
//
|
|
|
|
//
|
|
// NOTE: THERE IS NO LEAK HERE
|
|
// pDeferredConnectInfo is freed in the
|
|
// DeferredCallBack
|
|
//
|
|
SetTimer( hwndMain,
|
|
(UINT_PTR)(pDeferredConnectInfo),
|
|
100, //100ms delay
|
|
DeferredCallBackProc );
|
|
}
|
|
else
|
|
{
|
|
ODS(L"Unable to get MMC main window handle");
|
|
hr = E_FAIL;
|
|
if(pDeferredConnectInfo)
|
|
{
|
|
LocalFree(pDeferredConnectInfo);
|
|
pDeferredConnectInfo = NULL;
|
|
}
|
|
goto FN_EXIT_POINT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ODS(L"Alloc for TSSNAPIN_DEFER_CONNECT failed");
|
|
hr = E_OUTOFMEMORY;
|
|
goto FN_EXIT_POINT;
|
|
}
|
|
}
|
|
}
|
|
|
|
hr = S_OK;
|
|
}
|
|
|
|
FN_EXIT_POINT:
|
|
|
|
if(pTS)
|
|
{
|
|
pTS->Release();
|
|
pTS = NULL;
|
|
}
|
|
|
|
if(pUnk)
|
|
{
|
|
pUnk->Release();
|
|
pUnk = NULL;
|
|
}
|
|
|
|
if(pTsMultiHost)
|
|
{
|
|
pTsMultiHost->Release();
|
|
pTsMultiHost = NULL;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Get the window handle to MMC's main window
|
|
//
|
|
HWND CComp::GetMMCMainWindow()
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
HWND hwnd = NULL;
|
|
IConsole2* pConsole2;
|
|
|
|
if(m_pConsole)
|
|
{
|
|
hr = m_pConsole->GetMainWindow( &hwnd );
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
return hwnd;
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
HRESULT CComp::ConnectWithNewSettings(IMsRdpClient* pTS, CConNode* pConNode)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
IMsRdpClientSecuredSettings *pMstscSecured = NULL;
|
|
IMsRdpClientAdvancedSettings *pAdvSettings = NULL;
|
|
IMsTscNonScriptable *ptsns = NULL;
|
|
IMsRdpClient2* pTsc2 = NULL;
|
|
|
|
ASSERT(NULL != pTS);
|
|
ASSERT(NULL != pConNode);
|
|
if(NULL == pTS || !pConNode)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
//
|
|
// Init con settings
|
|
//
|
|
if (FAILED(hr = pTS->put_Server( pConNode->GetServerName() ))) {
|
|
DC_QUIT;
|
|
}
|
|
|
|
if (FAILED(hr = pTS->QueryInterface(IID_IMsRdpClient2, (void**)&pTsc2))) {
|
|
|
|
//
|
|
// NOT a fatal error it just means we can't use the newer features
|
|
//
|
|
DBGMSG( L"QueryInterface IID_IMsRdpClient2 failed: 0x%x\n", hr );
|
|
}
|
|
|
|
//
|
|
// Setup the connection status string
|
|
//
|
|
TCHAR szConnectingStatus[MAX_PATH*2];
|
|
_stprintf(szConnectingStatus, m_wszConnectingStatus,
|
|
pConNode->GetServerName());
|
|
|
|
if(FAILED(hr = pTS->put_ConnectingText( szConnectingStatus))) {
|
|
DC_QUIT;
|
|
}
|
|
|
|
if (FAILED(hr = pTS->put_FullScreenTitle( pConNode->GetServerName()))) {
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Connected status text
|
|
//
|
|
if (pTsc2) {
|
|
TCHAR szConnectedStatus[MAX_PATH*2];
|
|
_stprintf(szConnectedStatus, m_wszConnectedStatus,
|
|
pConNode->GetServerName());
|
|
if (FAILED(hr = pTsc2->put_ConnectedStatusText(szConnectedStatus))) {
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
if (pConNode->GetResType() != SCREEN_RES_FILL_MMC &&
|
|
pConNode->GetDesktopWidth() && pConNode->GetDesktopHeight()) {
|
|
|
|
if (FAILED(hr = pTS->put_DesktopWidth( pConNode->GetDesktopWidth()))) {
|
|
DC_QUIT;
|
|
}
|
|
if (FAILED(hr = pTS->put_DesktopHeight( pConNode->GetDesktopHeight()))) {
|
|
DC_QUIT;
|
|
}
|
|
|
|
}
|
|
else if(pConNode->GetResType() == SCREEN_RES_FILL_MMC) {
|
|
//
|
|
// Need to fill the MMC result pane so tell the control
|
|
// to size itself to the container by giving 0 width/height
|
|
//
|
|
if (FAILED(hr = pTS->put_DesktopWidth( 0))) {
|
|
DC_QUIT;
|
|
}
|
|
if (FAILED(hr = pTS->put_DesktopHeight( 0))) {
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Program/Start directory
|
|
//
|
|
|
|
if(FAILED(hr = pTS->get_SecuredSettings2( &pMstscSecured))) {
|
|
DC_QUIT;
|
|
}
|
|
|
|
if (FAILED(hr = pMstscSecured->put_StartProgram( pConNode->GetProgramPath() ))) {
|
|
DC_QUIT;
|
|
}
|
|
|
|
if (FAILED(hr = pMstscSecured->put_WorkDir( pConNode->GetWorkDir() ))) {
|
|
DC_QUIT;
|
|
}
|
|
pMstscSecured->Release();
|
|
pMstscSecured = NULL;
|
|
|
|
|
|
hr = pTS->get_AdvancedSettings2( &pAdvSettings);
|
|
if(FAILED(hr)) {
|
|
DC_QUIT;
|
|
}
|
|
|
|
if (FAILED(hr = pAdvSettings->put_RedirectDrives(
|
|
BOOL_TO_VB(pConNode->GetRedirectDrives())))) {
|
|
DC_QUIT;
|
|
}
|
|
|
|
if (FAILED(hr = pAdvSettings->put_RedirectPrinters(
|
|
BOOL_TO_VB(TRUE)))) {
|
|
DC_QUIT;
|
|
}
|
|
|
|
if (FAILED(hr = pAdvSettings->put_RedirectPorts(
|
|
BOOL_TO_VB(TRUE)))) {
|
|
DC_QUIT;
|
|
}
|
|
|
|
if (FAILED(hr = pAdvSettings->put_RedirectSmartCards(
|
|
BOOL_TO_VB(TRUE)))) {
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Container handled fullscreen
|
|
//
|
|
hr = pAdvSettings->put_ConnectToServerConsole(
|
|
BOOL_TO_VB(pConNode->GetConnectToConsole()));
|
|
if(FAILED(hr)) {
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Don't allow the control to grab focus
|
|
// the snapin will manage giving focus to a node when it switches
|
|
// to it. This prevents problems where an obscured session steals
|
|
// focus from another one.
|
|
//
|
|
hr = pAdvSettings->put_GrabFocusOnConnect( FALSE );
|
|
if(FAILED(hr)) {
|
|
DC_QUIT;
|
|
}
|
|
|
|
if (FAILED(hr = pTS->put_UserName( pConNode->GetUserName()))) {
|
|
DC_QUIT;
|
|
}
|
|
|
|
if (FAILED(hr = pTS->put_Domain( pConNode->GetDomain()))) {
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Set the password/salt
|
|
//
|
|
if ( pConNode->GetPasswordSpecified())
|
|
{
|
|
TCHAR szPass[CL_MAX_PASSWORD_LENGTH_BYTES/sizeof(TCHAR)];
|
|
|
|
hr = pConNode->GetClearTextPass(szPass, sizeof(szPass));
|
|
if (SUCCEEDED(hr)) {
|
|
BSTR Pass = SysAllocString(szPass);
|
|
if (Pass) {
|
|
hr = pAdvSettings->put_ClearTextPassword(Pass);
|
|
SecureZeroMemory(Pass, SysStringByteLen(Pass));
|
|
SysFreeString(Pass);
|
|
}
|
|
}
|
|
SecureZeroMemory(szPass, sizeof(szPass));
|
|
}
|
|
else {
|
|
//Password is not specified, make sure logon
|
|
//properties are reset
|
|
hr = pTS->QueryInterface(IID_IMsTscNonScriptable, (void**)&ptsns);
|
|
if(SUCCEEDED(hr)) {
|
|
if (FAILED(hr = ptsns->ResetPassword())) {
|
|
DC_QUIT;
|
|
}
|
|
ptsns->Release();
|
|
ptsns = NULL;
|
|
}
|
|
else {
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
pAdvSettings->Release();
|
|
pAdvSettings = NULL;
|
|
|
|
pConNode->SetConnectionInitialized(TRUE);
|
|
|
|
//
|
|
// Release any existing view and connect
|
|
//
|
|
pConNode->SetView(NULL);
|
|
pConNode->SetView(this);
|
|
|
|
hr = pTS->Connect( );
|
|
if (FAILED(hr)) {
|
|
DC_QUIT;
|
|
}
|
|
GiveFocusToControl(pTS);
|
|
|
|
pConNode->SetConnected(TRUE);
|
|
hr = S_OK;
|
|
|
|
DC_EXIT_POINT:
|
|
if (pMstscSecured) {
|
|
pMstscSecured->Release();
|
|
pMstscSecured = NULL;
|
|
}
|
|
|
|
if (pAdvSettings) {
|
|
pAdvSettings->Release();
|
|
pAdvSettings = NULL;
|
|
}
|
|
|
|
if (ptsns) {
|
|
ptsns->Release();
|
|
ptsns = NULL;
|
|
}
|
|
|
|
if (pTsc2) {
|
|
pTsc2->Release();
|
|
pTsc2 = NULL;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
BOOL CComp::GiveFocusToControl(IMsRdpClient* pTs)
|
|
{
|
|
IOleInPlaceActiveObject* poipa = NULL;
|
|
HWND hwnd;
|
|
HRESULT hr = E_FAIL;
|
|
if(pTs)
|
|
{
|
|
hr = pTs->QueryInterface( IID_IOleInPlaceActiveObject,
|
|
(void**)&poipa );
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
hr = poipa->GetWindow( &hwnd );
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
DBGMSG(L"Giving focus to control wnd: 0%p",
|
|
hwnd);
|
|
SetFocus( hwnd );
|
|
}
|
|
else
|
|
{
|
|
ODS(L"poipa->GetWindow failed");
|
|
}
|
|
poipa->Release();
|
|
}
|
|
}
|
|
return SUCCEEDED(hr);
|
|
}
|
|
|
|
|
|
//
|
|
// menu items
|
|
//
|
|
STDMETHODIMP CComp::AddMenuItems( LPDATAOBJECT pNode,
|
|
LPCONTEXTMENUCALLBACK pCtxMenu,
|
|
PLONG plInsertion)
|
|
{
|
|
TCHAR tchBuffer1[ 128 ];
|
|
TCHAR tchBuffer2[ 128 ];
|
|
ATLASSERT( pNode != NULL );
|
|
ATLASSERT( pCtxMenu != NULL );
|
|
ATLASSERT( plInsertion != NULL );
|
|
|
|
if (!pNode)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
if(IS_SPECIAL_DATAOBJECT(pNode))
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
CBaseNode *pBaseNode = dynamic_cast< CBaseNode *>( pNode );
|
|
if (!pBaseNode)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
if (pBaseNode->GetNodeType() == MAIN_NODE)
|
|
{
|
|
//
|
|
// Check that insertion at the View is allowed
|
|
//
|
|
if (!(*plInsertion & CCM_INSERTIONALLOWED_VIEW))
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
|
|
//
|
|
// Add menu item to root node
|
|
//
|
|
CONTEXTMENUITEM ctxmi;
|
|
if(!LoadString( _Module.GetResourceInstance( ) , IDS_CTXM_NEW_CONNECTION ,
|
|
tchBuffer1 , SIZE_OF_BUFFER( tchBuffer1 )))
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
ctxmi.strName = tchBuffer1;
|
|
if(!LoadString( _Module.GetResourceInstance( ) , IDS_CTXM_STATUS_NEW_CONNECTION ,
|
|
tchBuffer2 , SIZE_OF_BUFFER( tchBuffer2)))
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
ctxmi.strStatusBarText = tchBuffer2;
|
|
ctxmi.lCommandID = IDM_CREATECON;
|
|
ctxmi.lInsertionPointID = CCM_INSERTIONPOINTID_PRIMARY_TOP ;
|
|
ctxmi.fFlags = 0;
|
|
ctxmi.fSpecialFlags = 0;
|
|
|
|
if (FAILED(pCtxMenu->AddItem( &ctxmi )))
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
else if(pBaseNode->GetNodeType() == CONNECTION_NODE)
|
|
{
|
|
IComponent* pOwningView = NULL;
|
|
BOOL fBailOut = FALSE;
|
|
|
|
//
|
|
// Check that insertion at the view is allowed
|
|
//
|
|
if (!(*plInsertion & CCM_INSERTIONALLOWED_VIEW))
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
|
|
//
|
|
// Add 'Connect' menu item
|
|
//
|
|
CConNode* pConNode = (CConNode*) pBaseNode;
|
|
ASSERT(pConNode);
|
|
if(!pConNode)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
pOwningView = pConNode->GetView();
|
|
|
|
//
|
|
// A connected node 'belongs' to a view so don't allow
|
|
// commands on other views to affect it
|
|
//
|
|
// A null pOwningView means an unowned connection
|
|
//
|
|
if (pOwningView && pOwningView != this)
|
|
{
|
|
fBailOut = TRUE;
|
|
}
|
|
|
|
if (pOwningView)
|
|
{
|
|
pOwningView->Release();
|
|
pOwningView = NULL;
|
|
}
|
|
|
|
if (fBailOut)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
BOOL bIsTSCliConnected = CCompdata::IsTSClientConnected(pConNode);
|
|
CONTEXTMENUITEM ctxmi;
|
|
if(!LoadString( _Module.GetResourceInstance( ) , IDS_CTXM_CONNECT ,
|
|
tchBuffer1 , SIZE_OF_BUFFER( tchBuffer1)))
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
ctxmi.strName = tchBuffer1;
|
|
if(!LoadString( _Module.GetResourceInstance( ) , IDS_CTXM_STATUS_CONNECT ,
|
|
tchBuffer2 , SIZE_OF_BUFFER( tchBuffer2)))
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
ctxmi.strStatusBarText = tchBuffer2;
|
|
ctxmi.lCommandID = IDM_CONNECT;
|
|
ctxmi.lInsertionPointID = CCM_INSERTIONPOINTID_PRIMARY_TOP;
|
|
ctxmi.fFlags = bIsTSCliConnected ? MF_GRAYED: MF_ENABLED;
|
|
ctxmi.fSpecialFlags = 0;
|
|
|
|
if (FAILED(pCtxMenu->AddItem( &ctxmi )))
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
//
|
|
// Add 'Disconnect' menu item
|
|
//
|
|
if(!LoadString( _Module.GetResourceInstance( ) , IDS_CTXM_DISCONNECT ,
|
|
tchBuffer1 , SIZE_OF_BUFFER( tchBuffer1 ) ) )
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
ctxmi.strName = tchBuffer1;
|
|
if(!LoadString( _Module.GetResourceInstance( ) , IDS_CTXM_STATUS_DISCONNECT ,
|
|
tchBuffer2 , SIZE_OF_BUFFER( tchBuffer2 )))
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
ctxmi.strStatusBarText = tchBuffer2;
|
|
ctxmi.lCommandID = IDM_DISCONNECT;
|
|
ctxmi.lInsertionPointID = CCM_INSERTIONPOINTID_PRIMARY_TOP;
|
|
ctxmi.fFlags = !bIsTSCliConnected ? MF_GRAYED: MF_ENABLED;
|
|
ctxmi.fSpecialFlags = 0;
|
|
|
|
if (FAILED(pCtxMenu->AddItem( &ctxmi )))
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------
|
|
// Menu handler
|
|
//----------------------------------------------------------------------------------------------------------
|
|
STDMETHODIMP CComp::Command( LONG lCommand , LPDATAOBJECT pDo)
|
|
{
|
|
//
|
|
// Add a new connection...
|
|
//
|
|
CBaseNode *pNode = dynamic_cast< CBaseNode *>( pDo );
|
|
HRESULT hr = S_OK;
|
|
if (IDM_CREATECON == lCommand)
|
|
{
|
|
if ( m_pCompdata)
|
|
{
|
|
hr = m_pCompdata->AddNewConnection();
|
|
}
|
|
else
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
else if (IDM_CONNECT == lCommand)
|
|
{
|
|
//
|
|
// Connect
|
|
//
|
|
if(!pNode)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
else if(pNode->GetNodeType() != CONNECTION_NODE)
|
|
{
|
|
//
|
|
// Can't receive a connect request for a node other
|
|
// than a connection node
|
|
//
|
|
ASSERT(pNode->GetNodeType() == CONNECTION_NODE);
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
CConNode* pConNode = (CConNode*) pNode;
|
|
|
|
//
|
|
// Select the scope node, that will call CComp::OnShow which will connect
|
|
//
|
|
|
|
ASSERT(m_pConsole);
|
|
if(!m_pConsole)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
IMsRdpClient* pTS = pConNode->GetTsClient();
|
|
if(NULL != pTS && pConNode->IsConnInitialized())
|
|
{
|
|
//
|
|
// Only connect directly if the connection settings are initialized
|
|
//
|
|
|
|
//
|
|
// Set view ownership
|
|
//
|
|
pConNode->SetView( this );
|
|
HRESULT hr = pTS->Connect();
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
pConNode->SetConnected(TRUE);
|
|
pTS->Release();
|
|
}
|
|
|
|
//
|
|
// Selecting the node if the con settings are not initialized
|
|
// initializes them and connects
|
|
//
|
|
if(FAILED(m_pConsole->SelectScopeItem( pConNode->GetScopeID())))
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
hr = S_OK;
|
|
}
|
|
else if (IDM_DISCONNECT == lCommand)
|
|
{
|
|
if(!pNode)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
//
|
|
// Disconnect
|
|
//
|
|
if(pNode->GetNodeType() != CONNECTION_NODE)
|
|
{
|
|
//
|
|
// Can't receive a connect request for a node other
|
|
// than a connection node
|
|
//
|
|
ASSERT(pNode->GetNodeType() == CONNECTION_NODE);
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
CConNode* pConNode = (CConNode*) pNode;
|
|
ASSERT(m_pConsole);
|
|
if(!m_pConsole)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
IMsRdpClient* pTS = pConNode->GetTsClient();
|
|
if(NULL != pTS)
|
|
{
|
|
HRESULT hr = pTS->Disconnect();
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
pTS->Release();
|
|
}
|
|
pConNode->SetConnected(FALSE);
|
|
hr = S_OK;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|