Leaked source code of windows server 2003
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.

900 lines
19 KiB

  1. /*++
  2. Copyright (c) 1999-2000 Microsoft Corporation
  3. Module Name:
  4. RemoteDesktopClient
  5. Abstract:
  6. The CRemoteDesktopClient class is the parent
  7. class for the Remote Desktop class hierarchy on the server-side.
  8. It helps the CRemoteDesktopClientHost class to implement
  9. the ISAFRemoteDesktopClient interface.
  10. The Remote Desktop class hierarchy provides a pluggable C++ interface
  11. for remote desktop access, by abstracting the implementation
  12. specific details of remote desktop access for the server-side.
  13. Author:
  14. Tad Brockway 02/00
  15. Revision History:
  16. --*/
  17. #include "stdafx.h"
  18. #ifdef TRC_FILE
  19. #undef TRC_FILE
  20. #endif
  21. #define TRC_FILE "_rdclnt"
  22. #include "RDCHost.h"
  23. #include "RemoteDesktopClient.h"
  24. #include <RemoteDesktopUtils.h>
  25. #include "ClientDataChannelMgr.h"
  26. #include <algorithm>
  27. using namespace std;
  28. ///////////////////////////////////////////////////////
  29. //
  30. // CRemoteDesktopClientEventSink Methods
  31. //
  32. void __stdcall
  33. CRemoteDesktopClientEventSink::OnConnected()
  34. {
  35. m_Obj->OnConnected();
  36. }
  37. void __stdcall
  38. CRemoteDesktopClientEventSink::OnDisconnected(long reason)
  39. {
  40. m_Obj->OnDisconnected(reason);
  41. }
  42. void __stdcall
  43. CRemoteDesktopClientEventSink::OnConnectRemoteDesktopComplete(long status)
  44. {
  45. m_Obj->OnConnectRemoteDesktopComplete(status);
  46. }
  47. void __stdcall
  48. CRemoteDesktopClientEventSink::OnListenConnect(long status)
  49. {
  50. m_Obj->OnListenConnect(status);
  51. }
  52. void __stdcall
  53. CRemoteDesktopClientEventSink::OnBeginConnect()
  54. {
  55. m_Obj->OnBeginConnect();
  56. }
  57. ///////////////////////////////////////////////////////
  58. //
  59. // CRemoteDesktopClient Methods
  60. //
  61. HRESULT
  62. CRemoteDesktopClient::FinalConstruct()
  63. /*++
  64. Routine Description:
  65. Final Constructor
  66. Arguments:
  67. Return Value:
  68. S_OK on success. Otherwise, an error code is returned.
  69. --*/
  70. {
  71. DC_BEGIN_FN("CRemoteDesktopClient::FinalConstruct");
  72. //
  73. // Register with ActiveX
  74. //
  75. HRESULT hr = S_OK;
  76. if (!AtlAxWinInit()) {
  77. TRC_ERR((TB, L"AtlAxWinInit failed."));
  78. hr = E_FAIL;
  79. }
  80. //
  81. // Create the Data Channel Manager
  82. //
  83. m_ChannelMgr = new CComObject<CClientDataChannelMgr>();
  84. m_ChannelMgr->AddRef();
  85. //
  86. // Initialize the channel mnager
  87. //
  88. hr = m_ChannelMgr->Initialize();
  89. DC_END_FN();
  90. return hr;
  91. }
  92. CRemoteDesktopClient::~CRemoteDesktopClient()
  93. /*++
  94. Routine Description:
  95. Destructor
  96. Arguments:
  97. Return Value:
  98. --*/
  99. {
  100. DC_BEGIN_FN("CRemoteDesktopClient::~CRemoteDesktopClient");
  101. DisconnectFromServer();
  102. //
  103. // !!!!NOTE!!!!
  104. // Cleaning up the contained m_Client control is being done in the destructor
  105. // for Windows XP to make sure it and the MSTSCAX control are not destroyed
  106. // in a callback to PC Health via a DisconnectFromServer invokation. Cleaning
  107. // up here removes late-binding of the protocol in that once we connect one time
  108. // to a particular protocol type (RDP only for XP), we can't later connect using
  109. // some other protocol.
  110. //
  111. // If we are to support other protocol types in the future, then the clean up
  112. // should be done in the DisconnectFromServer so we can rebind to a different protocol
  113. // on each ConnectToServer call. To make this work, we will need to clean up MSTCAX
  114. // and the TSRDP Salem control so they can be destroyed in a callback. I (TadB) actually
  115. // had this working for the TSRDP Salem control in an afternoon.
  116. //
  117. // Zero out the IO interface ptr for the channel manager, since it is
  118. // going away.
  119. //
  120. if (m_ChannelMgr != NULL) {
  121. m_ChannelMgr->SetIOInterface(NULL);
  122. }
  123. if (m_Client != NULL) {
  124. m_Client = NULL;
  125. }
  126. if (m_ClientWnd != NULL) {
  127. m_ClientAxView.DestroyWindow();
  128. m_ClientWnd = NULL;
  129. }
  130. //
  131. // Release the data channel manager.
  132. //
  133. m_ChannelMgr->Release();
  134. if ( NULL != m_ExtDllName )
  135. SysFreeString( m_ExtDllName );
  136. if ( NULL != m_ExtParams )
  137. SysFreeString( m_ExtParams );
  138. DC_END_FN();
  139. }
  140. STDMETHODIMP
  141. CRemoteDesktopClient::get_IsServerConnected(
  142. BOOL *pVal
  143. )
  144. /*++
  145. Routine Description:
  146. Indicates whether the client-side Remote Desktop Host ActiveX Control is
  147. connected to the server, independent of whether the remote user's desktop
  148. is currently remote controlled.
  149. Arguments:
  150. pVal - Set to TRUE if the client is currently connected to the server.
  151. Return Value:
  152. S_OK on success. Otherwise, an error code is returned.
  153. --*/
  154. {
  155. DC_BEGIN_FN("CRemoteDesktopClient::get_IsServerConnected");
  156. HRESULT hr;
  157. if (pVal == NULL) {
  158. ASSERT(FALSE);
  159. hr = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
  160. goto CLEANUPANDEXIT;
  161. }
  162. if (m_Client != NULL) {
  163. hr = m_Client->get_IsServerConnected(pVal);
  164. }
  165. else {
  166. *pVal = FALSE;
  167. hr = S_OK;
  168. }
  169. CLEANUPANDEXIT:
  170. DC_END_FN();
  171. return hr;
  172. }
  173. STDMETHODIMP
  174. CRemoteDesktopClient::get_IsRemoteDesktopConnected(
  175. BOOL *pVal
  176. )
  177. /*++
  178. Routine Description:
  179. Indicates whether the control is currently connected to the server
  180. machine.
  181. Arguments:
  182. pVal - Sets to TRUE if the control is currently connected to the server.
  183. Return Value:
  184. S_OK on success. Otherwise, an error code is returned.
  185. --*/
  186. {
  187. DC_BEGIN_FN("CRemoteDesktopClient::get_IsRemoteDesktopConnected");
  188. HRESULT hr;
  189. if (pVal == NULL) {
  190. ASSERT(FALSE);
  191. hr = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
  192. goto CLEANUPANDEXIT;
  193. }
  194. if (m_Client != NULL) {
  195. hr = m_Client->get_IsRemoteDesktopConnected(pVal);
  196. }
  197. else {
  198. hr = HRESULT_FROM_WIN32(ERROR_NOT_CONNECTED);
  199. }
  200. CLEANUPANDEXIT:
  201. DC_END_FN();
  202. return hr;
  203. }
  204. STDMETHODIMP
  205. CRemoteDesktopClient::get_ExtendedErrorInfo(
  206. LONG *error
  207. )
  208. /*++
  209. Routine Description:
  210. Arguments:
  211. Return Value:
  212. --*/
  213. {
  214. DC_BEGIN_FN("CRemoteDesktopClient::get_ExtendedErrorInfo");
  215. HRESULT hr;
  216. if (error == NULL) {
  217. ASSERT(FALSE);
  218. hr = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
  219. goto CLEANUPANDEXIT;
  220. }
  221. if (m_Client != NULL) {
  222. hr = m_Client->get_ExtendedErrorInfo(error);
  223. }
  224. else {
  225. hr = S_OK;
  226. *error = SAFERROR_NOERROR;
  227. }
  228. CLEANUPANDEXIT:
  229. DC_END_FN();
  230. return hr;
  231. }
  232. STDMETHODIMP
  233. CRemoteDesktopClient::DisconnectRemoteDesktop()
  234. /*++
  235. Routine Description:
  236. Arguments:
  237. Return Value:
  238. --*/
  239. {
  240. DC_BEGIN_FN("CRemoteDesktopClient::DisconnectRemoteDesktop");
  241. HRESULT hr;
  242. //
  243. // Hide our window.
  244. //
  245. //ShowWindow(SW_HIDE);
  246. m_RemoteControlEnabled = FALSE;
  247. if (m_Client != NULL) {
  248. hr = m_Client->DisconnectRemoteDesktop();
  249. }
  250. else {
  251. hr = HRESULT_FROM_WIN32(ERROR_NOT_CONNECTED);
  252. }
  253. DC_END_FN();
  254. return hr;
  255. }
  256. STDMETHODIMP
  257. CRemoteDesktopClient::ConnectRemoteDesktop()
  258. /*++
  259. Routine Description:
  260. Arguments:
  261. Return Value:
  262. --*/
  263. {
  264. DC_BEGIN_FN("CRemoteDesktopClient::ConnectRemoteDesktop");
  265. HRESULT hr;
  266. if (m_Client != NULL) {
  267. hr = m_Client->ConnectRemoteDesktop();
  268. }
  269. else {
  270. hr = HRESULT_FROM_WIN32(ERROR_NOT_CONNECTED);
  271. }
  272. DC_END_FN();
  273. return hr;
  274. }
  275. HRESULT
  276. CRemoteDesktopClient::InitializeRemoteDesktopClientObject()
  277. /*++
  278. Routine Description:
  279. Routine to initialize window for actvie x control and setups our channel manager
  280. Parameters:
  281. None.
  282. Returns:
  283. S_OK or error code.
  284. Notes:
  285. put_EnableSmartSizing() is not invoked in here because on listen mode, we
  286. create/initialize object first then goes into actual connection, two seperate calls,
  287. and so it is possible for caller to invoke smartsizeing in-between and we will
  288. never pick it up, both ConnectToServer() and AcceptListenConnection()
  289. need to make the call.
  290. --*/
  291. {
  292. DC_BEGIN_FN("CRemoteDesktopClient::InitializeRemoteDesktopClientObject");
  293. HRESULT hr = S_OK;
  294. IUnknown *pUnk = NULL;
  295. RECT ourWindowRect;
  296. RECT rcClient;
  297. CComPtr<IDataChannelIO> ioInterface;
  298. //
  299. // Get the dimensions of our window.
  300. //
  301. if (!::GetWindowRect(m_hWnd, &ourWindowRect)) {
  302. hr = HRESULT_FROM_WIN32(GetLastError());
  303. TRC_ERR((TB, L"GetWindowRect: %08X", hr));
  304. goto CLEANUPANDEXIT;
  305. }
  306. //
  307. // Create the client Window.
  308. //
  309. rcClient.top = 0;
  310. rcClient.left = 0;
  311. rcClient.right = ourWindowRect.right - ourWindowRect.left;
  312. rcClient.bottom = ourWindowRect.bottom - ourWindowRect.top;
  313. m_ClientWnd = m_ClientAxView.Create(
  314. m_hWnd, rcClient, REMOTEDESKTOPRDPCLIENT_TEXTGUID,
  315. WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN, 0
  316. );
  317. if (m_ClientWnd == NULL) {
  318. hr = HRESULT_FROM_WIN32(GetLastError());
  319. TRC_ERR((TB, L"Window Create: %08X", GetLastError()));
  320. goto CLEANUPANDEXIT;
  321. }
  322. ASSERT(::IsWindow(m_ClientWnd));
  323. //
  324. // Get IUnknown
  325. //
  326. hr = AtlAxGetControl(m_ClientWnd, &pUnk);
  327. if (!SUCCEEDED(hr)) {
  328. TRC_ERR((TB, L"AtlAxGetControl: %08X", hr));
  329. pUnk = NULL;
  330. goto CLEANUPANDEXIT;
  331. }
  332. //
  333. // Get the control.
  334. //
  335. hr = pUnk->QueryInterface(__uuidof(ISAFRemoteDesktopClient), (void**)&m_Client);
  336. if (!SUCCEEDED(hr)) {
  337. TRC_ERR((TB, L"QueryInterface: %08X", hr));
  338. goto CLEANUPANDEXIT;
  339. }
  340. //
  341. // Initialize the event sink.
  342. //
  343. m_ClientEventSink.m_Obj = this;
  344. //
  345. // Add the event sink.
  346. //
  347. hr = m_ClientEventSink.DispEventAdvise(pUnk);
  348. if (!SUCCEEDED(hr)) {
  349. TRC_ERR((TB, L"DispEventAdvise: %08X", hr));
  350. goto CLEANUPANDEXIT;
  351. }
  352. //
  353. // Get the Data IO interface from the control so we can talk
  354. // over an OOB data channel.
  355. //
  356. hr = pUnk->QueryInterface(__uuidof(IDataChannelIO), (void**)&ioInterface);
  357. if (!SUCCEEDED(hr)) {
  358. TRC_ERR((TB, L"QueryInterface: %08X", hr));
  359. goto CLEANUPANDEXIT;
  360. }
  361. //
  362. // Pass the channel manager to the control.
  363. //
  364. ioInterface->put_ChannelMgr(m_ChannelMgr);
  365. //
  366. // Indicate the current data io provider to the channel manager
  367. // because it just changed.
  368. //
  369. m_ChannelMgr->SetIOInterface(ioInterface);
  370. CLEANUPANDEXIT:
  371. //
  372. // m_Client keeps our reference to the client object until
  373. // it ref counts to zero.
  374. //
  375. if (pUnk != NULL) {
  376. pUnk->Release();
  377. }
  378. return hr;
  379. }
  380. STDMETHODIMP
  381. CRemoteDesktopClient::ConnectToServer(BSTR bstrExpertBlob)
  382. /*++
  383. Routine Description:
  384. Arguments:
  385. bstrExpertBlob : Optional blob to be transmitted over to user side, this
  386. is used only in the case of SAF resolver.
  387. Return Value:
  388. --*/
  389. {
  390. DC_BEGIN_FN("CRemoteDesktopClient::ConnectToServer");
  391. HRESULT hr;
  392. DWORD protocolType;
  393. CComBSTR tmp;
  394. CComBSTR helpSessionId;
  395. DWORD result;
  396. CComBSTR channelInfo;
  397. ChannelsType::iterator element;
  398. DWORD dwConnParmVersion;
  399. WCHAR buf[MAX_PATH];
  400. //
  401. // Check the connection parameters.
  402. //
  403. if (m_ConnectParms.Length() == 0) {
  404. ASSERT(FALSE);
  405. hr = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
  406. goto CLEANUPANDEXIT;
  407. }
  408. //
  409. // Parse the connection parms to get the type of server
  410. // to which we are connecting.
  411. //
  412. result = ParseConnectParmsString(
  413. m_ConnectParms, &dwConnParmVersion, &protocolType, tmp, tmp,
  414. tmp, helpSessionId, tmp, tmp,
  415. tmp
  416. );
  417. if (result != ERROR_SUCCESS) {
  418. hr = HRESULT_FROM_WIN32(result);
  419. goto CLEANUPANDEXIT;
  420. }
  421. //
  422. // Right now, we only support the TSRDP client.
  423. // TODO: We should make this pluggable for Whistler timeframe
  424. // via registry defined CLSID's.
  425. //
  426. if (protocolType != REMOTEDESKTOP_TSRDP_PROTOCOL) {
  427. TRC_ERR((TB, L"Unsupported protocol: %ld", protocolType));
  428. hr = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
  429. goto CLEANUPANDEXIT;
  430. }
  431. if( m_Client == NULL) {
  432. hr = InitializeRemoteDesktopClientObject();
  433. if( FAILED(hr) ) {
  434. TRC_ERR((TB, L"InitializeRemoteDesktopClientObject() failed with : %x", hr));
  435. goto CLEANUPANDEXIT;
  436. }
  437. }
  438. //
  439. // Enable/disable smart sizing.
  440. //
  441. hr = m_Client->put_EnableSmartSizing(m_EnableSmartSizing);
  442. if (!SUCCEEDED(hr)) {
  443. goto CLEANUPANDEXIT;
  444. }
  445. hr = m_Client->put_ColorDepth(m_ColorDepth);
  446. if (!SUCCEEDED(hr)) {
  447. goto CLEANUPANDEXIT;
  448. }
  449. //
  450. // setup the test extension
  451. //
  452. _PutExtParams();
  453. //
  454. // Connect.
  455. //
  456. m_Client->put_ConnectParms(m_ConnectParms);
  457. hr = m_Client->ConnectToServer(bstrExpertBlob);
  458. CLEANUPANDEXIT:
  459. DC_END_FN();
  460. return hr;
  461. }
  462. STDMETHODIMP
  463. CRemoteDesktopClient::DisconnectFromServer()
  464. /*++
  465. Routine Description:
  466. Arguments:
  467. Return Value:
  468. --*/
  469. {
  470. DC_BEGIN_FN("CRemoteDesktopClient::DisconnectFromServer");
  471. //
  472. // Hide our window.
  473. //
  474. //ShowWindow(SW_HIDE);
  475. //
  476. // Notify the contained client object.
  477. //
  478. if (m_Client != NULL) {
  479. m_Client->DisconnectFromServer();
  480. }
  481. DC_END_FN();
  482. return S_OK;
  483. }
  484. //
  485. // send parameters to ISAFRemoteDesktopTestExtension
  486. //
  487. HRESULT
  488. CRemoteDesktopClient::_PutExtParams(
  489. VOID
  490. )
  491. {
  492. ISAFRemoteDesktopTestExtension *pExt = NULL;
  493. HRESULT hr = E_NOTIMPL;
  494. DC_BEGIN_FN("CRemoteDesktopClient::_PutExtParams");
  495. if ( NULL == m_ExtDllName )
  496. {
  497. hr = S_OK;
  498. goto CLEANUPANDEXIT;
  499. }
  500. if (m_Client == NULL)
  501. {
  502. hr = HRESULT_FROM_WIN32(ERROR_NOT_CONNECTED);
  503. goto CLEANUPANDEXIT;
  504. }
  505. hr = m_Client->QueryInterface( __uuidof( ISAFRemoteDesktopTestExtension ),
  506. (void **)&pExt );
  507. if (FAILED( hr ))
  508. {
  509. TRC_ERR((TB, L"QueryInterface( ISAFRemoteDesktopTestExtension ), failed %08X", hr));
  510. goto CLEANUPANDEXIT;
  511. }
  512. hr = pExt->put_TestExtDllName( m_ExtDllName );
  513. if (FAILED( hr ))
  514. {
  515. TRC_ERR((TB, L"put_TestExtDllName failed %08X", hr ));
  516. goto CLEANUPANDEXIT;
  517. }
  518. if ( NULL != m_ExtParams )
  519. hr = pExt->put_TestExtParams( m_ExtParams );
  520. CLEANUPANDEXIT:
  521. if ( NULL != pExt )
  522. pExt->Release();
  523. DC_END_FN();
  524. return hr;
  525. }
  526. STDMETHODIMP
  527. CRemoteDesktopClient::StartListen(
  528. /*[in]*/ LONG timeout
  529. )
  530. /*++
  531. Description:
  532. Put client (expert) in listening on socket port listening_port and wait for TS server to connect.
  533. Parameters:
  534. listening_port : Port to listen on, 0 for dynamic port.
  535. timeout : Listen timeout.
  536. pConnectParm : Return Salem specific connection parameter for ISAFRemoteDesktopServerHost object
  537. to connect to this client (expert).
  538. returns:
  539. S_OK or error code.
  540. Notes:
  541. Function is async, return code, if error, is for listening thread set up, caller is notified of
  542. successful or error in network connection via ListenConnect event.
  543. --*/
  544. {
  545. HRESULT hr;
  546. if( m_Client != NULL ) {
  547. hr = m_Client->StartListen( timeout );
  548. }
  549. else {
  550. hr = E_FAIL;
  551. }
  552. CLEANUPANDEXIT:
  553. return hr;
  554. }
  555. STDMETHODIMP
  556. CRemoteDesktopClient::CreateListenEndpoint(
  557. /*[in]*/ LONG listening_port,
  558. /*[out, retval]*/ BSTR* pConnectParm
  559. )
  560. /*++
  561. Description:
  562. Put client (expert) in listening on socket port listening_port and wait for TS server to connect.
  563. Parameters:
  564. listening_port : Port to listen on, 0 for dynamic port.
  565. pConnectParm : Return Salem specific connection parameter for ISAFRemoteDesktopServerHost object
  566. to connect to this client (expert).
  567. returns:
  568. S_OK or error code.
  569. Notes:
  570. Function is async, return code, if error, is for listening thread set up, caller is notified of
  571. successful or error in network connection via ListenConnect event.
  572. --*/
  573. {
  574. HRESULT hr;
  575. if( NULL == pConnectParm ) {
  576. hr = E_INVALIDARG;
  577. }
  578. else {
  579. if( m_Client == NULL ) {
  580. hr = InitializeRemoteDesktopClientObject();
  581. if( FAILED(hr) ) {
  582. goto CLEANUPANDEXIT;
  583. }
  584. }
  585. hr = m_Client->CreateListenEndpoint( listening_port, pConnectParm );
  586. }
  587. CLEANUPANDEXIT:
  588. return hr;
  589. }
  590. STDMETHODIMP
  591. CRemoteDesktopClient::StopListen()
  592. /*++
  593. Description:
  594. Stop listening waiting for TS server (helpee, user) to connect.
  595. Parameters:
  596. None.
  597. Returns:
  598. S_OK or error code.
  599. --*/
  600. {
  601. HRESULT hr;
  602. if( m_Client != NULL ) {
  603. hr = m_Client->StopListen();
  604. }
  605. else {
  606. hr = E_FAIL;
  607. }
  608. return hr;
  609. }
  610. STDMETHODIMP
  611. CRemoteDesktopClient::AcceptListenConnection(
  612. /*[in]*/ BSTR expertBlob
  613. )
  614. /*++
  615. Description:
  616. Stop listening waiting for TS server (helpee, user) to connect.
  617. Parameters:
  618. None.
  619. Returns:
  620. S_OK or error code.
  621. --*/
  622. {
  623. HRESULT hr = S_OK;
  624. DWORD protocolType;
  625. CComBSTR tmp;
  626. CComBSTR helpSessionId;
  627. DWORD result;
  628. CComBSTR channelInfo;
  629. ChannelsType::iterator element;
  630. DWORD dwConnParmVersion;
  631. WCHAR buf[MAX_PATH];
  632. DC_BEGIN_FN("CRemoteDesktopClient::AcceptListenConnection");
  633. //
  634. // Check the connection parameters.
  635. //
  636. if (m_ConnectParms.Length() == 0 || m_Client == NULL) {
  637. hr = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
  638. goto CLEANUPANDEXIT;
  639. }
  640. //
  641. // Parse the connection parms to get the type of server
  642. // to which we are connecting.
  643. //
  644. result = ParseConnectParmsString(
  645. m_ConnectParms, &dwConnParmVersion, &protocolType, tmp, tmp,
  646. tmp, helpSessionId, tmp, tmp,
  647. tmp
  648. );
  649. if (result != ERROR_SUCCESS) {
  650. hr = HRESULT_FROM_WIN32(result);
  651. goto CLEANUPANDEXIT;
  652. }
  653. //
  654. // Right now, we only support the TSRDP client.
  655. // TODO: We should make this pluggable for Whistler timeframe
  656. // via registry defined CLSID's.
  657. //
  658. if (protocolType != REMOTEDESKTOP_TSRDP_PROTOCOL) {
  659. TRC_ERR((TB, L"Unsupported protocol: %ld", protocolType));
  660. hr = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
  661. goto CLEANUPANDEXIT;
  662. }
  663. //
  664. // Enable/disable smart sizing.
  665. //
  666. hr = m_Client->put_EnableSmartSizing(m_EnableSmartSizing);
  667. if (!SUCCEEDED(hr)) {
  668. goto CLEANUPANDEXIT;
  669. }
  670. hr = m_Client->put_ColorDepth(m_ColorDepth);
  671. if (!SUCCEEDED(hr)) {
  672. goto CLEANUPANDEXIT;
  673. }
  674. //
  675. // setup the test extension
  676. //
  677. _PutExtParams();
  678. //
  679. // Connect.
  680. //
  681. m_Client->put_ConnectParms(m_ConnectParms);
  682. hr = m_Client->AcceptListenConnection(expertBlob);
  683. CLEANUPANDEXIT:
  684. DC_END_FN();
  685. return hr;
  686. }