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.

744 lines
19 KiB

  1. #include "comp.h"
  2. #include "compdata.h"
  3. #include "dataobj.h"
  4. #include "resource.h"
  5. #include <crtdbg.h>
  6. #include "globals.h"
  7. #include "guids.h"
  8. #include "uddi.h"
  9. CComponentData::CComponentData()
  10. : m_cref(0)
  11. , m_ipConsoleNameSpace(NULL)
  12. , m_ipConsole(NULL)
  13. , m_bExpanded( false )
  14. {
  15. OBJECT_CREATED
  16. m_pStaticNode = new CUDDIServicesNode;
  17. }
  18. CComponentData::~CComponentData()
  19. {
  20. if( m_pStaticNode )
  21. {
  22. delete m_pStaticNode;
  23. }
  24. OBJECT_DESTROYED
  25. }
  26. ///////////////////////
  27. // IUnknown implementation
  28. ///////////////////////
  29. STDMETHODIMP CComponentData::QueryInterface(REFIID riid, LPVOID *ppv)
  30. {
  31. if( !ppv )
  32. return E_FAIL;
  33. *ppv = NULL;
  34. if( IsEqualIID( riid, IID_IUnknown ) )
  35. *ppv = static_cast<IComponentData *>(this);
  36. else if( IsEqualIID( riid, IID_IComponentData ) )
  37. *ppv = static_cast<IComponentData *>(this);
  38. else if( IsEqualIID( riid, IID_IExtendPropertySheet ) ||
  39. IsEqualIID( riid, IID_IExtendPropertySheet2 ) )
  40. *ppv = static_cast<IExtendPropertySheet2 *>(this);
  41. else if( IsEqualIID(riid, IID_IExtendContextMenu ) )
  42. *ppv = static_cast<IExtendContextMenu *>(this);
  43. else if( IsEqualIID( riid, IID_IPersistStream ) )
  44. *ppv = static_cast<IPersistStream *>(this);
  45. else if( IsEqualIID( riid, IID_ISnapinHelp ) )
  46. *ppv = static_cast<ISnapinHelp *>(this);
  47. if( *ppv )
  48. {
  49. reinterpret_cast<IUnknown *>(*ppv)->AddRef();
  50. return S_OK;
  51. }
  52. return E_NOINTERFACE;
  53. }
  54. STDMETHODIMP_(ULONG) CComponentData::AddRef()
  55. {
  56. return InterlockedIncrement(( LONG *)&m_cref );
  57. }
  58. STDMETHODIMP_(ULONG) CComponentData::Release()
  59. {
  60. if( 0 == InterlockedDecrement( (LONG *)&m_cref ) )
  61. {
  62. //
  63. // We need to decrement our object count in the DLL
  64. //
  65. delete this;
  66. return 0;
  67. }
  68. return m_cref;
  69. }
  70. ///////////////////////////////
  71. // Interface IComponentData
  72. ///////////////////////////////
  73. HRESULT CComponentData::Initialize( LPUNKNOWN pUnknown )
  74. {
  75. HRESULT hr;
  76. //
  77. // Get pointer to name space interface
  78. //
  79. hr = pUnknown->QueryInterface( IID_IConsoleNameSpace, (void **) &m_ipConsoleNameSpace );
  80. _ASSERT( S_OK == hr );
  81. //
  82. // Get pointer to console interface
  83. //
  84. hr = pUnknown->QueryInterface( IID_IConsole, (void **) &m_ipConsole );
  85. _ASSERT( S_OK == hr );
  86. IImageList *pImageList;
  87. m_ipConsole->QueryScopeImageList( &pImageList );
  88. _ASSERT( S_OK == hr );
  89. hr = pImageList->ImageListSetStrip(
  90. (long *) m_pStaticNode->m_pBMapSm, // pointer to a handle
  91. (long *) m_pStaticNode->m_pBMapLg, // pointer to a handle
  92. 0, // index of the first image in the strip
  93. RGB(0, 128, 128) // color of the icon mask
  94. );
  95. pImageList->Release();
  96. return S_OK;
  97. }
  98. HRESULT CComponentData::CreateComponent( LPCOMPONENT __RPC_FAR *ppComponent )
  99. {
  100. *ppComponent = NULL;
  101. CComponent *pComponent = new CComponent(this);
  102. if( NULL == pComponent )
  103. return E_OUTOFMEMORY;
  104. return pComponent->QueryInterface( IID_IComponent, (void **)ppComponent );
  105. }
  106. HRESULT CComponentData::ExtractData( IDataObject* piDataObject,
  107. CLIPFORMAT cfClipFormat,
  108. BYTE* pbData,
  109. DWORD cbData )
  110. {
  111. HRESULT hr = S_OK;
  112. FORMATETC formatetc = {cfClipFormat, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  113. STGMEDIUM stgmedium = {TYMED_HGLOBAL, NULL};
  114. stgmedium.hGlobal = ::GlobalAlloc(GPTR, cbData);
  115. do // false loop
  116. {
  117. if( NULL == stgmedium.hGlobal )
  118. {
  119. hr = E_OUTOFMEMORY;
  120. break;
  121. }
  122. hr = piDataObject->GetDataHere( &formatetc, &stgmedium );
  123. if( FAILED(hr) )
  124. {
  125. break;
  126. }
  127. BYTE* pbNewData = reinterpret_cast<BYTE*>(stgmedium.hGlobal);
  128. if( NULL == pbNewData )
  129. {
  130. hr = E_UNEXPECTED;
  131. break;
  132. }
  133. ::memcpy( pbData, pbNewData, cbData );
  134. }
  135. while( FALSE ); // false loop
  136. if( NULL != stgmedium.hGlobal )
  137. {
  138. ::GlobalFree(stgmedium.hGlobal);
  139. }
  140. return hr;
  141. } // ExtractData()
  142. HRESULT CComponentData::ExtractString( IDataObject *piDataObject,
  143. CLIPFORMAT cfClipFormat,
  144. WCHAR *pstr,
  145. DWORD cchMaxLength )
  146. {
  147. return ExtractData( piDataObject, cfClipFormat, (PBYTE)pstr, cchMaxLength );
  148. }
  149. HRESULT CComponentData::ExtractSnapInCLSID( IDataObject* piDataObject, CLSID* pclsidSnapin )
  150. {
  151. return ExtractData( piDataObject, static_cast<CLIPFORMAT>(CDataObject::s_cfSnapinClsid), (PBYTE)pclsidSnapin, sizeof(CLSID) );
  152. }
  153. HRESULT CComponentData::ExtractComputerNameExt( IDataObject * pDataObject, tstring& strComputer )
  154. {
  155. //
  156. // Find the computer name from the ComputerManagement snapin
  157. //
  158. CLIPFORMAT CCF_MyComputMachineName = (CLIPFORMAT) RegisterClipboardFormat( _T("MMC_SNAPIN_MACHINE_NAME") );
  159. STGMEDIUM stgmedium = { TYMED_HGLOBAL, NULL };
  160. FORMATETC formatetc = {
  161. CCF_MyComputMachineName, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL
  162. };
  163. //
  164. // Allocate memory for the stream
  165. //
  166. int len = MAX_PATH;
  167. stgmedium.hGlobal = GlobalAlloc( GMEM_SHARE, len );
  168. if( NULL == stgmedium.hGlobal )
  169. {
  170. return ERROR_NOT_ENOUGH_MEMORY;
  171. }
  172. HRESULT hr = pDataObject->GetDataHere( &formatetc, &stgmedium );
  173. _ASSERT( SUCCEEDED(hr) );
  174. //
  175. // Get the computer name
  176. //
  177. strComputer = (LPTSTR) stgmedium.hGlobal;
  178. GlobalFree( stgmedium.hGlobal );
  179. return hr;
  180. }
  181. HRESULT CComponentData::ExtractObjectTypeGUID( IDataObject* piDataObject, GUID* pguidObjectType )
  182. {
  183. return ExtractData( piDataObject, static_cast<CLIPFORMAT>(CDataObject::s_cfNodeType), (PBYTE)pguidObjectType, sizeof(GUID) );
  184. }
  185. HRESULT CComponentData::Notify( LPDATAOBJECT lpDataObject, MMC_NOTIFY_TYPE event, LPARAM arg, LPARAM param )
  186. {
  187. MMCN_Crack(TRUE, lpDataObject, this, NULL, event, arg, param);
  188. HRESULT hr = S_FALSE;
  189. if( NULL == lpDataObject )
  190. {
  191. if( MMCN_PROPERTY_CHANGE == event )
  192. {
  193. CDelegationBase* pNode = (CDelegationBase*) param;
  194. if( NULL == pNode )
  195. {
  196. return E_INVALIDARG;
  197. }
  198. else
  199. {
  200. return pNode->OnPropertyChange( this->m_ipConsole, NULL );
  201. }
  202. }
  203. return S_FALSE;
  204. }
  205. switch( event )
  206. {
  207. case MMCN_EXPAND:
  208. {
  209. GUID guid;
  210. //
  211. // Extract GUID of the currently selected node type
  212. // from the data object
  213. //
  214. memset( &guid, 0, sizeof( GUID ) );
  215. hr = ExtractObjectTypeGUID( lpDataObject, &guid );
  216. if( FAILED( hr ) )
  217. {
  218. return E_INVALIDARG;
  219. }
  220. //
  221. // Check to see if the "Services and Applications" node
  222. // is being expanded. If so add our stuff.
  223. //
  224. if( IsEqualGUID( guid, CLSID_CServicesAndApplications ) )
  225. {
  226. m_pStaticNode->SetExtension( true );
  227. //
  228. // Turn on Extension mode
  229. //
  230. tstring strComputerName;
  231. hr = ExtractComputerNameExt( lpDataObject, strComputerName );
  232. OutputDebugString( _T("Computer Name is: ") );
  233. OutputDebugString( strComputerName.c_str() );
  234. OutputDebugString( _T("\n") );
  235. m_pStaticNode->SetRemoteComputerName( strComputerName.c_str() );
  236. OnExpand( m_ipConsoleNameSpace, m_ipConsole, (HSCOPEITEM) param );
  237. }
  238. else
  239. {
  240. //
  241. // Get our data object. If it is NULL, we return with S_FALSE.
  242. // See implementation of GetOurDataObject() to see how to
  243. // handle special data objects.
  244. //
  245. CDataObject *pDataObject = GetOurDataObject( lpDataObject );
  246. if( NULL == pDataObject )
  247. {
  248. return S_FALSE;
  249. }
  250. CDelegationBase *base = pDataObject->GetBaseNodeObject();
  251. if( NULL == base )
  252. {
  253. return S_FALSE;
  254. }
  255. hr = base->OnExpand( m_ipConsoleNameSpace, m_ipConsole, (HSCOPEITEM) param );
  256. }
  257. break;
  258. }
  259. case MMCN_PROPERTY_CHANGE:
  260. {
  261. //
  262. // Get our data object. If it is NULL, we return with S_FALSE.
  263. // See implementation of GetOurDataObject() to see how to
  264. // handle special data objects.
  265. //
  266. CDataObject *pDataObject = GetOurDataObject( lpDataObject );
  267. if( NULL == pDataObject )
  268. {
  269. return S_FALSE;
  270. }
  271. CDelegationBase *base = pDataObject->GetBaseNodeObject();
  272. if( NULL == base )
  273. {
  274. return S_FALSE;
  275. }
  276. //
  277. // We pass CComponentData's stored IConsole pointer here,
  278. // so that the IConsole::UpdateAllViews can be called in OnPropertyChange
  279. //
  280. hr = base->OnPropertyChange( m_ipConsole, NULL );
  281. break;
  282. }
  283. case MMCN_DELETE:
  284. {
  285. //
  286. // Get our data object. If it is NULL, we return with S_FALSE.
  287. // See implementation of GetOurDataObject() to see how to
  288. // handle special data objects.
  289. //
  290. CDataObject *pDataObject = GetOurDataObject( lpDataObject );
  291. if( NULL == pDataObject )
  292. {
  293. return S_FALSE;
  294. }
  295. CDelegationBase *base = pDataObject->GetBaseNodeObject();
  296. if( NULL == base )
  297. {
  298. return S_FALSE;
  299. }
  300. hr = base->OnDelete( m_ipConsoleNameSpace, m_ipConsole );
  301. break;
  302. }
  303. case MMCN_REMOVE_CHILDREN:
  304. {
  305. OutputDebugString( _T( "inside MMCN_REMOVE_CHILDREN handler.\r\n" ) );
  306. HSCOPEITEM hToBeDeleted = (HSCOPEITEM)arg;
  307. HSCOPEITEM hStaticNodesParent = m_pStaticNode->GetParentScopeItem();
  308. if( hToBeDeleted == hStaticNodesParent )
  309. {
  310. hr = m_pStaticNode->RemoveChildren( m_ipConsoleNameSpace );
  311. }
  312. break;
  313. }
  314. }
  315. return hr;
  316. }
  317. HRESULT CComponentData::OnExpand(IConsoleNameSpace *pConsoleNameSpace, IConsole *pConsole, HSCOPEITEM parent)
  318. {
  319. //
  320. // Now fill an SCOPEDATAITEM for each item and then insert it
  321. //
  322. SCOPEDATAITEM sdi;
  323. if( !m_bExpanded )
  324. {
  325. //
  326. // Create the child nodes, then expand them
  327. //
  328. ZeroMemory( &sdi, sizeof(SCOPEDATAITEM) );
  329. sdi.mask = SDI_STR | // Displayname is valid
  330. SDI_PARAM | // lParam is valid
  331. SDI_IMAGE | // nImage is valid
  332. SDI_PARENT | // relativeID is valid
  333. SDI_OPENIMAGE |
  334. SDI_CHILDREN; // cChildren is valid
  335. sdi.relativeID = (HSCOPEITEM)parent;
  336. sdi.nImage = m_pStaticNode->GetBitmapIndex();
  337. sdi.nOpenImage = m_pStaticNode->GetBitmapIndex();
  338. sdi.displayname = MMC_CALLBACK;
  339. sdi.lParam = (LPARAM) m_pStaticNode;
  340. sdi.cChildren = 1;
  341. HRESULT hr = pConsoleNameSpace->InsertItem( &sdi );
  342. m_pStaticNode->SetScopeItemValue( sdi.ID );
  343. m_pStaticNode->SetParentScopeItem( sdi.relativeID );
  344. _ASSERT( SUCCEEDED(hr) );
  345. }
  346. return S_OK;
  347. }
  348. HRESULT CComponentData::Destroy( void )
  349. {
  350. //
  351. // Free interfaces
  352. //
  353. if( m_ipConsoleNameSpace )
  354. {
  355. m_ipConsoleNameSpace->Release();
  356. m_ipConsoleNameSpace = NULL;
  357. }
  358. if( m_ipConsole )
  359. {
  360. m_ipConsole->Release();
  361. m_ipConsole = NULL;
  362. }
  363. return S_OK;
  364. }
  365. HRESULT CComponentData::QueryDataObject(
  366. /* [in] */ MMC_COOKIE cookie,
  367. /* [in] */ DATA_OBJECT_TYPES type,
  368. /* [out] */LPDATAOBJECT *ppDataObject )
  369. {
  370. CDataObject *pObj = NULL;
  371. if( 0 == cookie )
  372. pObj = new CDataObject( (MMC_COOKIE) m_pStaticNode, type );
  373. else
  374. pObj = new CDataObject( cookie, type );
  375. if( !pObj )
  376. return E_OUTOFMEMORY;
  377. pObj->QueryInterface( IID_IDataObject, (void **) ppDataObject );
  378. return S_OK;
  379. }
  380. HRESULT CComponentData::GetDisplayInfo( SCOPEDATAITEM *pScopeDataItem )
  381. {
  382. HRESULT hr = S_FALSE;
  383. //
  384. // If they are asking for the SDI_STR we have one of those to give
  385. //
  386. if( pScopeDataItem->lParam )
  387. {
  388. CDelegationBase *base = (CDelegationBase *) pScopeDataItem->lParam;
  389. if( NULL == base )
  390. {
  391. return hr;
  392. }
  393. if( pScopeDataItem->mask & SDI_STR )
  394. {
  395. LPCTSTR pszT = base->GetDisplayName();
  396. if( NULL == pszT )
  397. {
  398. return E_OUTOFMEMORY;
  399. }
  400. pScopeDataItem->displayname = const_cast<LPOLESTR>( pszT );
  401. }
  402. if( pScopeDataItem->mask & SDI_IMAGE )
  403. {
  404. pScopeDataItem->nImage = base->GetBitmapIndex();
  405. }
  406. }
  407. return hr;
  408. }
  409. HRESULT CComponentData::CompareObjects( LPDATAOBJECT piDataObjectA, LPDATAOBJECT piDataObjectB )
  410. {
  411. if( ( NULL == piDataObjectA ) || ( NULL == piDataObjectB ) )
  412. {
  413. return E_INVALIDARG;
  414. }
  415. CDataObject *pDataObjectA = GetOurDataObject( piDataObjectA );
  416. if( NULL == pDataObjectA )
  417. {
  418. return E_FAIL;
  419. }
  420. CDataObject *pDataObjectB = GetOurDataObject( piDataObjectB );
  421. if( NULL == pDataObjectB )
  422. {
  423. return E_FAIL;
  424. }
  425. CDelegationBase *baseA = pDataObjectA->GetBaseNodeObject();
  426. if( NULL == baseA )
  427. {
  428. return E_FAIL;
  429. }
  430. CDelegationBase *baseB = pDataObjectB->GetBaseNodeObject();
  431. if( NULL == baseB )
  432. {
  433. return E_FAIL;
  434. }
  435. //
  436. // Compare the object pointers
  437. //
  438. if( baseA->GetCookie() == baseB->GetCookie() )
  439. {
  440. return S_OK;
  441. }
  442. else
  443. {
  444. return S_FALSE;
  445. }
  446. }
  447. ///////////////////////////////////
  448. // Interface IExtendPropertySheet2
  449. ///////////////////////////////////
  450. HRESULT CComponentData::CreatePropertyPages(
  451. /* [in] */ LPPROPERTYSHEETCALLBACK lpProvider,
  452. /* [in] */ LONG_PTR handle,
  453. /* [in] */ LPDATAOBJECT piDataObject )
  454. {
  455. if( ( NULL == lpProvider ) || ( NULL == handle ) || ( NULL == piDataObject ) )
  456. {
  457. return E_INVALIDARG;
  458. }
  459. CDataObject *pDataObject = GetOurDataObject( piDataObject );
  460. if( NULL == pDataObject )
  461. {
  462. return E_FAIL;
  463. }
  464. CDelegationBase *base = pDataObject->GetBaseNodeObject();
  465. if( NULL == base )
  466. {
  467. return E_FAIL;
  468. }
  469. return base->CreatePropertyPages( lpProvider, handle );
  470. }
  471. HRESULT CComponentData::QueryPagesFor( LPDATAOBJECT piDataObject )
  472. {
  473. if( NULL == piDataObject )
  474. {
  475. return E_INVALIDARG;
  476. }
  477. CDataObject *pDataObject = GetOurDataObject( piDataObject );
  478. if( NULL == pDataObject )
  479. {
  480. return E_FAIL;
  481. }
  482. CDelegationBase *base = pDataObject->GetBaseNodeObject();
  483. if( NULL == base )
  484. {
  485. return E_FAIL;
  486. }
  487. return base->HasPropertySheets();
  488. }
  489. HRESULT CComponentData::GetWatermarks(
  490. /* [in] */ LPDATAOBJECT piDataObject,
  491. /* [out] */ HBITMAP __RPC_FAR *lphWatermark,
  492. /* [out] */ HBITMAP __RPC_FAR *lphHeader,
  493. /* [out] */ HPALETTE __RPC_FAR *lphPalette,
  494. /* [out] */ BOOL __RPC_FAR *bStretch)
  495. {
  496. if( ( NULL == piDataObject ) || ( NULL == lphWatermark ) || ( NULL == lphHeader ) || ( NULL == lphPalette ) || ( NULL == bStretch ) )
  497. {
  498. return E_INVALIDARG;
  499. }
  500. CDataObject *pDataObject = GetOurDataObject( piDataObject );
  501. if( NULL == pDataObject )
  502. {
  503. return E_FAIL;
  504. }
  505. CDelegationBase *base = pDataObject->GetBaseNodeObject();
  506. if( NULL == base )
  507. {
  508. return E_FAIL;
  509. }
  510. return base->GetWatermarks( lphWatermark, lphHeader, lphPalette, bStretch );
  511. }
  512. ///////////////////////////////
  513. // Interface IExtendContextMenu
  514. ///////////////////////////////
  515. HRESULT CComponentData::AddMenuItems(
  516. /* [in] */ LPDATAOBJECT piDataObject,
  517. /* [in] */ LPCONTEXTMENUCALLBACK piCallback,
  518. /* [out][in] */ long __RPC_FAR *pInsertionAllowed)
  519. {
  520. if( ( NULL == piDataObject ) || ( NULL == piCallback ) || ( NULL == pInsertionAllowed ) )
  521. {
  522. return E_INVALIDARG;
  523. }
  524. CDataObject *pDataObject = GetOurDataObject( piDataObject );
  525. if( NULL == pDataObject )
  526. {
  527. return E_FAIL;
  528. }
  529. CDelegationBase *base = pDataObject->GetBaseNodeObject();
  530. if( NULL == base )
  531. {
  532. return E_FAIL;
  533. }
  534. return base->OnAddMenuItems( piCallback, pInsertionAllowed );
  535. }
  536. HRESULT CComponentData::Command( long lCommandID, LPDATAOBJECT piDataObject )
  537. {
  538. if( NULL == piDataObject )
  539. {
  540. return E_INVALIDARG;
  541. }
  542. CDataObject *pDataObject = GetOurDataObject( piDataObject );
  543. if( NULL == pDataObject )
  544. {
  545. return E_FAIL;
  546. }
  547. CDelegationBase *base = pDataObject->GetBaseNodeObject();
  548. if( NULL == base )
  549. {
  550. return E_FAIL;
  551. }
  552. return base->OnMenuCommand( m_ipConsole, m_ipConsoleNameSpace, lCommandID, piDataObject );
  553. }
  554. ///////////////////////////////
  555. // Interface IPersistStream
  556. ///////////////////////////////
  557. HRESULT CComponentData::GetClassID( /*[out]*/ CLSID *pClassID )
  558. {
  559. *pClassID = m_pStaticNode->getNodeType();
  560. return S_OK;
  561. }
  562. HRESULT CComponentData::IsDirty()
  563. {
  564. return m_pStaticNode->IsDirty() ? S_OK : S_FALSE;
  565. }
  566. HRESULT CComponentData::Load( /*[unique][in]*/ IStream *pStm )
  567. {
  568. return m_pStaticNode->Load( pStm );
  569. }
  570. HRESULT CComponentData::Save( /*[unique][in]*/ IStream *pStm, /*[in]*/ BOOL fClearDirty )
  571. {
  572. return m_pStaticNode->Save( pStm, fClearDirty );
  573. }
  574. HRESULT CComponentData::GetSizeMax( /*[out]*/ ULARGE_INTEGER *pcbSize )
  575. {
  576. ULARGE_INTEGER cbSize = { m_pStaticNode->GetSizeMax(), 0 };
  577. *pcbSize = cbSize;
  578. return S_OK;
  579. }
  580. ///////////////////////////////
  581. // Interface ISnapinHelp
  582. ///////////////////////////////
  583. HRESULT CComponentData::GetHelpTopic( LPOLESTR* lpCompiledHelpFile )
  584. {
  585. try
  586. {
  587. if( NULL == lpCompiledHelpFile )
  588. {
  589. return E_INVALIDARG;
  590. }
  591. //
  592. // Determine the help file location based
  593. // on the install root
  594. //
  595. WCHAR szWindowsDir[ MAX_PATH ];
  596. szWindowsDir[ 0 ] = 0x00;
  597. UINT nSize = GetWindowsDirectoryW( szWindowsDir, MAX_PATH );
  598. if( 0 == nSize || nSize > MAX_PATH )
  599. return E_FAIL;
  600. wstring strHelpFile = szWindowsDir;
  601. strHelpFile += L"\\Help\\";
  602. WCHAR szHelpFileName[ 100 ];
  603. szHelpFileName[ 0 ] = 0x00;
  604. LoadString( g_hinst, IDS_UDDIMMC_HELPFILE, szHelpFileName, ARRAYLEN( szHelpFileName ) );
  605. strHelpFile += szHelpFileName;
  606. UINT nBytes = ( strHelpFile.length() + 1 ) * sizeof(WCHAR);
  607. *lpCompiledHelpFile = (LPOLESTR) CoTaskMemAlloc( nBytes );
  608. if( NULL == *lpCompiledHelpFile )
  609. {
  610. return E_OUTOFMEMORY;
  611. }
  612. memcpy( *lpCompiledHelpFile, strHelpFile.c_str(), nBytes );
  613. return S_OK;
  614. }
  615. catch( ... )
  616. {
  617. return E_OUTOFMEMORY;
  618. }
  619. }
  620. HRESULT CComponentData::GetLinkedTopics( LPOLESTR* lpCompiledHelpFiles )
  621. {
  622. return E_NOTIMPL;
  623. }