Source code of Windows XP (NT5)
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.

1414 lines
41 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (C) Microsoft Corporation, 2000
  6. //
  7. // File: cpview.cpp
  8. //
  9. // This module provides the Control Panel user interface information
  10. // to the shell through the ICplView interface. The ICplView implementation
  11. // instantiates a CCplNamespace object through which it obtains the
  12. // display information on demand. CCplView then takes that information
  13. // and either makes it available to the shell for display in the webview
  14. // left-hand pane or generates a DUI element hierarchy for display in the
  15. // right-hand pane.
  16. //
  17. // The majority of the code is associated with building Direct UI content.
  18. //
  19. //--------------------------------------------------------------------------
  20. #include "shellprv.h"
  21. #include "cpviewp.h"
  22. #include "cpduihlp.h"
  23. #include "cpguids.h"
  24. #include "cplnkele.h"
  25. #include "cpnamespc.h"
  26. #include "cputil.h"
  27. #include "ids.h"
  28. #include "shstyle.h"
  29. #include <uxtheme.h>
  30. namespace CPL {
  31. class CCplView : public CObjectWithSite,
  32. public ICplView,
  33. public IServiceProvider
  34. {
  35. public:
  36. ~CCplView(void);
  37. //
  38. // IUnknown
  39. //
  40. STDMETHOD(QueryInterface)(REFIID riid, void **ppv);
  41. STDMETHOD_(ULONG, AddRef)(void);
  42. STDMETHOD_(ULONG, Release)(void);
  43. //
  44. // ICplView
  45. //
  46. STDMETHOD(EnumClassicWebViewInfo)(DWORD dwFlags, IEnumCplWebViewInfo **ppenum);
  47. STDMETHOD(EnumCategoryChoiceWebViewInfo)(DWORD dwFlags, IEnumCplWebViewInfo **ppenum);
  48. STDMETHOD(EnumCategoryWebViewInfo)(DWORD dwFlags, eCPCAT eCategory, IEnumCplWebViewInfo **ppenum);
  49. STDMETHOD(CreateCategoryChoiceElement)(DUI::Element **ppe);
  50. STDMETHOD(CreateCategoryElement)(eCPCAT eCategory, DUI::Element **ppe);
  51. STDMETHOD(GetCategoryHelpURL)(eCPCAT eCategory, LPWSTR pszURL, UINT cchURL);
  52. STDMETHOD(RefreshIDs)(IEnumIDList *penumIDs);
  53. //
  54. // IServiceProvider
  55. //
  56. STDMETHOD(QueryService)(REFGUID guidService, REFIID riid, void **ppv);
  57. static HRESULT CreateInstance(IEnumIDList *penumIDs, IUnknown *punkSite, REFIID riid, void **ppvOut);
  58. private:
  59. LONG m_cRef;
  60. ICplNamespace *m_pns;
  61. CSafeServiceSite *m_psss;
  62. ATOM m_idDirective;
  63. ATOM m_idDirective2;
  64. ATOM m_idTitle;
  65. ATOM m_idIcon;
  66. ATOM m_idCategoryList;
  67. ATOM m_idCategoryTaskList;
  68. ATOM m_idAppletList;
  69. ATOM m_idBanner;
  70. ATOM m_idBarricadeTitle;
  71. ATOM m_idBarricadeMsg;
  72. ATOM m_idContainer;
  73. CCplView::CCplView(void);
  74. HRESULT _Initialize(IEnumIDList *penumIDs, IUnknown *punkSite);
  75. HRESULT _CreateCategoryChoiceElement(DUI::Element **ppe);
  76. HRESULT _CreateCategoryElement(ICplCategory *pCategory, DUI::Element **ppe);
  77. HRESULT _BuildCategoryBanner(ICplCategory *pCategory, DUI::Element *pePrimaryPane);
  78. HRESULT _BuildCategoryBarricade(DUI::Element *peRoot);
  79. HRESULT _BuildCategoryTaskList(DUI::Parser *pParser, ICplCategory *pCategory, DUI::Element *pePrimaryPane, int *pcTasks);
  80. HRESULT _BuildCategoryAppletList(DUI::Parser *pParser, ICplCategory *pCategory, DUI::Element *pePrimaryPane, int *pcApplets);
  81. HRESULT _CreateWatermark(DUI::Element *peRoot);
  82. HRESULT _CreateAndAddListItem(DUI::Parser *pParser, DUI::Element *peList, LPCWSTR pszItemTemplate, DUI::Value *pvSsListItem, IUICommand *puic, eCPIMGSIZE eIconSize);
  83. HRESULT _IncludeCategory(ICplCategory *pCategory) const;
  84. HRESULT _AddOrDeleteAtoms(bool bAdd);
  85. HRESULT _GetStyleModuleAndResId(HINSTANCE *phInstance, UINT *pidStyle);
  86. HRESULT _LoadUiFileFromResources(HINSTANCE hInstance, int idResource, char **ppUIFile);
  87. HRESULT _BuildUiFile(char **ppUIFile, int *piCharCount, HINSTANCE *phInstance);
  88. HRESULT _CreateUiFileParser(DUI::Parser **ppParser);
  89. eCPCAT _DisplayIndexToCategoryIndex(int iCategory) const;
  90. //
  91. // Prevent copy.
  92. //
  93. CCplView(const CCplView& rhs);
  94. CCplView& operator = (const CCplView& rhs);
  95. };
  96. CCplView::CCplView(
  97. void
  98. ) : m_cRef(1),
  99. m_pns(NULL),
  100. m_idDirective(0),
  101. m_idDirective2(0),
  102. m_idTitle(0),
  103. m_idIcon(0),
  104. m_idCategoryList(0),
  105. m_idCategoryTaskList(0),
  106. m_idAppletList(0),
  107. m_idBanner(0),
  108. m_idBarricadeTitle(0),
  109. m_idBarricadeMsg(0),
  110. m_idContainer(0),
  111. m_psss(NULL)
  112. {
  113. TraceMsg(TF_LIFE, "CCplView::CCplView, this = 0x%x", this);
  114. }
  115. CCplView::~CCplView(
  116. void
  117. )
  118. {
  119. TraceMsg(TF_LIFE, "CCplView::~CCplView, this = 0x%x", this);
  120. if (NULL != m_psss)
  121. {
  122. m_psss->SetProviderWeakRef(NULL);
  123. m_psss->Release();
  124. }
  125. if (NULL != m_pns)
  126. {
  127. IUnknown_SetSite(m_pns, NULL);
  128. m_pns->Release();
  129. }
  130. _AddOrDeleteAtoms(false);
  131. }
  132. HRESULT
  133. CCplView::CreateInstance( // [static]
  134. IEnumIDList *penumIDs,
  135. IUnknown *punkSite,
  136. REFIID riid,
  137. void **ppvOut
  138. )
  139. {
  140. ASSERT(NULL != penumIDs);
  141. ASSERT(NULL != ppvOut);
  142. ASSERT(!IsBadWritePtr(ppvOut, sizeof(*ppvOut)));
  143. *ppvOut = NULL;
  144. HRESULT hr = E_OUTOFMEMORY;
  145. CCplView* pv = new CCplView();
  146. if (NULL != pv)
  147. {
  148. hr = pv->_Initialize(penumIDs, punkSite);
  149. if (SUCCEEDED(hr))
  150. {
  151. hr = pv->QueryInterface(riid, ppvOut);
  152. if (SUCCEEDED(hr))
  153. {
  154. //
  155. // Set the site of the view to the site passed into the
  156. // instance generator. This is most likely the site of
  157. // the Control Panel folder view callback.
  158. //
  159. hr = IUnknown_SetSite(static_cast<IUnknown *>(*ppvOut), punkSite);
  160. }
  161. }
  162. pv->Release();
  163. }
  164. return THR(hr);
  165. }
  166. STDMETHODIMP
  167. CCplView::QueryInterface(
  168. REFIID riid,
  169. void **ppv
  170. )
  171. {
  172. ASSERT(NULL != ppv);
  173. ASSERT(!IsBadWritePtr(ppv, sizeof(*ppv)));
  174. static const QITAB qit[] = {
  175. QITABENT(CCplView, ICplView),
  176. QITABENT(CCplView, IObjectWithSite),
  177. QITABENT(CCplView, IServiceProvider),
  178. { 0 },
  179. };
  180. HRESULT hr = QISearch(this, qit, riid, ppv);
  181. return E_NOINTERFACE == hr ? hr : THR(hr);
  182. }
  183. STDMETHODIMP_(ULONG)
  184. CCplView::AddRef(
  185. void
  186. )
  187. {
  188. TraceMsg(TF_LIFE, "CCplView::AddRef %d->%d", m_cRef, m_cRef+1);
  189. return InterlockedIncrement(&m_cRef);
  190. }
  191. STDMETHODIMP_(ULONG)
  192. CCplView::Release(
  193. void
  194. )
  195. {
  196. TraceMsg(TF_LIFE, "CCplView::Release %d<-%d", m_cRef-1, m_cRef);
  197. if (InterlockedDecrement(&m_cRef))
  198. return m_cRef;
  199. delete this;
  200. return 0;
  201. }
  202. STDMETHODIMP
  203. CCplView::QueryService(
  204. REFGUID guidService,
  205. REFIID riid,
  206. void **ppv
  207. )
  208. {
  209. DBG_ENTER(FTF_CPANEL, "CCplView::QueryService");
  210. HRESULT hr = E_NOINTERFACE;
  211. if (SID_SControlPanelView == guidService)
  212. {
  213. TraceMsg(TF_CPANEL, "SID_SControlPanelView service requested");
  214. if (IID_ICplNamespace == riid)
  215. {
  216. TraceMsg(TF_CPANEL, "SID_SControlPanelView::IID_ICplNamespace requested");
  217. ASSERT(NULL != m_pns);
  218. hr = m_pns->QueryInterface(IID_ICplNamespace, ppv);
  219. }
  220. else if (IID_ICplView == riid)
  221. {
  222. TraceMsg(TF_CPANEL, "SID_SControlPanelView::IID_ICplView requested");
  223. ASSERT(NULL != m_pns);
  224. hr = this->QueryInterface(IID_ICplView, ppv);
  225. }
  226. }
  227. else
  228. {
  229. //
  230. // Most likely a command object requesting SID_STopLevelBrowser.
  231. //
  232. TraceMsg(TF_CPANEL, "Handing service request to view's site.");
  233. ASSERT(NULL != _punkSite);
  234. hr = IUnknown_QueryService(_punkSite, guidService, riid, ppv);
  235. }
  236. DBG_EXIT_HRES(FTF_CPANEL, "CCplView::QueryService", hr);
  237. return THR(hr);
  238. }
  239. STDMETHODIMP
  240. CCplView::EnumClassicWebViewInfo(
  241. DWORD dwFlags,
  242. IEnumCplWebViewInfo **ppenum
  243. )
  244. {
  245. DBG_ENTER(FTF_CPANEL, "CCplView::EnumClassicWebViewInfo");
  246. ASSERT(NULL != ppenum);
  247. ASSERT(!IsBadWritePtr(ppenum, sizeof(*ppenum)));
  248. ASSERT(NULL != m_pns);
  249. HRESULT hr = m_pns->EnumClassicWebViewInfo(dwFlags, ppenum);
  250. DBG_EXIT_HRES(FTF_CPANEL, "CCplView::EnumClassicWebViewInfo", hr);
  251. return THR(hr);
  252. }
  253. //
  254. // Returns an enumerator for webview info associated with
  255. // the category 'choice' page.
  256. //
  257. STDMETHODIMP
  258. CCplView::EnumCategoryChoiceWebViewInfo(
  259. DWORD dwFlags,
  260. IEnumCplWebViewInfo **ppenum
  261. )
  262. {
  263. DBG_ENTER(FTF_CPANEL, "CCplView::EnumCategoryChoiceWebViewInfo");
  264. ASSERT(NULL != ppenum);
  265. ASSERT(!IsBadWritePtr(ppenum, sizeof(*ppenum)));
  266. ASSERT(NULL != m_pns);
  267. HRESULT hr = m_pns->EnumWebViewInfo(dwFlags, ppenum);
  268. DBG_EXIT_HRES(FTF_CPANEL, "CCplView::EnumCategoryChoiceWebViewInfo", hr);
  269. return THR(hr);
  270. }
  271. //
  272. // Returns an enumerator for webview info associated with
  273. // a given category page.
  274. //
  275. STDMETHODIMP
  276. CCplView::EnumCategoryWebViewInfo(
  277. DWORD dwFlags,
  278. eCPCAT eCategory,
  279. IEnumCplWebViewInfo **ppenum
  280. )
  281. {
  282. DBG_ENTER(FTF_CPANEL, "CCplView::EnumCategoryWebViewInfo");
  283. ASSERT(NULL != ppenum);
  284. ASSERT(!IsBadWritePtr(ppenum, sizeof(*ppenum)));
  285. ASSERT(NULL != m_pns);
  286. ICplCategory *pCategory;
  287. HRESULT hr = m_pns->GetCategory(eCategory, &pCategory);
  288. if (SUCCEEDED(hr))
  289. {
  290. hr = pCategory->EnumWebViewInfo(dwFlags, ppenum);
  291. ATOMICRELEASE(pCategory);
  292. }
  293. DBG_EXIT_HRES(FTF_CPANEL, "CCplView::EnumCategoryWebViewInfo", hr);
  294. return THR(hr);
  295. }
  296. //
  297. // Creates the DUI element tree for the category 'choice'
  298. // page. Returns the root of the tree.
  299. //
  300. STDMETHODIMP
  301. CCplView::CreateCategoryChoiceElement(
  302. DUI::Element **ppe
  303. )
  304. {
  305. DBG_ENTER(FTF_CPANEL, "CCplView::CreateCategoryChoiceElement");
  306. ASSERT(NULL != ppe);
  307. ASSERT(!IsBadWritePtr(ppe, sizeof(*ppe)));
  308. HRESULT hr = _CreateCategoryChoiceElement(ppe);
  309. DBG_EXIT_HRES(FTF_CPANEL, "CCplView::CreateCategoryChoiceElement", hr);
  310. return THR(hr);
  311. }
  312. //
  313. // Creates the DUI element tree for a given category page.
  314. // Returns the root of the tree.
  315. //
  316. STDMETHODIMP
  317. CCplView::CreateCategoryElement(
  318. eCPCAT eCategory,
  319. DUI::Element **ppe
  320. )
  321. {
  322. DBG_ENTER(FTF_CPANEL, "CCplView::CreateCategoryElement");
  323. TraceMsg(TF_CPANEL, "Category ID = %d", eCategory);
  324. ASSERT(NULL != ppe);
  325. ASSERT(!IsBadWritePtr(ppe, sizeof(*ppe)));
  326. ASSERT(NULL != m_pns);
  327. ICplCategory *pCategory;
  328. HRESULT hr = m_pns->GetCategory(eCategory, &pCategory);
  329. if (SUCCEEDED(hr))
  330. {
  331. hr = _CreateCategoryElement(pCategory, ppe);
  332. ATOMICRELEASE(pCategory);
  333. }
  334. DBG_EXIT_HRES(FTF_CPANEL, "CCplView::CreateCategoryElement", hr);
  335. return THR(hr);
  336. }
  337. STDMETHODIMP
  338. CCplView::GetCategoryHelpURL(
  339. CPL::eCPCAT eCategory,
  340. LPWSTR pszURL,
  341. UINT cchURL
  342. )
  343. {
  344. DBG_ENTER(FTF_CPANEL, "CCplView::GetCategoryHelpURL");
  345. ASSERT(NULL != pszURL);
  346. ASSERT(!IsBadWritePtr(pszURL, cchURL * sizeof(*pszURL)));
  347. ICplCategory *pCategory;
  348. HRESULT hr = m_pns->GetCategory(eCategory, &pCategory);
  349. if (SUCCEEDED(hr))
  350. {
  351. hr = pCategory->GetHelpURL(pszURL, cchURL);
  352. ATOMICRELEASE(pCategory);
  353. }
  354. DBG_EXIT_HRES(FTF_CPANEL, "CCplView::GetCategoryHelpURL", hr);
  355. return THR(hr);
  356. }
  357. STDMETHODIMP
  358. CCplView::RefreshIDs(
  359. IEnumIDList *penumIDs
  360. )
  361. {
  362. DBG_ENTER(FTF_CPANEL, "CCplView::RefreshIDs");
  363. ASSERT(NULL != m_pns);
  364. //
  365. // This will cause the namespace object to reset it's internal
  366. // list of item IDs. This results in a re-categorization of
  367. // applets such that all information returned by the namespace
  368. // will now reflect the new set of folder items.
  369. //
  370. HRESULT hr = m_pns->RefreshIDs(penumIDs);
  371. DBG_EXIT_HRES(FTF_CPANEL, "CCplView::RefreshIDs", hr);
  372. return THR(hr);
  373. }
  374. HRESULT
  375. CCplView::_Initialize(
  376. IEnumIDList *penumIDs,
  377. IUnknown *punkSite
  378. )
  379. {
  380. ASSERT(NULL == m_pns);
  381. ASSERT(NULL != penumIDs);
  382. HRESULT hr = E_OUTOFMEMORY;
  383. //
  384. // We use this weak-reference implementation of IServiceProvider
  385. // as described by ZekeL in shell\inc\cowsite.h. A strong reference
  386. // would create a circular reference cycle between children of the
  387. // view and the view itself, preventing the view's destruction.
  388. // This weak-reference implementation is designed specifically
  389. // for this case.
  390. //
  391. ASSERT(NULL == m_psss);
  392. m_psss = new CSafeServiceSite();
  393. if (NULL != m_psss)
  394. {
  395. hr = m_psss->SetProviderWeakRef(this);
  396. if (SUCCEEDED(hr))
  397. {
  398. hr = CplNamespace_CreateInstance(penumIDs, CPL::IID_ICplNamespace, (void **)&m_pns);
  399. if (SUCCEEDED(hr))
  400. {
  401. IUnknown *punkSafeSite;
  402. hr = m_psss->QueryInterface(IID_IUnknown, (void **)&punkSafeSite);
  403. if (SUCCEEDED(hr))
  404. {
  405. //
  406. // Site the namespace object to the view.
  407. // By doing this, all command objects created by the namespace will
  408. // QueryService on the view object. If the view doesn't support
  409. // the requested service, it will query it's site.
  410. // We use this so that command objects can query the view for
  411. // IID_ICplNamespace and gather information on the namespace
  412. // if necessary.
  413. //
  414. hr = IUnknown_SetSite(m_pns, punkSafeSite);
  415. if (SUCCEEDED(hr))
  416. {
  417. hr = _AddOrDeleteAtoms(true);
  418. }
  419. punkSafeSite->Release();
  420. }
  421. }
  422. }
  423. }
  424. return THR(hr);
  425. }
  426. HRESULT
  427. CCplView::_AddOrDeleteAtoms(
  428. bool bAdd
  429. )
  430. {
  431. struct CPL::ATOMINFO rgAtomInfo[] = {
  432. { L"directive", &m_idDirective },
  433. { L"directive2", &m_idDirective2 },
  434. { L"title", &m_idTitle },
  435. { L"icon", &m_idIcon },
  436. { L"categorylist", &m_idCategoryList },
  437. { L"categorytasklist", &m_idCategoryTaskList },
  438. { L"appletlist", &m_idAppletList },
  439. { L"banner", &m_idBanner },
  440. { L"barricadetitle", &m_idBarricadeTitle },
  441. { L"barricademsg", &m_idBarricadeMsg },
  442. { L"container", &m_idContainer }
  443. };
  444. HRESULT hr = Dui_AddOrDeleteAtoms(rgAtomInfo, ARRAYSIZE(rgAtomInfo), bAdd);
  445. return THR(hr);
  446. }
  447. //
  448. // Creates the DUI element tree for the category 'choice' page.
  449. // Returns the root element.
  450. //
  451. HRESULT
  452. CCplView::_CreateCategoryChoiceElement(
  453. DUI::Element **ppe
  454. )
  455. {
  456. DBG_ENTER(FTF_CPANEL, "CCplView::_CreateCategoryChoiceElement");
  457. ASSERT(NULL != ppe);
  458. ASSERT(!IsBadWritePtr(ppe, sizeof(*ppe)));
  459. ASSERT(NULL != m_pns);
  460. DUI::Element *peRoot = NULL;
  461. DUI::Parser *pParser;
  462. HRESULT hr = _CreateUiFileParser(&pParser);
  463. if (SUCCEEDED(hr))
  464. {
  465. hr = Dui_CreateElement(pParser, L"CategoryList", NULL, &peRoot);
  466. if (SUCCEEDED(hr))
  467. {
  468. hr = _CreateWatermark(peRoot);
  469. if (SUCCEEDED(hr))
  470. {
  471. CDuiValuePtr pvSsCategoryListItem;
  472. hr = Dui_GetStyleSheet(pParser, L"CategoryListItemSS", &pvSsCategoryListItem);
  473. if (SUCCEEDED(hr))
  474. {
  475. //
  476. // Set the "Pick a category..." title.
  477. //
  478. hr = Dui_SetDescendentElementText(peRoot,
  479. L"directive",
  480. MAKEINTRESOURCEW(IDS_CP_PICKCATEGORY));
  481. if (SUCCEEDED(hr))
  482. {
  483. //
  484. // Build the list of categories.
  485. //
  486. DUI::Element *peCategoryList;
  487. hr = Dui_FindDescendent(peRoot, L"categorylist", &peCategoryList);
  488. if (SUCCEEDED(hr))
  489. {
  490. for (int i = 0; SUCCEEDED(hr) && i < int(eCPCAT_NUMCATEGORIES); i++)
  491. {
  492. ICplCategory *pCategory;
  493. hr = m_pns->GetCategory(_DisplayIndexToCategoryIndex(i), &pCategory);
  494. if (SUCCEEDED(hr))
  495. {
  496. if (S_OK == _IncludeCategory(pCategory))
  497. {
  498. IUICommand *puic;
  499. hr = pCategory->GetUiCommand(&puic);
  500. if (SUCCEEDED(hr))
  501. {
  502. hr = _CreateAndAddListItem(pParser,
  503. peCategoryList,
  504. L"CategoryLink",
  505. pvSsCategoryListItem,
  506. puic,
  507. eCPIMGSIZE_CATEGORY);
  508. ATOMICRELEASE(puic);
  509. }
  510. }
  511. ATOMICRELEASE(pCategory);
  512. }
  513. }
  514. }
  515. }
  516. }
  517. }
  518. }
  519. pParser->Destroy();
  520. }
  521. *ppe = peRoot;
  522. DBG_EXIT_HRES(FTF_CPANEL, "CCplView::_CreateCategoryChoiceElement", hr);
  523. return THR(hr);
  524. }
  525. //
  526. // Creates the DUI element tree for a given category page.
  527. // Returns the root element.
  528. //
  529. HRESULT
  530. CCplView::_CreateCategoryElement(
  531. ICplCategory *pCategory,
  532. DUI::Element **ppe
  533. )
  534. {
  535. DBG_ENTER(FTF_CPANEL, "CCplView::_CreateCategoryElement");
  536. ASSERT(NULL != pCategory);
  537. ASSERT(NULL != ppe);
  538. ASSERT(!IsBadWritePtr(ppe, sizeof(*ppe)));
  539. ASSERT(NULL != m_pns);
  540. DUI::Element *peRoot = NULL;
  541. DUI::Parser *pParser;
  542. HRESULT hr = _CreateUiFileParser(&pParser);
  543. if (SUCCEEDED(hr))
  544. {
  545. hr = Dui_CreateElement(pParser, L"CategoryView", NULL, &peRoot);
  546. if (SUCCEEDED(hr))
  547. {
  548. hr = _CreateWatermark(peRoot);
  549. if (SUCCEEDED(hr))
  550. {
  551. int cTasks = 0;
  552. int cApplets = 0;
  553. hr = _BuildCategoryBanner(pCategory, peRoot);
  554. if (SUCCEEDED(hr))
  555. {
  556. hr = _BuildCategoryTaskList(pParser, pCategory, peRoot, &cTasks);
  557. if (SUCCEEDED(hr))
  558. {
  559. hr = _BuildCategoryAppletList(pParser, pCategory, peRoot, &cApplets);
  560. }
  561. }
  562. if (SUCCEEDED(hr))
  563. {
  564. if (0 == cTasks && 0 == cApplets)
  565. {
  566. //
  567. // No tasks or applets. Display a message explaining
  568. // that the content has been made unavailable by the system
  569. // administrator.
  570. //
  571. hr = _BuildCategoryBarricade(peRoot);
  572. }
  573. else
  574. {
  575. //
  576. // Delete the barricade DUI elements. They're unused.
  577. //
  578. THR(Dui_DestroyDescendentElement(peRoot, L"barricadetitle"));
  579. THR(Dui_DestroyDescendentElement(peRoot, L"barricademsg"));
  580. //
  581. // Set the text in the 'directive' text elements.
  582. //
  583. if (0 < cTasks)
  584. {
  585. //
  586. // We've displayed a list of tasks.
  587. // Set the "Pick a task..." title.
  588. //
  589. hr = Dui_SetDescendentElementText(peRoot,
  590. L"directive",
  591. MAKEINTRESOURCEW(IDS_CP_PICKTASK));
  592. }
  593. if (SUCCEEDED(hr))
  594. {
  595. if (0 < cApplets)
  596. {
  597. //
  598. // We've displayed a list of applets. Display one of the
  599. // following directives based on the existance of a task
  600. // list above.
  601. //
  602. // Task list? Directive
  603. // ------------- ---------------------------
  604. // Yes "or pick a Control Panel icon"
  605. // No "Pick a Control Panel icon"
  606. //
  607. UINT idsDirective2 = IDS_CP_PICKICON;
  608. if (0 < cTasks)
  609. {
  610. idsDirective2 = IDS_CP_ORPICKICON;
  611. }
  612. hr = Dui_SetDescendentElementText(peRoot,
  613. L"directive2",
  614. MAKEINTRESOURCEW(idsDirective2));
  615. }
  616. }
  617. }
  618. }
  619. }
  620. }
  621. pParser->Destroy();
  622. }
  623. *ppe = peRoot;
  624. DBG_EXIT_HRES(FTF_CPANEL, "CCplView::_CreateCategoryElement", hr);
  625. return THR(hr);
  626. }
  627. //
  628. // Builds the 'barricade' that is displayed when a category has no
  629. // tasks or CPL applets to show.
  630. //
  631. HRESULT
  632. CCplView::_BuildCategoryBarricade(
  633. DUI::Element *peRoot
  634. )
  635. {
  636. DBG_ENTER(FTF_CPANEL, "CCplView::_BuildCategoryBarricade");
  637. HRESULT hr = Dui_SetDescendentElementText(peRoot,
  638. L"barricadetitle",
  639. MAKEINTRESOURCE(IDS_CP_CATEGORY_BARRICADE_TITLE));
  640. if (SUCCEEDED(hr))
  641. {
  642. hr = Dui_SetDescendentElementText(peRoot,
  643. L"barricademsg",
  644. MAKEINTRESOURCE(IDS_CP_CATEGORY_BARRICADE_MSG));
  645. }
  646. DBG_EXIT_HRES(FTF_CPANEL, "CCplView::_BuildCategoryBarricade", hr);
  647. return THR(hr);
  648. }
  649. //
  650. // Add the background watermark to the view if user is using a non-classic
  651. // theme.
  652. //
  653. HRESULT
  654. CCplView::_CreateWatermark(
  655. DUI::Element *peRoot
  656. )
  657. {
  658. DBG_ENTER(FTF_CPANEL, "CCplView::_CreateWatermark");
  659. ASSERT(NULL != peRoot);
  660. HINSTANCE hStyleModule;
  661. UINT idStyle;
  662. HRESULT hr = _GetStyleModuleAndResId(&hStyleModule, &idStyle);
  663. if (SUCCEEDED(hr))
  664. {
  665. HBITMAP hWatermark = (HBITMAP) LoadImage (hStyleModule, MAKEINTRESOURCE(IDB_CPANEL_WATERMARK),
  666. IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
  667. if (NULL != hWatermark)
  668. {
  669. //
  670. // Set watermark only on non-classic themes.
  671. //
  672. DUI::Element *peWatermark;
  673. hr = Dui_FindDescendent(peRoot, L"watermark", &peWatermark);
  674. if (SUCCEEDED(hr))
  675. {
  676. CDuiValuePtr ptrValue = DUI::Value::CreateGraphic(hWatermark,
  677. GRAPHIC_TransColor,
  678. 255);
  679. if (!ptrValue.IsNULL())
  680. {
  681. hr = Dui_SetElementProperty(peWatermark, ContentProp, ptrValue);
  682. peWatermark->SetContentAlign(CA_BottomRight);
  683. }
  684. else
  685. {
  686. hr = E_OUTOFMEMORY;
  687. DeleteObject (hWatermark);
  688. }
  689. }
  690. else
  691. {
  692. DeleteObject (hWatermark);
  693. }
  694. FreeLibrary(hStyleModule);
  695. }
  696. else
  697. {
  698. //
  699. // If 'classic' theme, do nothing.
  700. //
  701. hr = S_FALSE;
  702. }
  703. }
  704. DBG_EXIT_HRES(FTF_CPANEL, "CCplView::_CreateWatermark", hr);
  705. return THR(hr);
  706. }
  707. //
  708. // Builds the banner for a category page.
  709. //
  710. HRESULT
  711. CCplView::_BuildCategoryBanner(
  712. ICplCategory *pCategory,
  713. DUI::Element *pePrimaryPane
  714. )
  715. {
  716. DBG_ENTER(FTF_CPANEL, "CCplView::_BuildCategoryBanner");
  717. ASSERT(NULL != pCategory);
  718. ASSERT(NULL != pePrimaryPane);
  719. IUICommand *puic;
  720. HRESULT hr = pCategory->GetUiCommand(&puic);
  721. if (SUCCEEDED(hr))
  722. {
  723. ICpUiElementInfo *pei;
  724. hr = puic->QueryInterface(IID_ICpUiElementInfo, (void **)&pei);
  725. if (SUCCEEDED(hr))
  726. {
  727. DUI::Element *peBanner;
  728. hr = Dui_FindDescendent(pePrimaryPane, L"banner", &peBanner);
  729. if (SUCCEEDED(hr))
  730. {
  731. //
  732. // Create the title text.
  733. //
  734. LPWSTR pszTitle;
  735. hr = pei->LoadName(&pszTitle);
  736. if (SUCCEEDED(hr))
  737. {
  738. hr = Dui_SetDescendentElementText(peBanner, L"title", pszTitle);
  739. CoTaskMemFree(pszTitle);
  740. }
  741. if (SUCCEEDED(hr))
  742. {
  743. //
  744. // Create the icon.
  745. //
  746. HICON hIcon;
  747. hr = pei->LoadIcon(eCPIMGSIZE_BANNER, &hIcon);
  748. if (SUCCEEDED(hr))
  749. {
  750. hr = Dui_SetDescendentElementIcon(peBanner, L"icon", hIcon);
  751. if (FAILED(hr))
  752. {
  753. DestroyIcon(hIcon);
  754. }
  755. }
  756. }
  757. }
  758. ATOMICRELEASE(pei);
  759. }
  760. ATOMICRELEASE(puic);
  761. }
  762. DBG_EXIT_HRES(FTF_CPANEL, "CCplView::_BuildCategoryBanner", hr);
  763. return THR(hr);
  764. }
  765. //
  766. // Builds the list of tasks for a category page.
  767. //
  768. HRESULT
  769. CCplView::_BuildCategoryTaskList(
  770. DUI::Parser *pParser,
  771. ICplCategory *pCategory,
  772. DUI::Element *pePrimaryPane,
  773. int *pcTasks
  774. )
  775. {
  776. DBG_ENTER(FTF_CPANEL, "CCplView::_BuildCategoryTaskList");
  777. ASSERT(NULL != pCategory);
  778. ASSERT(NULL != pePrimaryPane);
  779. ASSERT(NULL != m_pns);
  780. ASSERT(NULL != pParser);
  781. int cTasks = 0;
  782. DUI::Element *peCategoryTaskList;
  783. HRESULT hr = Dui_FindDescendent(pePrimaryPane, L"categorytasklist", &peCategoryTaskList);
  784. if (SUCCEEDED(hr))
  785. {
  786. CDuiValuePtr pvStyleSheet;
  787. hr = Dui_GetStyleSheet(pParser, L"CategoryTaskListItemSS", &pvStyleSheet);
  788. if (SUCCEEDED(hr))
  789. {
  790. IEnumUICommand *peuic;
  791. hr = pCategory->EnumTasks(&peuic);
  792. if (SUCCEEDED(hr))
  793. {
  794. IUICommand *puic;
  795. while(S_OK == (hr = peuic->Next(1, &puic, NULL)))
  796. {
  797. hr = _CreateAndAddListItem(pParser,
  798. peCategoryTaskList,
  799. L"TaskLink",
  800. pvStyleSheet,
  801. puic,
  802. eCPIMGSIZE_TASK);
  803. if (SUCCEEDED(hr))
  804. {
  805. cTasks++;
  806. }
  807. ATOMICRELEASE(puic);
  808. }
  809. ATOMICRELEASE(peuic);
  810. }
  811. }
  812. }
  813. if (NULL != pcTasks)
  814. {
  815. ASSERT(!IsBadWritePtr(pcTasks, sizeof(*pcTasks)));
  816. *pcTasks = cTasks;
  817. }
  818. DBG_EXIT_HRES(FTF_CPANEL, "CCplView::_BuildCategoryTaskList", hr);
  819. return THR(hr);
  820. }
  821. //
  822. // Builds the list of CPL applets for a category page.
  823. //
  824. HRESULT
  825. CCplView::_BuildCategoryAppletList(
  826. DUI::Parser *pParser,
  827. ICplCategory *pCategory,
  828. DUI::Element *pePrimaryPane,
  829. int *pcApplets
  830. )
  831. {
  832. DBG_ENTER(FTF_CPANEL, "CCplView::_BuildCategoryAppletList");
  833. ASSERT(NULL != pCategory);
  834. ASSERT(NULL != pePrimaryPane);
  835. ASSERT(NULL != pParser);
  836. int cApplets = 0;
  837. DUI::Element *peAppletList;
  838. HRESULT hr = Dui_FindDescendent(pePrimaryPane, L"appletlist", &peAppletList);
  839. if (SUCCEEDED(hr))
  840. {
  841. CDuiValuePtr pvStyleSheet;
  842. hr = Dui_GetStyleSheet(pParser, L"CategoryTaskListItemSS", &pvStyleSheet);
  843. if (SUCCEEDED(hr))
  844. {
  845. IEnumUICommand *peuicApplets;
  846. hr = pCategory->EnumCplApplets(&peuicApplets);
  847. if (SUCCEEDED(hr))
  848. {
  849. IUICommand *puicApplet;
  850. while(S_OK == (hr = peuicApplets->Next(1, &puicApplet, NULL)))
  851. {
  852. hr = _CreateAndAddListItem(pParser,
  853. peAppletList,
  854. L"AppletLink",
  855. pvStyleSheet,
  856. puicApplet,
  857. eCPIMGSIZE_APPLET);
  858. if (SUCCEEDED(hr))
  859. {
  860. cApplets++;
  861. }
  862. ATOMICRELEASE(puicApplet);
  863. }
  864. ATOMICRELEASE(peuicApplets);
  865. }
  866. }
  867. }
  868. if (NULL != pcApplets)
  869. {
  870. ASSERT(!IsBadWritePtr(pcApplets, sizeof(*pcApplets)));
  871. *pcApplets = cApplets;
  872. }
  873. DBG_EXIT_HRES(FTF_CPANEL, "CCplView::_BuildCategoryAppletList", hr);
  874. return THR(hr);
  875. }
  876. //
  877. // Helper for adding link element to the view.
  878. //
  879. HRESULT
  880. CCplView::_CreateAndAddListItem(
  881. DUI::Parser *pParser,
  882. DUI::Element *peList, // List inserting into.
  883. LPCWSTR pszItemTemplate, // Name of template in UI file.
  884. DUI::Value *pvSsListItem, // Style sheet for new list item
  885. IUICommand *puic, // The new item's link object.
  886. eCPIMGSIZE eIconSize // Desired size of item icon.
  887. )
  888. {
  889. DBG_ENTER(FTF_CPANEL, "CCplView::_CreateAndAddListItem");
  890. ASSERT(NULL != pParser);
  891. ASSERT(NULL != peList);
  892. ASSERT(NULL != pvSsListItem);
  893. ASSERT(NULL != puic);
  894. ASSERT(NULL != pszItemTemplate);
  895. DUI::Element *peListItem;
  896. HRESULT hr = Dui_CreateElement(pParser, pszItemTemplate, NULL, &peListItem);
  897. if (SUCCEEDED(hr))
  898. {
  899. if (NULL != pvSsListItem)
  900. {
  901. hr = Dui_SetElementStyleSheet(peListItem, pvSsListItem);
  902. }
  903. if (SUCCEEDED(hr))
  904. {
  905. ASSERTMSG(peListItem->GetClassInfo() == CLinkElement::Class, "CCplView::_CreateAndAddListItem didn't get a CLinkElement::Class object (%s)", peListItem->GetClassInfo()->GetName());
  906. CLinkElement *pLinkEle = static_cast<CLinkElement *>(peListItem);
  907. hr = pLinkEle->Initialize(puic, eIconSize);
  908. if (SUCCEEDED(hr))
  909. {
  910. if (SUCCEEDED(hr))
  911. {
  912. hr = peList->Add(peListItem);
  913. if (SUCCEEDED(hr))
  914. {
  915. peListItem = NULL;
  916. }
  917. }
  918. }
  919. if (NULL != peListItem)
  920. {
  921. peListItem->Destroy();
  922. }
  923. }
  924. }
  925. DBG_EXIT_HRES(FTF_CPANEL, "CCplView::_CreateAndAddListItem", hr);
  926. return THR(hr);
  927. }
  928. //
  929. // Determine if a given category item should be shown in the UI.
  930. //
  931. // Returns:
  932. // S_OK - Include the item.
  933. // S_FALSE - Do not include the item.
  934. // Error - Cannot determine.
  935. //
  936. HRESULT
  937. CCplView::_IncludeCategory(
  938. ICplCategory *pCategory
  939. ) const
  940. {
  941. HRESULT hr = S_OK; // Assume it's included.
  942. //
  943. // If a category link invokes a restricted operation,
  944. // hide it from the UI.
  945. //
  946. IUICommand *puic;
  947. hr = pCategory->GetUiCommand(&puic);
  948. if (SUCCEEDED(hr))
  949. {
  950. UISTATE uis;
  951. hr = puic->get_State(NULL, TRUE, &uis);
  952. if (SUCCEEDED(hr))
  953. {
  954. if (UIS_HIDDEN == uis)
  955. {
  956. hr = S_FALSE;
  957. }
  958. }
  959. ATOMICRELEASE(puic);
  960. }
  961. return THR(hr);
  962. }
  963. //
  964. // Map a category display index to a category index in the
  965. // namespace. Categories in the namespace are ordered to match up
  966. // with the various category IDs. The view may be (and is) ordered
  967. // differently and is subject to change based on usability feedback.
  968. //
  969. eCPCAT
  970. CCplView::_DisplayIndexToCategoryIndex(
  971. int iCategory
  972. ) const
  973. {
  974. //
  975. // This array determins the order the categories are displayed
  976. // in the category selection view. To change the display order,
  977. // simply reorder these entries.
  978. //
  979. static const eCPCAT rgMap[] = { // Position in DUI grid control
  980. eCPCAT_APPEARANCE, // Row 0, Col 0
  981. eCPCAT_HARDWARE, // Row 0, Col 1
  982. eCPCAT_NETWORK, // Row 1, Col 0
  983. eCPCAT_ACCOUNTS, // Row 1, Col 1
  984. eCPCAT_ARP, // Row 2, Col 0
  985. eCPCAT_REGIONAL, // Row 2, Col 1
  986. eCPCAT_SOUND, // Row 3, Col 0
  987. eCPCAT_ACCESSIBILITY, // Row 3, Col 1
  988. eCPCAT_PERFMAINT, // Row 4, Col 0
  989. eCPCAT_OTHER // Row 4, Col 1
  990. };
  991. ASSERT(ARRAYSIZE(rgMap) == eCPCAT_NUMCATEGORIES);
  992. ASSERT(iCategory >= 0 && iCategory < ARRAYSIZE(rgMap));
  993. return rgMap[iCategory];
  994. }
  995. HRESULT
  996. CCplView::_CreateUiFileParser(
  997. DUI::Parser **ppParser
  998. )
  999. {
  1000. DBG_ENTER(FTF_CPANEL, "CCplView::_CreateUiFileParser");
  1001. ASSERT(NULL != ppParser);
  1002. ASSERT(!IsBadWritePtr(ppParser, sizeof(*ppParser)));
  1003. char *pszUiFile;
  1004. int cchUiFile;
  1005. HINSTANCE hInstance; // Instance containing resources referenced in UI file.
  1006. HRESULT hr = _BuildUiFile(&pszUiFile, &cchUiFile, &hInstance);
  1007. if (SUCCEEDED(hr))
  1008. {
  1009. hr = Dui_CreateParser(pszUiFile, cchUiFile, hInstance, ppParser);
  1010. LocalFree(pszUiFile);
  1011. if (HINST_THISDLL != hInstance)
  1012. {
  1013. ASSERT(NULL != hInstance);
  1014. FreeLibrary(hInstance);
  1015. }
  1016. }
  1017. DBG_EXIT(FTF_CPANEL, "CCplView::_CreateUiFileParser");
  1018. return THR(hr);
  1019. }
  1020. //
  1021. // Builds the UI file for this view from the
  1022. // appropriate base template + style sheet
  1023. //
  1024. // pUIFile receives a pointer to the ui file in memory
  1025. // piCharCount receives the size of the file
  1026. //
  1027. HRESULT
  1028. CCplView::_BuildUiFile(
  1029. char **ppUIFile,
  1030. int *piCharCount,
  1031. HINSTANCE *phInstance
  1032. )
  1033. {
  1034. DBG_ENTER(FTF_CPANEL, "CCplView::_BuildUiFile");
  1035. ASSERT(NULL != ppUIFile);
  1036. ASSERT(!IsBadWritePtr(ppUIFile, sizeof(*ppUIFile)));
  1037. ASSERT(NULL != phInstance);
  1038. ASSERT(!IsBadWritePtr(phInstance, sizeof(*phInstance)));
  1039. *phInstance = NULL;
  1040. //
  1041. // Load the 'structure' UI file
  1042. //
  1043. char *pStructure;
  1044. HRESULT hr = _LoadUiFileFromResources(HINST_THISDLL, IDR_DUI_CPVIEW, &pStructure);
  1045. if (SUCCEEDED(hr))
  1046. {
  1047. HINSTANCE hStyleModule;
  1048. UINT idStyle;
  1049. hr = _GetStyleModuleAndResId(&hStyleModule, &idStyle);
  1050. if (SUCCEEDED(hr))
  1051. {
  1052. //
  1053. // Load the style sheet. First, check if the current theme has a style sheet,
  1054. // if not, use the default style sheet in the resources.
  1055. //
  1056. char *pStyle;
  1057. hr = _LoadUiFileFromResources(hStyleModule, idStyle, &pStyle);
  1058. if (SUCCEEDED(hr))
  1059. {
  1060. const int cbStyle = lstrlenA(pStyle);
  1061. const int cbStructure = lstrlenA(pStructure);
  1062. char *pResult = (char *)LocalAlloc(LPTR, cbStyle + cbStructure + 1);
  1063. if (pResult)
  1064. {
  1065. //
  1066. // Put the resouces together (style + structure)
  1067. //
  1068. CopyMemory(pResult, pStyle, cbStyle);
  1069. CopyMemory(pResult + cbStyle, pStructure, cbStructure);
  1070. ASSERT(cbStructure + cbStyle == lstrlenA(pResult));
  1071. *ppUIFile = pResult;
  1072. //
  1073. // This count is ANSI chars so we can use byte counts
  1074. // directly.
  1075. //
  1076. *piCharCount = cbStructure + cbStyle;
  1077. *phInstance = hStyleModule;
  1078. //
  1079. // Indicate that HINSTANCE is being returned to caller.
  1080. //
  1081. hStyleModule = NULL;
  1082. }
  1083. else
  1084. {
  1085. hr = E_OUTOFMEMORY;
  1086. }
  1087. LocalFree(pStyle);
  1088. }
  1089. if (NULL != hStyleModule && HINST_THISDLL != hStyleModule)
  1090. {
  1091. //
  1092. // Something failed. Need to free style module
  1093. // if it's not shell32.dll.
  1094. //
  1095. FreeLibrary(hStyleModule);
  1096. }
  1097. }
  1098. LocalFree(pStructure);
  1099. }
  1100. DBG_EXIT_HRES(FTF_CPANEL, "CCplView::_BuildUiFile", hr);
  1101. return THR(hr);
  1102. }
  1103. HRESULT
  1104. CCplView::_GetStyleModuleAndResId(
  1105. HINSTANCE *phInstance,
  1106. UINT *pidStyle
  1107. )
  1108. {
  1109. DBG_ENTER(FTF_CPANEL, "CCplView::_GetStyleModuleAndResId");
  1110. ASSERT(NULL != phInstance);
  1111. ASSERT(!IsBadWritePtr(phInstance, sizeof(*phInstance)));
  1112. ASSERT(NULL != pidStyle);
  1113. ASSERT(!IsBadWritePtr(pidStyle, sizeof(*pidStyle)));
  1114. HRESULT hr = S_OK;
  1115. *phInstance = NULL;
  1116. HINSTANCE hThemeModule = SHGetShellStyleHInstance();
  1117. if (NULL != hThemeModule)
  1118. {
  1119. *pidStyle = IDR_DUI_CPSTYLE;
  1120. *phInstance = hThemeModule;
  1121. }
  1122. else
  1123. {
  1124. TraceMsg(TF_CPANEL, "Error %d loading theme file", GetLastError());
  1125. hr = ResultFromLastError();
  1126. }
  1127. DBG_EXIT_HRES(FTF_CPANEL, "CCplView::_GetStyleModuleAndResId", hr);
  1128. return THR(hr);
  1129. }
  1130. //
  1131. // Loads the requested UI file from a module's resources.
  1132. //
  1133. // iID - UI file id
  1134. // pUIFile - receives a pointer to the UI file
  1135. //
  1136. HRESULT
  1137. CCplView::_LoadUiFileFromResources(
  1138. HINSTANCE hInstance,
  1139. int idResource,
  1140. char **ppUIFile
  1141. )
  1142. {
  1143. DBG_ENTER(FTF_CPANEL, "CCplView::_LoadUiFileFromResources");
  1144. ASSERT(NULL != ppUIFile);
  1145. ASSERT(!IsBadWritePtr(ppUIFile, sizeof(*ppUIFile)));
  1146. HRESULT hr = E_FAIL;
  1147. *ppUIFile = NULL;
  1148. HRSRC hFile = FindResourceW(hInstance, MAKEINTRESOURCEW(idResource), L"UIFILE");
  1149. if (hFile)
  1150. {
  1151. HGLOBAL hResource = LoadResource(hInstance, hFile);
  1152. if (hResource)
  1153. {
  1154. char *pFile = (char *)LockResource(hResource);
  1155. if (pFile)
  1156. {
  1157. DWORD dwSize = SizeofResource(hInstance, hFile);
  1158. //
  1159. // Include one extra byte for a terminating nul character.
  1160. // We're loading text and want it to be nul-terminated.
  1161. //
  1162. *ppUIFile = (char *)LocalAlloc(LPTR, dwSize + 1);
  1163. if (NULL != *ppUIFile)
  1164. {
  1165. CopyMemory(*ppUIFile, pFile, dwSize);
  1166. hr = S_OK;
  1167. }
  1168. else
  1169. {
  1170. hr = E_OUTOFMEMORY;
  1171. }
  1172. }
  1173. else
  1174. {
  1175. hr = ResultFromLastError();
  1176. }
  1177. }
  1178. else
  1179. {
  1180. hr = ResultFromLastError();
  1181. }
  1182. }
  1183. else
  1184. {
  1185. hr = ResultFromLastError();
  1186. }
  1187. DBG_EXIT_HRES(FTF_CPANEL, "CCplView::_LoadUiFileFromResources", hr);
  1188. return THR(hr);
  1189. }
  1190. HRESULT
  1191. CPL::CplView_CreateInstance(
  1192. IEnumIDList *penumIDs,
  1193. IUnknown *punkSite,
  1194. REFIID riid,
  1195. void **ppvOut
  1196. )
  1197. {
  1198. HRESULT hr = CCplView::CreateInstance(penumIDs, punkSite, riid, ppvOut);
  1199. return THR(hr);
  1200. }
  1201. HRESULT
  1202. CplView_GetCategoryTitle(
  1203. eCPCAT eCategory,
  1204. LPWSTR pszTitle,
  1205. UINT cchTitle
  1206. )
  1207. {
  1208. ASSERT(NULL != pszTitle);
  1209. ASSERT(!IsBadWritePtr(pszTitle, cchTitle * sizeof(*pszTitle)));
  1210. //
  1211. // These must remain in the same order as the eCPCAT_XXXXX enumeration.
  1212. //
  1213. static const UINT rgid[] = {
  1214. IDS_CPCAT_OTHERCPLS_TITLE,
  1215. IDS_CPCAT_APPEARANCE_TITLE,
  1216. IDS_CPCAT_HARDWARE_TITLE,
  1217. IDS_CPCAT_NETWORK_TITLE,
  1218. IDS_CPCAT_SOUNDS_TITLE,
  1219. IDS_CPCAT_PERFMAINT_TITLE,
  1220. IDS_CPCAT_REGIONAL_TITLE,
  1221. IDS_CPCAT_ACCESSIBILITY_TITLE,
  1222. IDS_CPCAT_ARP_TITLE,
  1223. IDS_CPCAT_ACCOUNTS_TITLE
  1224. };
  1225. HRESULT hr = S_OK;
  1226. ASSERT(eCategory >= 0 && eCategory < eCPCAT_NUMCATEGORIES);
  1227. if (0 == LoadString(HINST_THISDLL, rgid[int(eCategory)], pszTitle, cchTitle))
  1228. {
  1229. hr = ResultFromLastError();
  1230. }
  1231. return THR(hr);
  1232. }
  1233. } // namespace CPL
  1234. HRESULT InitializeCPClasses()
  1235. {
  1236. HRESULT hr;
  1237. hr = CPL::CLinkElement::Register();
  1238. if (FAILED(hr))
  1239. goto Failure;
  1240. return S_OK;
  1241. Failure:
  1242. return hr;
  1243. }