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.

777 lines
19 KiB

  1. /////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (C) 1998-2000 Microsoft Corporation
  4. //
  5. // Module Name:
  6. // ExtObj.cpp
  7. //
  8. // Description:
  9. // Implementation of the CExtObject class, which implements the
  10. // extension interfaces required by a Microsoft Windows NT Cluster
  11. // Administrator Extension DLL.
  12. //
  13. // Maintained By:
  14. // Galen Barbee (GalenB) Mmmm DD, 1998
  15. //
  16. // Revision History:
  17. //
  18. // Notes:
  19. //
  20. /////////////////////////////////////////////////////////////////////////////
  21. #include "stdafx.h"
  22. #include "DummyEx.h"
  23. #include "ExtObj.h"
  24. #include "ResProp.h"
  25. /////////////////////////////////////////////////////////////////////////////
  26. // Global Variables
  27. /////////////////////////////////////////////////////////////////////////////
  28. const WCHAR g_wszResourceTypeNames[] =
  29. L"Dummy\0"
  30. ;
  31. const DWORD g_cchResourceTypeNames = sizeof(g_wszResourceTypeNames) / sizeof(WCHAR);
  32. static CRuntimeClass * g_rgprtcResPSPages[] = {
  33. RUNTIME_CLASS(CDummyParamsPage),
  34. NULL
  35. };
  36. static CRuntimeClass ** g_rgpprtcResPSPages[] = {
  37. g_rgprtcResPSPages,
  38. };
  39. static CRuntimeClass ** g_rgpprtcResWizPages[] = {
  40. g_rgprtcResPSPages,
  41. };
  42. /////////////////////////////////////////////////////////////////////////////
  43. // CExtObject
  44. /////////////////////////////////////////////////////////////////////////////
  45. /////////////////////////////////////////////////////////////////////////////
  46. //++
  47. //
  48. // CExtObject::CExtObject
  49. //
  50. // Routine Description:
  51. // Default constructor.
  52. //
  53. // Arguments:
  54. // None.
  55. //
  56. // Return Value:
  57. // None.
  58. //
  59. //--
  60. /////////////////////////////////////////////////////////////////////////////
  61. CExtObject::CExtObject(void)
  62. {
  63. m_piData = NULL;
  64. m_piWizardCallback = NULL;
  65. m_bWizard = FALSE;
  66. m_istrResTypeName = 0;
  67. m_lcid = NULL;
  68. m_hfont = NULL;
  69. m_hicon = NULL;
  70. m_hcluster = NULL;
  71. m_cobj = 0;
  72. m_podObjData = NULL;
  73. } //*** CExtObject::CExtObject()
  74. /////////////////////////////////////////////////////////////////////////////
  75. //++
  76. //
  77. // CExtObject::~CExtObject
  78. //
  79. // Routine Description:
  80. // Destructor.
  81. //
  82. // Arguments:
  83. // None.
  84. //
  85. // Return Value:
  86. // None.
  87. //
  88. //--
  89. /////////////////////////////////////////////////////////////////////////////
  90. CExtObject::~CExtObject(void)
  91. {
  92. // Release the data interface.
  93. if (PiData() != NULL)
  94. {
  95. PiData()->Release();
  96. m_piData = NULL;
  97. } // if: we have a data interface pointer
  98. // Release the wizard callback interface.
  99. if (PiWizardCallback() != NULL)
  100. {
  101. PiWizardCallback()->Release();
  102. m_piWizardCallback = NULL;
  103. } // if: we have a wizard callback interface pointer
  104. // Delete the pages.
  105. {
  106. POSITION pos;
  107. pos = Lpg().GetHeadPosition();
  108. while (pos != NULL)
  109. delete Lpg().GetNext(pos);
  110. } // Delete the pages
  111. delete m_podObjData;
  112. } //*** CExtObject::~CExtObject()
  113. /////////////////////////////////////////////////////////////////////////////
  114. // ISupportErrorInfo Implementation
  115. /////////////////////////////////////////////////////////////////////////////
  116. //++
  117. //
  118. // CExtObject::InterfaceSupportsErrorInfo (ISupportErrorInfo)
  119. //
  120. // Routine Description:
  121. // Indicates whether an interface suportes the IErrorInfo interface.
  122. // This interface is provided by ATL.
  123. //
  124. // Arguments:
  125. // riid Interface ID.
  126. //
  127. // Return Value:
  128. // S_OK Interface supports IErrorInfo.
  129. // S_FALSE Interface does not support IErrorInfo.
  130. //
  131. //--
  132. /////////////////////////////////////////////////////////////////////////////
  133. STDMETHODIMP CExtObject::InterfaceSupportsErrorInfo(REFIID riid)
  134. {
  135. static const IID * rgiid[] =
  136. {
  137. &IID_IWEExtendPropertySheet,
  138. &IID_IWEExtendWizard,
  139. };
  140. int iiid;
  141. for (iiid = 0 ; iiid < sizeof(rgiid) / sizeof(rgiid[0]) ; iiid++)
  142. {
  143. if (InlineIsEqualGUID(*rgiid[iiid], riid))
  144. return S_OK;
  145. }
  146. return S_FALSE;
  147. } //*** CExtObject::InterfaceSupportsErrorInfo()
  148. /////////////////////////////////////////////////////////////////////////////
  149. // IWEExtendPropertySheet Implementation
  150. /////////////////////////////////////////////////////////////////////////////
  151. //++
  152. //
  153. // CExtObject::CreatePropertySheetPages (IWEExtendPropertySheet)
  154. //
  155. // Routine Description:
  156. // Create property sheet pages and add them to the sheet.
  157. //
  158. // Arguments:
  159. // piData IUnkown pointer from which to obtain interfaces
  160. // for obtaining data describing the object for
  161. // which the sheet is being displayed.
  162. // piCallback Pointer to an IWCPropertySheetCallback interface
  163. // for adding pages to the sheet.
  164. //
  165. // Return Value:
  166. // NOERROR Pages added successfully.
  167. // E_INVALIDARG Invalid arguments to the function.
  168. // E_OUTOFMEMORY Error allocating memory.
  169. // E_FAIL Error creating a page.
  170. // E_NOTIMPL Not implemented for this type of data.
  171. // Any error codes from IDataObject::GetData() (through HrSaveData()).
  172. //
  173. //--
  174. /////////////////////////////////////////////////////////////////////////////
  175. STDMETHODIMP CExtObject::CreatePropertySheetPages(
  176. IN IUnknown * piData,
  177. IN IWCPropertySheetCallback * piCallback
  178. )
  179. {
  180. HRESULT hr = NOERROR;
  181. HPROPSHEETPAGE hpage = NULL;
  182. CException exc(FALSE /*bAutoDelete*/);
  183. CRuntimeClass ** pprtc = NULL;
  184. int irtc;
  185. CBasePropertyPage * ppage;
  186. AFX_MANAGE_STATE(AfxGetStaticModuleState());
  187. // Validate the parameters.
  188. if ((piData == NULL) || (piCallback == NULL))
  189. return E_INVALIDARG;
  190. try
  191. {
  192. // Get info about displaying UI.
  193. hr = HrGetUIInfo(piData);
  194. if (hr != NOERROR)
  195. throw &exc;
  196. // Save the data.
  197. hr = HrSaveData(piData);
  198. if (hr != NOERROR)
  199. throw &exc;
  200. // Delete any previous pages.
  201. {
  202. POSITION pos;
  203. pos = Lpg().GetHeadPosition();
  204. while (pos != NULL)
  205. delete Lpg().GetNext(pos);
  206. Lpg().RemoveAll();
  207. } // Delete any previous pages
  208. // Create property pages.
  209. ASSERT(PodObjData() != NULL);
  210. switch (PodObjData()->m_cot)
  211. {
  212. case CLUADMEX_OT_RESOURCE:
  213. pprtc = g_rgpprtcResPSPages[IstrResTypeName()];
  214. break;
  215. default:
  216. hr = E_NOTIMPL;
  217. throw &exc;
  218. break;
  219. } // switch: object type
  220. // Create each page.
  221. for (irtc = 0 ; pprtc[irtc] != NULL ; irtc++)
  222. {
  223. // Create the page.
  224. ppage = (CBasePropertyPage *) pprtc[irtc]->CreateObject();
  225. ASSERT(ppage->IsKindOf(pprtc[irtc]));
  226. // Add it to the list.
  227. Lpg().AddTail(ppage);
  228. // Initialize the property page.
  229. if (!ppage->BInit(this))
  230. throw &exc;
  231. // Create the page.
  232. hpage = ::CreatePropertySheetPage(&ppage->m_psp);
  233. if (hpage == NULL)
  234. throw &exc;
  235. // Save the hpage in the page itself.
  236. ppage->SetHpage(hpage);
  237. // Add it to the property sheet.
  238. hr = piCallback->AddPropertySheetPage((LONG *) hpage);
  239. if (hr != NOERROR)
  240. throw &exc;
  241. } // for: each page in the list
  242. } // try
  243. catch (CMemoryException * pme)
  244. {
  245. TRACE(_T("CExtObject::CreatePropetySheetPages() - Failed to add property page\n"));
  246. pme->Delete();
  247. hr = E_OUTOFMEMORY;
  248. } // catch: anything
  249. catch (CException * pe)
  250. {
  251. TRACE(_T("CExtObject::CreatePropetySheetPages() - Failed to add property page\n"));
  252. pe->Delete();
  253. if (hr == NOERROR)
  254. hr = E_FAIL;
  255. } // catch: anything
  256. if (hr != NOERROR)
  257. {
  258. if (hpage != NULL)
  259. ::DestroyPropertySheetPage(hpage);
  260. piData->Release();
  261. m_piData = NULL;
  262. } // if: error occurred
  263. piCallback->Release();
  264. return hr;
  265. } //*** CExtObject::CreatePropertySheetPages()
  266. /////////////////////////////////////////////////////////////////////////////
  267. // IWEExtendWizard Implementation
  268. /////////////////////////////////////////////////////////////////////////////
  269. //++
  270. //
  271. // CExtObject::CreateWizardPages (IWEExtendWizard)
  272. //
  273. // Routine Description:
  274. // Create property sheet pages and add them to the wizard.
  275. //
  276. // Arguments:
  277. // piData IUnkown pointer from which to obtain interfaces
  278. // for obtaining data describing the object for
  279. // which the wizard is being displayed.
  280. // piCallback Pointer to an IWCPropertySheetCallback interface
  281. // for adding pages to the sheet.
  282. //
  283. // Return Value:
  284. // NOERROR Pages added successfully.
  285. // E_INVALIDARG Invalid arguments to the function.
  286. // E_OUTOFMEMORY Error allocating memory.
  287. // E_FAIL Error creating a page.
  288. // E_NOTIMPL Not implemented for this type of data.
  289. // Any error codes from IDataObject::GetData() (through HrSaveData()).
  290. //
  291. //--
  292. /////////////////////////////////////////////////////////////////////////////
  293. STDMETHODIMP CExtObject::CreateWizardPages(
  294. IN IUnknown * piData,
  295. IN IWCWizardCallback * piCallback
  296. )
  297. {
  298. HRESULT hr = NOERROR;
  299. HPROPSHEETPAGE hpage = NULL;
  300. CException exc(FALSE /*bAutoDelete*/);
  301. CRuntimeClass ** pprtc = NULL;
  302. int irtc;
  303. CBasePropertyPage * ppage;
  304. AFX_MANAGE_STATE(AfxGetStaticModuleState());
  305. // Validate the parameters.
  306. if ((piData == NULL) || (piCallback == NULL))
  307. return E_INVALIDARG;
  308. try
  309. {
  310. // Get info about displaying UI.
  311. hr = HrGetUIInfo(piData);
  312. if (hr != NOERROR)
  313. throw &exc;
  314. // Save the data.
  315. hr = HrSaveData(piData);
  316. if (hr != NOERROR)
  317. throw &exc;
  318. // Delete any previous pages.
  319. {
  320. POSITION pos;
  321. pos = Lpg().GetHeadPosition();
  322. while (pos != NULL)
  323. delete Lpg().GetNext(pos);
  324. Lpg().RemoveAll();
  325. } // Delete any previous pages
  326. m_piWizardCallback = piCallback;
  327. m_bWizard = TRUE;
  328. // Create property pages.
  329. ASSERT(PodObjData() != NULL);
  330. switch (PodObjData()->m_cot)
  331. {
  332. case CLUADMEX_OT_RESOURCE:
  333. pprtc = g_rgpprtcResWizPages[IstrResTypeName()];
  334. break;
  335. default:
  336. hr = E_NOTIMPL;
  337. throw &exc;
  338. break;
  339. } // switch: object type
  340. // Create each page.
  341. for (irtc = 0 ; pprtc[irtc] != NULL ; irtc++)
  342. {
  343. // Create the page.
  344. ppage = (CBasePropertyPage *) pprtc[irtc]->CreateObject();
  345. ASSERT(ppage->IsKindOf(pprtc[irtc]));
  346. // Add it to the list.
  347. Lpg().AddTail(ppage);
  348. // Initialize the property page.
  349. if (!ppage->BInit(this))
  350. throw &exc;
  351. // Create the page.
  352. hpage = ::CreatePropertySheetPage(&ppage->m_psp);
  353. if (hpage == NULL)
  354. throw &exc;
  355. // Save the hpage in the page itself.
  356. ppage->SetHpage(hpage);
  357. // Add it to the property sheet.
  358. hr = piCallback->AddWizardPage((LONG *) hpage);
  359. if (hr != NOERROR)
  360. throw &exc;
  361. } // for: each page in the list
  362. } // try
  363. catch (CMemoryException * pme)
  364. {
  365. TRACE(_T("CExtObject::CreateWizardPages() - Failed to add wizard page\n"));
  366. pme->Delete();
  367. hr = E_OUTOFMEMORY;
  368. } // catch: anything
  369. catch (CException * pe)
  370. {
  371. TRACE(_T("CExtObject::CreateWizardPages() - Failed to add wizard page\n"));
  372. pe->Delete();
  373. if (hr == NOERROR)
  374. hr = E_FAIL;
  375. } // catch: anything
  376. if (hr != NOERROR)
  377. {
  378. if (hpage != NULL)
  379. ::DestroyPropertySheetPage(hpage);
  380. piCallback->Release();
  381. piData->Release();
  382. m_piData = NULL;
  383. } // if: error occurred
  384. return hr;
  385. } //*** CExtObject::CreateWizardPages()
  386. /////////////////////////////////////////////////////////////////////////////
  387. //++
  388. //
  389. // CExtObject::HrGetUIInfo
  390. //
  391. // Routine Description:
  392. // Get info about displaying UI.
  393. //
  394. // Arguments:
  395. // piData IUnkown pointer from which to obtain interfaces
  396. // for obtaining data describing the object.
  397. //
  398. // Return Value:
  399. // NOERROR Data saved successfully.
  400. // E_NOTIMPL Not implemented for this type of data.
  401. // Any error codes from IUnknown::QueryInterface(), HrGetObjectName(),
  402. // or HrGetResourceName().
  403. //
  404. //--
  405. /////////////////////////////////////////////////////////////////////////////
  406. HRESULT CExtObject::HrGetUIInfo(IUnknown * piData)
  407. {
  408. HRESULT hr = NOERROR;
  409. ASSERT(piData != NULL);
  410. // Save info about all types of objects.
  411. {
  412. IGetClusterUIInfo * pi;
  413. hr = piData->QueryInterface(IID_IGetClusterUIInfo, (LPVOID *) &pi);
  414. if (hr != NOERROR)
  415. return hr;
  416. m_lcid = pi->GetLocale();
  417. m_hfont = pi->GetFont();
  418. m_hicon = pi->GetIcon();
  419. pi->Release();
  420. } // Save info about all types of objects
  421. return hr;
  422. } //*** CExtObject::HrGetUIInfo()
  423. /////////////////////////////////////////////////////////////////////////////
  424. //++
  425. //
  426. // CExtObject::HrSaveData
  427. //
  428. // Routine Description:
  429. // Save data from the object so that it can be used for the life
  430. // of the object.
  431. //
  432. // Arguments:
  433. // piData IUnkown pointer from which to obtain interfaces
  434. // for obtaining data describing the object.
  435. //
  436. // Return Value:
  437. // NOERROR Data saved successfully.
  438. // E_NOTIMPL Not implemented for this type of data.
  439. // Any error codes from IUnknown::QueryInterface(), HrGetObjectName(),
  440. // or HrGetResourceName().
  441. //
  442. //--
  443. /////////////////////////////////////////////////////////////////////////////
  444. HRESULT CExtObject::HrSaveData(IUnknown * piData)
  445. {
  446. HRESULT hr = NOERROR;
  447. ASSERT(piData != NULL);
  448. if (piData != m_piData)
  449. {
  450. if (m_piData != NULL)
  451. m_piData->Release();
  452. m_piData = piData;
  453. } // if: different data interface pointer
  454. // Save info about all types of objects.
  455. {
  456. IGetClusterDataInfo * pi;
  457. hr = piData->QueryInterface(IID_IGetClusterDataInfo, (LPVOID *) &pi);
  458. if (hr != NOERROR)
  459. return hr;
  460. m_hcluster = pi->GetClusterHandle();
  461. m_cobj = pi->GetObjectCount();
  462. if (Cobj() != 1) // Only have support for one selected object.
  463. hr = E_NOTIMPL;
  464. pi->Release();
  465. if (hr != NOERROR)
  466. return hr;
  467. } // Save info about all types of objects
  468. // Save info about this object.
  469. hr = HrGetObjectInfo();
  470. if (hr != NOERROR)
  471. return hr;
  472. return hr;
  473. } //*** CExtObject::HrSaveData()
  474. /////////////////////////////////////////////////////////////////////////////
  475. //++
  476. //
  477. // CExtObject::HrGetObjectInfo
  478. //
  479. // Routine Description:
  480. // Get information about the object.
  481. //
  482. // Arguments:
  483. // None.
  484. //
  485. // Return Value:
  486. // NOERROR Data saved successfully.
  487. // E_OUTOFMEMORY Error allocating memory.
  488. // E_NOTIMPL Not implemented for this type of data.
  489. // Any error codes from IUnknown::QueryInterface(), HrGetObjectName(),
  490. // or HrGetResourceTypeName().
  491. //
  492. //--
  493. /////////////////////////////////////////////////////////////////////////////
  494. HRESULT CExtObject::HrGetObjectInfo(void)
  495. {
  496. HRESULT hr = NOERROR;
  497. IGetClusterObjectInfo * piGcoi;
  498. CLUADMEX_OBJECT_TYPE cot = CLUADMEX_OT_NONE;
  499. CException exc(FALSE /*bAutoDelete*/);
  500. const CString * pstrResTypeName = NULL;
  501. ASSERT(PiData() != NULL);
  502. // Get object info.
  503. {
  504. // Get an IGetClusterObjectInfo interface pointer.
  505. hr = PiData()->QueryInterface(IID_IGetClusterObjectInfo, (LPVOID *) &piGcoi);
  506. if (hr != NOERROR)
  507. return hr;
  508. // Read the object data.
  509. try
  510. {
  511. // Delete the previous object data.
  512. delete m_podObjData;
  513. m_podObjData = NULL;
  514. // Get the type of the object.
  515. cot = piGcoi->GetObjectType(0);
  516. switch (cot)
  517. {
  518. case CLUADMEX_OT_RESOURCE:
  519. {
  520. IGetClusterResourceInfo * pi;
  521. m_podObjData = new CResData;
  522. // Get an IGetClusterResourceInfo interface pointer.
  523. hr = PiData()->QueryInterface(IID_IGetClusterResourceInfo, (LPVOID *) &pi);
  524. if (hr != NOERROR)
  525. throw &exc;
  526. PrdResDataRW()->m_hresource = pi->GetResourceHandle(0);
  527. ASSERT(PrdResDataRW()->m_hresource != NULL);
  528. if (PrdResDataRW()->m_hresource == NULL)
  529. hr = E_INVALIDARG;
  530. else
  531. hr = HrGetResourceTypeName(pi);
  532. pi->Release();
  533. if (hr != NOERROR)
  534. throw &exc;
  535. pstrResTypeName = &PrdResDataRW()->m_strResTypeName;
  536. } // if: object is a resource
  537. break;
  538. default:
  539. hr = E_NOTIMPL;
  540. throw &exc;
  541. } // switch: object type
  542. PodObjDataRW()->m_cot = cot;
  543. hr = HrGetObjectName(piGcoi);
  544. } // try
  545. catch (CException * pe)
  546. {
  547. pe->Delete();
  548. } // catch: CException
  549. piGcoi->Release();
  550. if (hr != NOERROR)
  551. return hr;
  552. } // Get object info
  553. // If this is a resource or resource type, see if we know about this type.
  554. if (((cot == CLUADMEX_OT_RESOURCE)
  555. || (cot == CLUADMEX_OT_RESOURCETYPE))
  556. && (hr == NOERROR))
  557. {
  558. LPCWSTR pwszResTypeName;
  559. // Find the resource type name in our list.
  560. // Save the index for use in other arrays.
  561. for (m_istrResTypeName = 0, pwszResTypeName = g_wszResourceTypeNames
  562. ; *pwszResTypeName != L'\0'
  563. ; m_istrResTypeName++, pwszResTypeName += lstrlenW(pwszResTypeName) + 1
  564. )
  565. {
  566. if (pstrResTypeName->CompareNoCase(pwszResTypeName) == 0)
  567. break;
  568. } // for: each resource type in the list
  569. if (*pwszResTypeName == L'\0')
  570. hr = E_NOTIMPL;
  571. } // See if we know about this resource type
  572. return hr;
  573. } //*** CExtObject::HrGetObjectInfo()
  574. /////////////////////////////////////////////////////////////////////////////
  575. //++
  576. //
  577. // CExtObject::HrGetObjectName
  578. //
  579. // Routine Description:
  580. // Get the name of the object.
  581. //
  582. // Arguments:
  583. // piData IGetClusterObjectInfo interface pointer for getting
  584. // the object name.
  585. //
  586. // Return Value:
  587. // NOERROR Data saved successfully.
  588. // E_OUTOFMEMORY Error allocating memory.
  589. // E_NOTIMPL Not implemented for this type of data.
  590. // Any error codes from IGetClusterObjectInfo::GetObjectInfo().
  591. //
  592. //--
  593. /////////////////////////////////////////////////////////////////////////////
  594. HRESULT CExtObject::HrGetObjectName(
  595. IN OUT IGetClusterObjectInfo * pi
  596. )
  597. {
  598. HRESULT hr = NOERROR;
  599. WCHAR * pwszName = NULL;
  600. LONG cchName;
  601. ASSERT(pi != NULL);
  602. hr = pi->GetObjectName(0, NULL, &cchName);
  603. if (hr != NOERROR)
  604. return hr;
  605. try
  606. {
  607. pwszName = new WCHAR[cchName];
  608. hr = pi->GetObjectName(0, pwszName, &cchName);
  609. if (hr != NOERROR)
  610. {
  611. delete [] pwszName;
  612. pwszName = NULL;
  613. } // if: error getting object name
  614. PodObjDataRW()->m_strName = pwszName;
  615. } // try
  616. catch (CMemoryException * pme)
  617. {
  618. pme->Delete();
  619. hr = E_OUTOFMEMORY;
  620. } // catch: CMemoryException
  621. delete [] pwszName;
  622. return hr;
  623. } //*** CExtObject::HrGetObjectName()
  624. /////////////////////////////////////////////////////////////////////////////
  625. //++
  626. //
  627. // CExtObject::HrGetResourceTypeName
  628. //
  629. // Routine Description:
  630. // Get the name of the resource's type.
  631. //
  632. // Arguments:
  633. // piData IGetClusterResourceInfo interface pointer for getting
  634. // the resource type name.
  635. //
  636. // Return Value:
  637. // NOERROR Data saved successfully.
  638. // E_OUTOFMEMORY Error allocating memory.
  639. // E_NOTIMPL Not implemented for this type of data.
  640. // Any error codes from IGetClusterResourceInfo::GetResourceTypeName().
  641. //
  642. //--
  643. /////////////////////////////////////////////////////////////////////////////
  644. HRESULT CExtObject::HrGetResourceTypeName(
  645. IN OUT IGetClusterResourceInfo * pi
  646. )
  647. {
  648. HRESULT hr = NOERROR;
  649. WCHAR * pwszName = NULL;
  650. LONG cchName;
  651. ASSERT(pi != NULL);
  652. hr = pi->GetResourceTypeName(0, NULL, &cchName);
  653. if (hr != NOERROR)
  654. return hr;
  655. try
  656. {
  657. pwszName = new WCHAR[cchName];
  658. hr = pi->GetResourceTypeName(0, pwszName, &cchName);
  659. if (hr != NOERROR)
  660. {
  661. delete [] pwszName;
  662. pwszName = NULL;
  663. } // if: error getting resource type name
  664. PrdResDataRW()->m_strResTypeName = pwszName;
  665. } // try
  666. catch (CMemoryException * pme)
  667. {
  668. pme->Delete();
  669. hr = E_OUTOFMEMORY;
  670. } // catch: CMemoryException
  671. delete [] pwszName;
  672. return hr;
  673. } //*** CExtObject::HrGetResourceTypeName()