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.

2861 lines
92 KiB

  1. #include "shellprv.h"
  2. #include "caggunk.h"
  3. #include "views.h"
  4. #include "ids.h"
  5. #include "shitemid.h"
  6. #include "datautil.h"
  7. #include "clsobj.h"
  8. #include "control.h"
  9. #include "drives.h"
  10. #include "infotip.h"
  11. #include "prop.h" // COLUMN_INFO
  12. #include "basefvcb.h"
  13. #include "fstreex.h"
  14. #include "idhidden.h"
  15. #include "shstyle.h"
  16. #include "util.h" // GetVariantFromRegistryValue
  17. #define GADGET_ENABLE_TRANSITIONS
  18. #define GADGET_ENABLE_CONTROLS
  19. #define GADGET_ENABLE_OLE
  20. #include <duser.h>
  21. #include <directui.h>
  22. #include <duserctrl.h>
  23. #include "cpview.h"
  24. #include "cputil.h"
  25. //
  26. // An array of pidls
  27. //
  28. typedef CPL::CDpa<UNALIGNED ITEMIDLIST, CPL::CDpaDestroyer_ILFree<UNALIGNED ITEMIDLIST> > CDpaItemIDList;
  29. #define MAX_CPL_EXEC_NAME (1 + MAX_PATH + 2 + MAX_CCH_CPLNAME) //See wnsprintf in GetExecName
  30. STDAPI_(BOOL) IsNameListedUnderKey(LPCTSTR pszFileName, LPCTSTR pszKey);
  31. #pragma pack(1)
  32. // our pidc type:
  33. typedef struct _IDCONTROL
  34. {
  35. USHORT cb;
  36. USHORT wDummy; // DONT REUSE - was stack garbage pre-XP
  37. int idIcon;
  38. USHORT oName; // cBuf[oName] is start of NAME
  39. USHORT oInfo; // cBuf[oInfo] is start of DESCRIPTION
  40. CHAR cBuf[MAX_PATH+MAX_CCH_CPLNAME+MAX_CCH_CPLINFO]; // cBuf[0] is the start of FILENAME
  41. } IDCONTROL;
  42. typedef UNALIGNED struct _IDCONTROL *LPIDCONTROL;
  43. typedef struct _IDCONTROLW
  44. {
  45. USHORT cb;
  46. USHORT wDummy; // DONT REUSE - was stack garbage pre-XP
  47. int idIcon;
  48. USHORT oName; // if Unicode .cpl, this will be 0
  49. USHORT oInfo; // if Unicode .cpl, this will be 0
  50. CHAR cBuf[2]; // if Unicode .cpl, cBuf[0] = '\0', cBuf[1] = magic byte
  51. USHORT wDummy2; // DONT REUSE - was stack garbage pre-XP
  52. DWORD dwFlags; // Unused; for future expansion
  53. USHORT oNameW; // cBufW[oNameW] is start of NAME
  54. USHORT oInfoW; // cBufW[oInfoW] is start of DESCRIPTION
  55. WCHAR cBufW[MAX_PATH+MAX_CCH_CPLNAME+MAX_CCH_CPLINFO]; // cBufW[0] is the start of FILENAME
  56. } IDCONTROLW;
  57. typedef UNALIGNED struct _IDCONTROLW *LPIDCONTROLW;
  58. #pragma pack()
  59. STDAPI ControlExtractIcon_CreateInstance(LPCTSTR pszSubObject, REFIID riid, void **ppv);
  60. class CControlPanelViewCallback;
  61. class CControlPanelFolder : public CAggregatedUnknown, public IShellFolder2, IPersistFolder2
  62. {
  63. friend CControlPanelViewCallback;
  64. public:
  65. // IUknown
  66. STDMETHODIMP QueryInterface(REFIID riid, void **ppv) { return CAggregatedUnknown::QueryInterface(riid, ppv); };
  67. STDMETHODIMP_(ULONG) AddRef(void) { return CAggregatedUnknown::AddRef(); };
  68. STDMETHODIMP_(ULONG) Release(void) { return CAggregatedUnknown::Release(); };
  69. // IShellFolder
  70. STDMETHODIMP ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR lpszDisplayName,
  71. ULONG* pchEaten, LPITEMIDLIST* ppidl, ULONG* pdwAttributes);
  72. STDMETHODIMP EnumObjects(HWND hwnd, DWORD grfFlags, LPENUMIDLIST* ppenumIDList);
  73. STDMETHODIMP BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void** ppv);
  74. STDMETHODIMP BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void** ppv);
  75. STDMETHODIMP CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
  76. STDMETHODIMP CreateViewObject (HWND hwndOwner, REFIID riid, void** ppv);
  77. STDMETHODIMP GetAttributesOf(UINT cidl, LPCITEMIDLIST* apidl, ULONG* rgfInOut);
  78. STDMETHODIMP GetUIObjectOf(HWND hwndOwner, UINT cidl, LPCITEMIDLIST* apidl,
  79. REFIID riid, UINT* prgfInOut, void** ppv);
  80. STDMETHODIMP GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, LPSTRRET lpName);
  81. STDMETHODIMP SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR lpszName, DWORD uFlags,
  82. LPITEMIDLIST* ppidlOut);
  83. // IShellFolder2
  84. STDMETHODIMP GetDefaultSearchGUID(GUID *pGuid);
  85. STDMETHODIMP EnumSearches(IEnumExtraSearch **ppenum);
  86. STDMETHODIMP GetDefaultColumn(DWORD dwRes, ULONG* pSort, ULONG* pDisplay);
  87. STDMETHODIMP GetDefaultColumnState(UINT iColumn, DWORD* pbState);
  88. STDMETHODIMP GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID* pscid, VARIANT* pv);
  89. STDMETHODIMP GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS* pDetails);
  90. STDMETHODIMP MapColumnToSCID(UINT iColumn, SHCOLUMNID* pscid);
  91. // IPersist
  92. STDMETHODIMP GetClassID(CLSID* pClassID);
  93. // IPersistFolder
  94. STDMETHODIMP Initialize(LPCITEMIDLIST pidl);
  95. // IPersistFolder2
  96. STDMETHODIMP GetCurFolder(LPITEMIDLIST* ppidl);
  97. protected:
  98. CControlPanelFolder(IUnknown* punkOuter);
  99. ~CControlPanelFolder();
  100. // used by the CAggregatedUnknown stuff
  101. HRESULT v_InternalQueryInterface(REFIID riid, void** ppv);
  102. static HRESULT GetExecName(LPIDCONTROL pidc, LPTSTR pszParseName, UINT cchParseName);
  103. static HRESULT GetModuleMapped(LPIDCONTROL pidc, LPTSTR pszModule, UINT cchModule,
  104. UINT* pidNewIcon, LPTSTR pszApplet, UINT cchApplet);
  105. static HRESULT GetDisplayName(LPIDCONTROL pidc, LPTSTR pszName, UINT cchName);
  106. static HRESULT GetModule(LPIDCONTROL pidc, LPTSTR pszModule, UINT cchModule);
  107. static HRESULT _GetDescription(LPIDCONTROL pidc, LPTSTR pszDesc, UINT cchDesc);
  108. static HRESULT _GetFullCPLName(LPIDCONTROL pidc, LPTSTR achKeyValName, UINT cchSize);
  109. HRESULT _GetExtPropRegValName(HKEY hkey, LPTSTR pszExpandedName, LPTSTR pszRegValName, UINT cch);
  110. static HRESULT _GetExtPropsKey(HKEY hkeyParent, HKEY * pHkey, const SHCOLUMNID * pscid);
  111. static LPIDCONTROL _IsValid(LPCITEMIDLIST pidl);
  112. static LPIDCONTROLW _IsUnicodeCPL(LPIDCONTROL pidc);
  113. LPCITEMIDLIST GetIDList() { return _pidl; }
  114. private:
  115. friend HRESULT CControlPanel_CreateInstance(IUnknown* punkOuter, REFIID riid, void** ppv);
  116. static HRESULT CALLBACK DFMCallBack(IShellFolder *psf, HWND hwndView,
  117. IDataObject *pdtobj, UINT uMsg,
  118. WPARAM wParam, LPARAM lParam);
  119. HRESULT _GetDisplayNameForSelf(DWORD dwFlags, STRRET* pstrret);
  120. LPITEMIDLIST _pidl;
  121. IUnknown* _punkReg;
  122. HDSA _hdsaExtPropRegVals; // Array of EPRV_CACHE_ENTRY.
  123. //
  124. // An entry in the Extended Property Reg Values cache.
  125. // This cache is used to minimize the amount of path 'normalization'
  126. // done when comparing CPL applet paths with the corresponding paths
  127. // stored for categorization.
  128. //
  129. struct EPRV_CACHE_ENTRY
  130. {
  131. LPTSTR pszRegValName;
  132. LPTSTR pszRegValNameNormalized;
  133. };
  134. DWORD _InitExtPropRegValNameCache(HKEY hkey);
  135. BOOL _LookupExtPropRegValName(HKEY hkey, LPTSTR pszSearchKeyNormalized, LPTSTR pszRegValName, UINT cch);
  136. DWORD _CacheExtPropRegValName(LPCTSTR pszRegValNameNormalized, LPCTSTR pszRegValName);
  137. static int CALLBACK _DestroyExtPropsRegValEntry(void *p, void *pData);
  138. static INT _FilterStackOverflow(INT nException);
  139. DWORD _NormalizeCplSpec(LPTSTR pszSpecIn, LPTSTR pszSpecOut, UINT cchSpecOut);
  140. DWORD _NormalizePath(LPCTSTR pszPathIn, LPTSTR pszPathOut, UINT cchPathOut);
  141. DWORD _NormalizePathWorker(LPCTSTR pszPathIn, LPTSTR pszPathOut, UINT cchPathOut);
  142. void _TrimSpaces(LPTSTR psz);
  143. };
  144. class CControlPanelEnum : public IEnumIDList
  145. {
  146. public:
  147. // IUnknown
  148. STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
  149. STDMETHODIMP_(ULONG) AddRef();
  150. STDMETHODIMP_(ULONG) Release();
  151. // IEnumIDList
  152. STDMETHODIMP Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched);
  153. STDMETHODIMP Skip(ULONG celt) { return E_NOTIMPL; };
  154. STDMETHODIMP Reset();
  155. STDMETHODIMP Clone(IEnumIDList **ppenum) { return E_NOTIMPL; };
  156. CControlPanelEnum(UINT uFlags);
  157. HRESULT Init();
  158. private:
  159. ~CControlPanelEnum();
  160. BOOL _DoesPolicyAllow(LPCTSTR pszName, LPCTSTR pszFileName);
  161. LONG _cRef;
  162. ULONG _uFlags;
  163. int _iModuleCur;
  164. int _cControlsOfCurrentModule;
  165. int _iControlCur;
  166. int _cControlsTotal;
  167. int _iRegControls;
  168. MINST _minstCur;
  169. ControlData _cplData;
  170. };
  171. //
  172. // This handler isn't defined in shlobjp.h.
  173. // The only reason I can see is that it references DUI::Element.
  174. //
  175. #define HANDLE_SFVM_GETWEBVIEWBARRICADE(pv, wP, lP, fn) \
  176. ((fn)((pv), (DUI::Element**)(lP)))
  177. class CControlPanelViewCallback : public CBaseShellFolderViewCB
  178. {
  179. public:
  180. CControlPanelViewCallback(CControlPanelFolder *pcpf)
  181. : _pcpf(pcpf),
  182. CBaseShellFolderViewCB(pcpf->GetIDList(), SHCNE_UPDATEITEM),
  183. _pCplView(NULL),
  184. _penumWvInfo(NULL)
  185. {
  186. TraceMsg(TF_LIFE, "CControlPanelViewCallback::CControlPanelViewCallback, this = 0x%x", this);
  187. _pcpf->AddRef();
  188. }
  189. STDMETHODIMP RealMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
  190. private:
  191. ~CControlPanelViewCallback()
  192. {
  193. TraceMsg(TF_LIFE, "CControlPanelViewCallback::~CControlPanelViewCallback, this = 0x%x", this);
  194. _pcpf->Release();
  195. ATOMICRELEASE(_penumWvInfo);
  196. ATOMICRELEASE(_pCplView);
  197. }
  198. HRESULT OnMergeMenu(DWORD pv, QCMINFO*lP)
  199. {
  200. return S_OK;
  201. }
  202. HRESULT OnSize(DWORD pv, UINT cx, UINT cy)
  203. {
  204. ResizeStatus(_punkSite, cx);
  205. return S_OK;
  206. }
  207. HRESULT OnGetPane(DWORD pv, LPARAM dwPaneID, DWORD *pdwPane)
  208. {
  209. if (PANE_ZONE == dwPaneID)
  210. *pdwPane = 2;
  211. return S_OK;
  212. }
  213. HRESULT _OnSFVMGetHelpTopic(DWORD pv, SFVM_HELPTOPIC_DATA * phtd);
  214. HRESULT _OnSFVMForceWebView(DWORD pv, PBOOL bForceWebView);
  215. HRESULT _OnSFVMGetWebViewLayout(DWORD pv, UINT uViewMode, SFVM_WEBVIEW_LAYOUT_DATA* pData);
  216. HRESULT _OnSFVMGetWebViewBarricade(DWORD pv, DUI::Element **ppe);
  217. HRESULT _OnSFVMGetWebViewContent(DWORD pv, SFVM_WEBVIEW_CONTENT_DATA *pData);
  218. HRESULT _OnSFVMEnumWebViewTasks(DWORD pv, SFVM_WEBVIEW_ENUMTASKSECTION_DATA *pData);
  219. HRESULT _OnSFVMWindowDestroy(DWORD pv, HWND hwnd);
  220. HRESULT _OnSFVMUpdateStatusBar(DWORD pv, BOOL bInitialize);
  221. HRESULT _GetCplView(CPL::ICplView **ppView, bool bInitialize = false);
  222. HRESULT _GetCplCategoryFromFolderIDList(CPL::eCPCAT *peCategory);
  223. HRESULT _GetWebViewInfoEnumerator(CPL::IEnumCplWebViewInfo **ppewvi);
  224. HRESULT _EnumFolderViewIDs(IEnumIDList **ppenumIDs);
  225. CControlPanelFolder *_pcpf;
  226. CPL::ICplView *_pCplView; // The 'categorized' view content
  227. CPL::IEnumCplWebViewInfo *_penumWvInfo;
  228. };
  229. STDMETHODIMP CControlPanelViewCallback::RealMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
  230. {
  231. switch (uMsg)
  232. {
  233. HANDLE_MSG(0, SFVM_MERGEMENU, OnMergeMenu);
  234. HANDLE_MSG(0, SFVM_ADDPROPERTYPAGES, SFVCB_OnAddPropertyPages);
  235. HANDLE_MSG(0, SFVM_SIZE, OnSize);
  236. HANDLE_MSG(0, SFVM_GETPANE, OnGetPane);
  237. HANDLE_MSG(0, SFVM_GETHELPTOPIC, _OnSFVMGetHelpTopic);
  238. HANDLE_MSG(0, SFVM_FORCEWEBVIEW, _OnSFVMForceWebView);
  239. HANDLE_MSG(0, SFVM_GETWEBVIEWLAYOUT, _OnSFVMGetWebViewLayout);
  240. HANDLE_MSG(0, SFVM_GETWEBVIEWBARRICADE, _OnSFVMGetWebViewBarricade);
  241. HANDLE_MSG(0, SFVM_GETWEBVIEWCONTENT, _OnSFVMGetWebViewContent);
  242. HANDLE_MSG(0, SFVM_ENUMWEBVIEWTASKS, _OnSFVMEnumWebViewTasks);
  243. HANDLE_MSG(0, SFVM_WINDOWDESTROY, _OnSFVMWindowDestroy);
  244. HANDLE_MSG(0, SFVM_UPDATESTATUSBAR, _OnSFVMUpdateStatusBar);
  245. default:
  246. return E_FAIL;
  247. }
  248. return S_OK;
  249. }
  250. HRESULT
  251. CControlPanelViewCallback::_OnSFVMGetHelpTopic(
  252. DWORD pv,
  253. SFVM_HELPTOPIC_DATA *phtd
  254. )
  255. {
  256. DBG_ENTER(FTF_CPANEL, "CControlPanelViewCallback::_OnSFVMGetHelpTopic");
  257. ASSERT(NULL != phtd);
  258. ASSERT(!IsBadWritePtr(phtd, sizeof(*phtd)));
  259. UNREFERENCED_PARAMETER(pv);
  260. HRESULT hr = E_FAIL;
  261. phtd->wszHelpFile[0] = L'\0';
  262. phtd->wszHelpTopic[0] = L'\0';
  263. if (IsOS(OS_ANYSERVER))
  264. {
  265. //
  266. // Server has a fixed help URL so we can simply
  267. // copy it.
  268. //
  269. hr = StringCchCopyW(phtd->wszHelpTopic,
  270. ARRAYSIZE(phtd->wszHelpTopic),
  271. L"hcp://services/centers/homepage");
  272. }
  273. else
  274. {
  275. if (CPL::CategoryViewIsActive(NULL))
  276. {
  277. //
  278. // Category view is active.
  279. // Retrieve help URL from the view object.
  280. //
  281. CPL::ICplView *pView;
  282. hr = _GetCplView(&pView);
  283. if (SUCCEEDED(hr))
  284. {
  285. CPL::eCPCAT eCategory;
  286. hr = _GetCplCategoryFromFolderIDList(&eCategory);
  287. if (S_OK == hr)
  288. {
  289. //
  290. // We're viewing a Control Panel category page.
  291. // Ask the view for the help URL for this category.
  292. //
  293. hr = pView->GetCategoryHelpURL(eCategory,
  294. phtd->wszHelpTopic,
  295. ARRAYSIZE(phtd->wszHelpTopic));
  296. }
  297. ATOMICRELEASE(pView);
  298. }
  299. }
  300. if (L'\0' == phtd->wszHelpTopic[0])
  301. {
  302. //
  303. // Either we're in 'classic' view, the 'category choice' page
  304. // or something failed above. Return the URL for the basic
  305. // Control Panel help.
  306. //
  307. hr = CPL::BuildHssHelpURL(NULL, phtd->wszHelpTopic, ARRAYSIZE(phtd->wszHelpTopic));
  308. }
  309. }
  310. DBG_EXIT_HRES(FTF_CPANEL, "CControlPanelViewCallback::_OnSFVMGetHelpTopic", hr);
  311. return THR(hr);
  312. }
  313. //
  314. // Defview sends SFVM_ENUMWEBVIEWTASKS repeatedly until
  315. // we set the SFVMWVF_NOMORETASKS flag in the data. With each call we
  316. // return data describing a single menu (caption and items) in the
  317. // webview pane.
  318. //
  319. HRESULT
  320. CControlPanelViewCallback::_OnSFVMEnumWebViewTasks(
  321. DWORD pv,
  322. SFVM_WEBVIEW_ENUMTASKSECTION_DATA *pData
  323. )
  324. {
  325. DBG_ENTER(FTF_CPANEL, "CControlPanelViewCallback::_OnSFVMEnumWebViewTasks");
  326. ASSERT(NULL != pData);
  327. ASSERT(!IsBadWritePtr(pData, sizeof(*pData)));
  328. UNREFERENCED_PARAMETER(pv);
  329. HRESULT hr = S_OK;
  330. if (NULL == _penumWvInfo)
  331. {
  332. hr = _GetWebViewInfoEnumerator(&_penumWvInfo);
  333. }
  334. if (SUCCEEDED(hr))
  335. {
  336. ASSERT(NULL != _penumWvInfo);
  337. CPL::ICplWebViewInfo *pwvi;
  338. hr = _penumWvInfo->Next(1, &pwvi, NULL);
  339. if (S_OK == hr)
  340. {
  341. ASSERT(NULL == pData->pHeader);
  342. hr = pwvi->get_Header(&(pData->pHeader));
  343. if (SUCCEEDED(hr))
  344. {
  345. ASSERT(NULL == pData->penumTasks);
  346. hr = pwvi->EnumTasks(&(pData->penumTasks));
  347. if (SUCCEEDED(hr))
  348. {
  349. DWORD dwStyle = 0;
  350. hr = pwvi->get_Style(&dwStyle);
  351. if (SUCCEEDED(hr))
  352. {
  353. //
  354. // ISSUE-2001/01/02-BrianAu Revisit this.
  355. // I don't like using an SFVMWVF_XXXXXX flag in the
  356. // dwStyle returned by get_Style. That style should
  357. // be defined independently of any SFVMWVF_XXXXX
  358. // flags then translated appropriately here.
  359. //
  360. pData->idBitmap = 0; // Default is no bitmap.
  361. pData->idWatermark = 0; // No watermark used.
  362. if (SFVMWVF_SPECIALTASK & dwStyle)
  363. {
  364. pData->dwFlags |= SFVMWVF_SPECIALTASK;
  365. pData->idBitmap = IDB_CPANEL_ICON_BMP;
  366. }
  367. }
  368. }
  369. }
  370. ATOMICRELEASE(pwvi);
  371. }
  372. else if (S_FALSE == hr)
  373. {
  374. //
  375. // Tell defview the enumeration is complete.
  376. // Release the info enumerator.
  377. //
  378. pData->dwFlags = SFVMWVF_NOMORETASKS;
  379. ATOMICRELEASE(_penumWvInfo);
  380. }
  381. }
  382. DBG_EXIT_HRES(FTF_CPANEL, "CControlPanelViewCallback::_OnSFVMEnumWebViewTasks", hr);
  383. return THR(hr);
  384. }
  385. //
  386. // Retrieves the proper enumerator of webview information.
  387. //
  388. HRESULT
  389. CControlPanelViewCallback::_GetWebViewInfoEnumerator(
  390. CPL::IEnumCplWebViewInfo **ppewvi
  391. )
  392. {
  393. DBG_ENTER(FTF_CPANEL, "CControlPanelViewCallback::_GetWebViewInfoEnumerator");
  394. ASSERT(NULL != ppewvi);
  395. ASSERT(!IsBadWritePtr(ppewvi, sizeof(*ppewvi)));
  396. CPL::ICplView *pView;
  397. HRESULT hr = _GetCplView(&pView);
  398. if (SUCCEEDED(hr))
  399. {
  400. DWORD dwFlags = 0;
  401. bool bBarricadeFixedByPolicy;
  402. bool bCategoryViewActive = CPL::CategoryViewIsActive(&bBarricadeFixedByPolicy);
  403. if (bBarricadeFixedByPolicy)
  404. {
  405. //
  406. // If the view type is fixed by policy, we don't present
  407. // controls that allow the user to switch view types.
  408. //
  409. dwFlags |= CPVIEW_EF_NOVIEWSWITCH;
  410. }
  411. if (bCategoryViewActive)
  412. {
  413. CPL::eCPCAT eCategory;
  414. hr = _GetCplCategoryFromFolderIDList(&eCategory);
  415. if (SUCCEEDED(hr))
  416. {
  417. if (S_OK == hr)
  418. {
  419. //
  420. // Displaying a category page.
  421. //
  422. hr = pView->EnumCategoryWebViewInfo(dwFlags, eCategory, ppewvi);
  423. }
  424. else
  425. {
  426. //
  427. // Displaying category choice page.
  428. //
  429. hr = pView->EnumCategoryChoiceWebViewInfo(dwFlags, ppewvi);
  430. }
  431. }
  432. }
  433. else
  434. {
  435. //
  436. // Displaying classic view.
  437. //
  438. hr = pView->EnumClassicWebViewInfo(dwFlags, ppewvi);
  439. }
  440. ATOMICRELEASE(pView);
  441. }
  442. DBG_EXIT_HRES(FTF_CPANEL, "CControlPanelViewCallback::_GetWebViewInfoEnumerator", hr);
  443. return THR(hr);
  444. }
  445. //
  446. // Get a pointer to the CCplView object. If the object is not yet created,
  447. // create one.
  448. //
  449. HRESULT
  450. CControlPanelViewCallback::_GetCplView(
  451. CPL::ICplView **ppView,
  452. bool bInitialize
  453. )
  454. {
  455. HRESULT hr = S_OK;
  456. *ppView = NULL;
  457. IEnumIDList *penumIDs = NULL;
  458. if (NULL == _pCplView || bInitialize)
  459. {
  460. //
  461. // If creating a new view object or reinitializing an
  462. // existing view object, we'll need the most recent list of
  463. // folder item IDs.
  464. //
  465. hr = _EnumFolderViewIDs(&penumIDs);
  466. }
  467. if (SUCCEEDED(hr))
  468. {
  469. if (NULL == _pCplView)
  470. {
  471. //
  472. // Create a new view object.
  473. // Give the view CB's site pointer to the CplView object.
  474. // This is then used to initialize the various command objects
  475. // contained in the CCplNamespace object. Some of these command objects
  476. // need access to the shell browser. The most generic method of
  477. // providing this access was to use the site mechanism.
  478. //
  479. IUnknown *punkSite;
  480. hr = GetSite(IID_IUnknown, (void **)&punkSite);
  481. if (SUCCEEDED(hr))
  482. {
  483. hr = CPL::CplView_CreateInstance(penumIDs, punkSite, CPL::IID_ICplView, (void **)&_pCplView);
  484. ATOMICRELEASE(punkSite);
  485. }
  486. }
  487. else if (bInitialize)
  488. {
  489. //
  490. // Reinitialize the existing view object.
  491. //
  492. hr = _pCplView->RefreshIDs(penumIDs);
  493. }
  494. }
  495. if (SUCCEEDED(hr))
  496. {
  497. //
  498. // Create a reference for the caller.
  499. //
  500. (*ppView = _pCplView)->AddRef();
  501. }
  502. ATOMICRELEASE(penumIDs);
  503. return THR(hr);
  504. }
  505. //
  506. // Get a Category ID number based on the folder's current ID list.
  507. // The Control Panel ID list uses a hidden part to store the
  508. // 'category' ID. The ID is simply one of the eCPCAT enumeration.
  509. // It is added to the ID list in CPL::COpenCplCategory::Execute().
  510. // This function returns:
  511. //
  512. // S_OK - *peCategory contains a valid category ID.
  513. // S_FALSE - folder ID list did not contain a category ID.
  514. // E_FAIL - folder ID list contains an invalid category ID.
  515. //
  516. HRESULT
  517. CControlPanelViewCallback::_GetCplCategoryFromFolderIDList(
  518. CPL::eCPCAT *peCategory
  519. )
  520. {
  521. ASSERT(NULL != peCategory);
  522. ASSERT(!IsBadWritePtr(peCategory, sizeof(*peCategory)));
  523. HRESULT hr = S_FALSE;
  524. CPL::eCPCAT eCategory = CPL::eCPCAT(-1);
  525. WCHAR szHidden[10];
  526. szHidden[0] = L'\0';
  527. ILGetHiddenStringW(_pcpf->GetIDList(), IDLHID_NAVIGATEMARKER, szHidden, ARRAYSIZE(szHidden));
  528. if (L'\0' != szHidden[0])
  529. {
  530. eCategory = CPL::eCPCAT(StrToInt(szHidden));
  531. if (CPL::eCPCAT_NUMCATEGORIES > eCategory)
  532. {
  533. hr = S_OK;
  534. }
  535. else
  536. {
  537. hr = E_FAIL;
  538. }
  539. }
  540. *peCategory = eCategory;
  541. return THR(hr);
  542. }
  543. HRESULT
  544. CControlPanelViewCallback::_EnumFolderViewIDs(
  545. IEnumIDList **ppenumIDs
  546. )
  547. {
  548. IUnknown *punkSite;
  549. HRESULT hr = THR(GetSite(IID_IUnknown, (void **)&punkSite));
  550. if (SUCCEEDED(hr))
  551. {
  552. IDVGetEnum *pdvge; // private defview interface
  553. hr = THR(IUnknown_QueryService(punkSite, SID_SFolderView, IID_PPV_ARG(IDVGetEnum, &pdvge)));
  554. if (SUCCEEDED(hr))
  555. {
  556. const DWORD dwEnumFlags = SHCONTF_NONFOLDERS | SHCONTF_FOLDERS;
  557. hr = THR(pdvge->CreateEnumIDListFromContents(_pidl, dwEnumFlags, ppenumIDs));
  558. pdvge->Release();
  559. }
  560. punkSite->Release();
  561. }
  562. return THR(hr);
  563. }
  564. //
  565. // DefView calls this to obtain information about the view CB's webview
  566. // content.
  567. //
  568. HRESULT
  569. CControlPanelViewCallback::_OnSFVMGetWebViewContent(
  570. DWORD pv,
  571. SFVM_WEBVIEW_CONTENT_DATA *pData
  572. )
  573. {
  574. DBG_ENTER(FTF_CPANEL, "CControlPanelViewCallback::_OnSFVMGetWebViewContent");
  575. ASSERT(NULL != pData);
  576. ASSERT(!IsBadWritePtr(pData, sizeof(*pData)));
  577. UNREFERENCED_PARAMETER(pv);
  578. HRESULT hr = S_OK;
  579. //
  580. // Tell defview...
  581. //
  582. // 1. We'll provide a 'barricade' if we're in 'category' view mode.
  583. // Our 'barricade' is 'category' view.
  584. // 2. We'll enumerate a set of non-standard webview tasks regardless
  585. // of the view mode. One of these tasks is to switch between 'classic'
  586. // view and 'category' view.
  587. //
  588. ZeroMemory(pData, sizeof(*pData));
  589. pData->dwFlags = SFVMWVF_ENUMTASKS | SFVMWVF_CONTENTSCHANGE;
  590. if (CPL::CategoryViewIsActive(NULL))
  591. {
  592. pData->dwFlags |= SFVMWVF_BARRICADE;
  593. }
  594. DBG_EXIT_HRES(FTF_CPANEL, "CControlPanelViewCallback::_OnSFVMGetWebViewContent", hr);
  595. return THR(hr);
  596. }
  597. //
  598. // SFVM_UPDATESTATUSBAR handler.
  599. // In 'Category' view, we want no status bar content.
  600. // In 'Classic' view, we want the standard content produced by defview.
  601. //
  602. HRESULT
  603. CControlPanelViewCallback::_OnSFVMUpdateStatusBar(
  604. DWORD pv,
  605. BOOL bInitialize
  606. )
  607. {
  608. DBG_ENTER(FTF_CPANEL, "CControlPanelViewCallback::_OnSFVMUpdateStatusBar");
  609. HRESULT hr;
  610. if (CPL::CategoryViewIsActive(NULL))
  611. {
  612. //
  613. // 'Category' view.
  614. // Simply return S_OK. DefView has already cleared the statusbar.
  615. // Returning S_OK tells DefView that we'll set the status text ourselves.
  616. // Therefore, by not setting anything, the statusbar remains empty.
  617. //
  618. hr = S_OK;
  619. }
  620. else
  621. {
  622. //
  623. // 'Classic' view. Returning an error code tells defview
  624. // to handle all the status bar content.
  625. //
  626. hr = E_NOTIMPL;
  627. }
  628. ASSERT(S_OK == hr || E_NOTIMPL == hr);
  629. DBG_EXIT_HRES(FTF_CPANEL, "CControlPanelViewCallback::_OnSFVMUpdateStatusBar", hr);
  630. return hr;
  631. }
  632. //
  633. // Selectively disable web view for this folder (WOW64 thing for the 32-bit
  634. // control panel.
  635. //
  636. HRESULT
  637. CControlPanelViewCallback::_OnSFVMForceWebView(
  638. DWORD pv,
  639. PBOOL pfForce
  640. )
  641. {
  642. DBG_ENTER(FTF_CPANEL, "CControlPanelViewCallback::_OnSFVMForceWebView");
  643. ASSERT(NULL != pfForce);
  644. ASSERT(!IsBadWritePtr(pfForce, sizeof(*pfForce)));
  645. UNREFERENCED_PARAMETER(pv);
  646. HRESULT hr;
  647. if (IsOS(OS_WOW6432))
  648. {
  649. *pfForce = FALSE;
  650. hr = S_OK;
  651. }
  652. else
  653. {
  654. hr = E_FAIL;
  655. }
  656. DBG_EXIT_HRES(FTF_CPANEL, "CControlPanelViewCallback::_OnSFVMForceWebView", hr);
  657. return hr;
  658. }
  659. //
  660. // Tell defview we're DUI (avoids extra legacy work on defview side)
  661. //
  662. HRESULT
  663. CControlPanelViewCallback::_OnSFVMGetWebViewLayout(
  664. DWORD pv,
  665. UINT uViewMode,
  666. SFVM_WEBVIEW_LAYOUT_DATA* pData)
  667. {
  668. ZeroMemory(pData, sizeof(*pData));
  669. pData->dwLayout = SFVMWVL_NORMAL;
  670. return S_OK;
  671. }
  672. //
  673. // Provide the barricade DUI element. In Control Panel, our 'barricade'
  674. // is simply our 'Category' view. We inspect the folder's pidl to determine
  675. // if we display the 'category choice' view or a view for a specific
  676. // category.
  677. //
  678. HRESULT
  679. CControlPanelViewCallback::_OnSFVMGetWebViewBarricade(
  680. DWORD pv,
  681. DUI::Element **ppe
  682. )
  683. {
  684. DBG_ENTER(FTF_CPANEL, "CControlPanelViewCallback::_OnSFVMGetWebViewBarricade");
  685. ASSERT(NULL != ppe);
  686. ASSERT(!IsBadWritePtr(ppe, sizeof(*ppe)));
  687. UNREFERENCED_PARAMETER(pv);
  688. *ppe = NULL;
  689. CPL::ICplView *pView;
  690. HRESULT hr = _GetCplView(&pView, true);
  691. if (SUCCEEDED(hr))
  692. {
  693. CPL::eCPCAT eCategory;
  694. hr = _GetCplCategoryFromFolderIDList(&eCategory);
  695. if (SUCCEEDED(hr))
  696. {
  697. if (S_OK == hr)
  698. {
  699. hr = pView->CreateCategoryElement(eCategory, ppe);
  700. }
  701. else
  702. {
  703. hr = pView->CreateCategoryChoiceElement(ppe);
  704. }
  705. }
  706. ATOMICRELEASE(pView);
  707. }
  708. DBG_EXIT_HRES(FTF_CPANEL, "CControlPanelViewCallback::_OnSFVMGetWebViewBarricade", hr);
  709. return THR(hr);
  710. }
  711. HRESULT
  712. CControlPanelViewCallback::_OnSFVMWindowDestroy(
  713. DWORD pv,
  714. HWND hwnd
  715. )
  716. {
  717. DBG_ENTER(FTF_CPANEL, "CControlPanelViewCallback::_OnSFVMWindowDestroy");
  718. UNREFERENCED_PARAMETER(pv);
  719. UNREFERENCED_PARAMETER(hwnd);
  720. HRESULT hr = S_OK;
  721. //
  722. // Need to destroy these in response to window destruction so
  723. // we break the site chains. If we don't do this, the
  724. // CDefView dtor is never called because our namespace
  725. // objects have outstanding references to CDefView.
  726. //
  727. ATOMICRELEASE(_penumWvInfo);
  728. ATOMICRELEASE(_pCplView);
  729. DBG_EXIT_HRES(FTF_CPANEL, "CControlPanelViewCallback::_OnSFVMWindowDestroy", hr);
  730. return THR(hr);
  731. }
  732. // column IDs
  733. typedef enum
  734. {
  735. CPL_ICOL_NAME = 0,
  736. CPL_ICOL_COMMENT,
  737. };
  738. const COLUMN_INFO c_cpl_cols[] =
  739. {
  740. DEFINE_COL_STR_ENTRY(SCID_NAME, 20, IDS_NAME_COL),
  741. DEFINE_COL_STR_ENTRY(SCID_Comment, 20, IDS_EXCOL_COMMENT),
  742. };
  743. #define PRINTERS_SORT_INDEX 45
  744. const REQREGITEM c_asControlPanelReqItems[] =
  745. {
  746. { &CLSID_Printers, IDS_PRNANDFAXFOLDER, c_szShell32Dll, -IDI_PRNFLD, PRINTERS_SORT_INDEX, SFGAO_DROPTARGET | SFGAO_FOLDER, NULL},
  747. };
  748. CControlPanelFolder::CControlPanelFolder(IUnknown* punkOuter) :
  749. CAggregatedUnknown (punkOuter),
  750. _pidl (NULL),
  751. _punkReg (NULL),
  752. _hdsaExtPropRegVals (NULL)
  753. {
  754. }
  755. CControlPanelFolder::~CControlPanelFolder()
  756. {
  757. if (NULL != _hdsaExtPropRegVals)
  758. {
  759. DSA_DestroyCallback(_hdsaExtPropRegVals,
  760. _DestroyExtPropsRegValEntry,
  761. NULL);
  762. }
  763. if (NULL != _pidl)
  764. {
  765. ILFree(_pidl);
  766. }
  767. SHReleaseInnerInterface(SAFECAST(this, IShellFolder *), &_punkReg);
  768. }
  769. #define REGSTR_POLICIES_RESTRICTCPL REGSTR_PATH_POLICIES TEXT("\\Explorer\\RestrictCpl")
  770. #define REGSTR_POLICIES_DISALLOWCPL REGSTR_PATH_POLICIES TEXT("\\Explorer\\DisallowCpl")
  771. HRESULT CControlPanel_CreateInstance(IUnknown* punkOuter, REFIID riid, void** ppvOut)
  772. {
  773. CControlPanelFolder* pcpf = new CControlPanelFolder(punkOuter);
  774. if (NULL != pcpf)
  775. {
  776. static REGITEMSPOLICY ripControlPanel =
  777. {
  778. REGSTR_POLICIES_RESTRICTCPL,
  779. REST_RESTRICTCPL,
  780. REGSTR_POLICIES_DISALLOWCPL,
  781. REST_DISALLOWCPL
  782. };
  783. REGITEMSINFO riiControlPanel =
  784. {
  785. REGSTR_PATH_EXPLORER TEXT("\\ControlPanel\\NameSpace"),
  786. &ripControlPanel,
  787. TEXT(':'),
  788. SHID_CONTROLPANEL_REGITEM_EX, // note, we don't really have a sig
  789. 1,
  790. SFGAO_CANLINK,
  791. ARRAYSIZE(c_asControlPanelReqItems),
  792. c_asControlPanelReqItems,
  793. RIISA_ALPHABETICAL,
  794. NULL,
  795. // we want everything from after IDREGITEM.bOrder to the first 2 cBuf bytes to be filled with 0's
  796. (FIELD_OFFSET(IDCONTROL, cBuf) + 2) - (FIELD_OFFSET(IDREGITEM, bOrder) + 1),
  797. SHID_CONTROLPANEL_REGITEM,
  798. };
  799. if (IsOS(OS_WOW6432))
  800. {
  801. // The crippled 32-bit control panel on IA64 should show less/other stuff
  802. riiControlPanel.pszRegKey = REGSTR_PATH_EXPLORER TEXT("\\ControlPanelWOW64\\NameSpace");
  803. riiControlPanel.iReqItems = 0;
  804. }
  805. //
  806. // we dont want to return a naked
  807. // control panel folder. this should
  808. // only fail with memory probs.
  809. //
  810. HRESULT hr = CRegFolder_CreateInstance(&riiControlPanel, (IUnknown*) (IShellFolder2*) pcpf,
  811. IID_IUnknown, (void **) &(pcpf->_punkReg));
  812. if (SUCCEEDED(hr))
  813. {
  814. hr = pcpf->QueryInterface(riid, ppvOut);
  815. }
  816. pcpf->Release();
  817. return hr;
  818. }
  819. *ppvOut = NULL;
  820. return E_OUTOFMEMORY;
  821. }
  822. HRESULT CControlPanelFolder::v_InternalQueryInterface(REFIID riid, void **ppv)
  823. {
  824. static const QITAB qit[] = {
  825. QITABENT(CControlPanelFolder, IShellFolder2), // IID_IShellFolder2
  826. QITABENTMULTI(CControlPanelFolder, IShellFolder, IShellFolder2), // IID_IShellFolder
  827. QITABENT(CControlPanelFolder, IPersistFolder2), // IID_IPersistFolder2
  828. QITABENTMULTI(CControlPanelFolder, IPersistFolder, IPersistFolder2), // IID_IPersistFolder
  829. QITABENTMULTI(CControlPanelFolder, IPersist, IPersistFolder2), // IID_IPersist
  830. { 0 },
  831. };
  832. if (_punkReg && RegGetsFirstShot(riid))
  833. {
  834. return _punkReg->QueryInterface(riid, ppv);
  835. }
  836. else
  837. {
  838. return QISearch(this, qit, riid, ppv);
  839. }
  840. }
  841. // Unicode .cpl's will be flagged by having oName = 0, oInfo = 0,
  842. // cBuf[0] = '\0', and cBuf[1] = UNICODE_CPL_SIGNATURE_BYTE
  843. #define UNICODE_CPL_SIGNATURE_BYTE (BYTE)0x6a
  844. LPIDCONTROLW CControlPanelFolder::_IsUnicodeCPL(LPIDCONTROL pidc)
  845. {
  846. ASSERT(_IsValid((LPCITEMIDLIST)pidc));
  847. if ((pidc->oName == 0) && (pidc->oInfo == 0) && (pidc->cBuf[0] == '\0') && (pidc->cBuf[1] == UNICODE_CPL_SIGNATURE_BYTE))
  848. return (LPIDCONTROLW)pidc;
  849. return NULL;
  850. }
  851. HRESULT _IDControlCreateW(PCWSTR pszModule, int idIcon, PCWSTR pszName, PCWSTR pszInfo, LPITEMIDLIST *ppidl)
  852. {
  853. UINT cbModule = CbFromCchW(lstrlen(pszModule) + 1);
  854. UINT cbName = CbFromCchW(lstrlen(pszName) + 1);
  855. UINT cbInfo = CbFromCchW(lstrlen(pszInfo) + 1);
  856. UINT cbIDC = FIELD_OFFSET(IDCONTROLW, cBufW) + cbModule + cbName + cbInfo;
  857. *ppidl = _ILCreate(cbIDC + sizeof(USHORT));
  858. if (*ppidl)
  859. {
  860. IDCONTROLW *pidc = (IDCONTROLW *) *ppidl;
  861. // init the static bits (ILCreate() zero inits)
  862. pidc->idIcon = idIcon;
  863. pidc->cBuf[1] = UNICODE_CPL_SIGNATURE_BYTE;
  864. // copy module
  865. ualstrcpy(pidc->cBufW, pszModule);
  866. // copy name
  867. pidc->oNameW = (USHORT)(cbModule / sizeof(pszModule[0]));
  868. ualstrcpy(pidc->cBufW + pidc->oNameW, pszName);
  869. // copy info
  870. pidc->oInfoW = pidc->oNameW + (USHORT)(cbName / sizeof(pszName[0]));
  871. ualstrcpy(pidc->cBufW + pidc->oInfoW, pszInfo);
  872. pidc->cb = (USHORT)cbIDC;
  873. return S_OK;
  874. }
  875. return E_OUTOFMEMORY;
  876. }
  877. HRESULT _IDControlCreateA(PCSTR pszModule, int idIcon, PCSTR pszName, PCSTR pszInfo, LPITEMIDLIST *ppidl)
  878. {
  879. UINT cbModule = CbFromCchA(lstrlenA(pszModule) + 1);
  880. UINT cbName = CbFromCchA(lstrlenA(pszName) + 1);
  881. UINT cbInfo = CbFromCchA(lstrlenA(pszInfo) + 1);
  882. UINT cbIDC = FIELD_OFFSET(IDCONTROL, cBuf) + cbModule + cbName + cbInfo;
  883. *ppidl = _ILCreate(cbIDC + sizeof(USHORT));
  884. if (*ppidl)
  885. {
  886. IDCONTROL *pidc = (IDCONTROL *) *ppidl;
  887. // init the static bits (ILCreate() zero inits)
  888. pidc->idIcon = idIcon;
  889. // copy module
  890. lstrcpyA(pidc->cBuf, pszModule);
  891. // copy name
  892. pidc->oName = (USHORT)(cbModule / sizeof(pszModule[0]));
  893. lstrcpyA(pidc->cBuf + pidc->oName, pszName);
  894. // copy info
  895. pidc->oInfo = pidc->oName + (USHORT)(cbName / sizeof(pszName[0]));
  896. lstrcpyA(pidc->cBuf + pidc->oInfo, pszInfo);
  897. pidc->cb = (USHORT)cbIDC;
  898. return S_OK;
  899. }
  900. return E_OUTOFMEMORY;
  901. }
  902. HRESULT IDControlCreate(LPTSTR pszModule, int idIcon, LPTSTR pszName, LPTSTR pszInfo, LPITEMIDLIST *ppidc)
  903. {
  904. CHAR szModuleA[MAX_PATH];
  905. CHAR szNameA[MAX_CCH_CPLNAME];
  906. CHAR szInfoA[MAX_CCH_CPLINFO];
  907. ASSERT(lstrlen(pszModule) < MAX_PATH);
  908. ASSERT(lstrlen(pszName) < MAX_CCH_CPLNAME);
  909. ASSERT(lstrlen(pszInfo) < MAX_CCH_CPLINFO);
  910. // See if any of the three string inputs cannot be represented as ANSI
  911. if (DoesStringRoundTrip(pszModule, szModuleA, ARRAYSIZE(szModuleA))
  912. && DoesStringRoundTrip(pszName, szNameA, ARRAYSIZE(szNameA))
  913. && DoesStringRoundTrip(pszInfo, szInfoA, ARRAYSIZE(szInfoA)))
  914. {
  915. return _IDControlCreateA(szModuleA, idIcon, szNameA, szInfoA, ppidc);
  916. }
  917. else
  918. {
  919. // Must create a full Unicode IDL
  920. return _IDControlCreateW(pszModule, idIcon, pszName, pszInfo, ppidc);
  921. }
  922. }
  923. LPIDCONTROL CControlPanelFolder::_IsValid(LPCITEMIDLIST pidl)
  924. {
  925. //
  926. // the original design had no signature
  927. // so we are left just trying to filter out the regitems that might
  928. // somehow get to us. we used to SIL_GetType(pidl) != SHID_CONTROLPANEL_REGITEM)
  929. // but if somehow we had an icon index that had the low byte equal
  930. // to SHID_CONTROLPANEL_REGITEM (0x70) we would invalidate it. DUMB!
  931. //
  932. // so we will complicate the heuristics a little bit. lets assume that
  933. // all icon indeces will range between 0xFF000000 and 0x00FFFFFF
  934. // (or -16777214 and 16777215, 16 million each way should be plenty).
  935. // of course this could easily get false positives, but there really
  936. // isnt anything else that we can check against.
  937. //
  938. // we will also check a minimum size.
  939. //
  940. if (pidl && pidl->mkid.cb > FIELD_OFFSET(IDCONTROL, cBuf))
  941. {
  942. LPIDCONTROL pidc = (LPIDCONTROL)pidl;
  943. int i = pidc->idIcon & 0xFF000000;
  944. if (i == 0 || i == 0xFF000000)
  945. return pidc;
  946. }
  947. return NULL;
  948. }
  949. #define REGVAL_CTRLFLDRITEM_MODULE TEXT("Module")
  950. #define REGVAL_CTRLFLDRITEM_ICONINDEX TEXT("IconIndex")
  951. #define REGVAL_CTRLFLDRITEM_NAME TEXT("Name")
  952. #define REGVAL_CTRLFLDRITEM_INFO TEXT("Info")
  953. HRESULT GetPidlFromCanonicalName(LPCTSTR pszCanonicalName, LPITEMIDLIST* ppidl)
  954. {
  955. *ppidl = NULL;
  956. TCHAR szRegPath[MAX_PATH] = REGSTR_PATH_EXPLORER TEXT("\\ControlPanel\\NameSpace\\");
  957. HRESULT hr = StringCchCat(szRegPath, ARRAYSIZE(szRegPath), pszCanonicalName);
  958. if (SUCCEEDED(hr))
  959. {
  960. HKEY hKey;
  961. if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, szRegPath, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
  962. {
  963. TCHAR szModule[MAX_PATH], szName[MAX_CCH_CPLNAME], szInfo[MAX_CCH_CPLINFO];
  964. DWORD dwIconIndex = 0, dwType, cbSize = sizeof(szModule);
  965. if (SHQueryValueEx(hKey, REGVAL_CTRLFLDRITEM_MODULE, NULL, &dwType, (LPBYTE)szModule, &cbSize) == ERROR_SUCCESS)
  966. {
  967. cbSize = sizeof(dwIconIndex);
  968. if (SHQueryValueEx(hKey, REGVAL_CTRLFLDRITEM_ICONINDEX, NULL, &dwType, (LPBYTE)&dwIconIndex, &cbSize) != ERROR_SUCCESS)
  969. {
  970. dwIconIndex = 0;
  971. }
  972. cbSize = sizeof(szName);
  973. if (SHQueryValueEx(hKey, REGVAL_CTRLFLDRITEM_NAME, NULL, &dwType, (LPBYTE)szName, &cbSize) != ERROR_SUCCESS)
  974. {
  975. szName[0] = TEXT('\0');
  976. }
  977. cbSize = sizeof(szInfo);
  978. if (SHQueryValueEx(hKey, REGVAL_CTRLFLDRITEM_INFO, NULL, &dwType, (LPBYTE)szInfo, &cbSize) != ERROR_SUCCESS)
  979. {
  980. szInfo[0] = TEXT('\0');
  981. }
  982. hr = IDControlCreate(szModule, EIRESID(dwIconIndex), szName, szInfo, ppidl);
  983. }
  984. RegCloseKey(hKey);
  985. }
  986. }
  987. return hr;
  988. }
  989. STDMETHODIMP CControlPanelFolder::ParseDisplayName(HWND hwnd, LPBC pbc, WCHAR* pszName,
  990. ULONG* pchEaten, LPITEMIDLIST* ppidl, ULONG* pdwAttrib)
  991. {
  992. if (!ppidl)
  993. return E_INVALIDARG;
  994. *ppidl = NULL;
  995. if (!pszName)
  996. return E_INVALIDARG;
  997. TCHAR szCanonicalName[MAX_PATH];
  998. SHUnicodeToTChar(pszName, szCanonicalName, ARRAYSIZE(szCanonicalName));
  999. HRESULT hr = GetPidlFromCanonicalName(szCanonicalName, ppidl);
  1000. if (SUCCEEDED(hr))
  1001. {
  1002. // First, make sure that the pidl we obtained is valid
  1003. DWORD dwAttrib = SFGAO_VALIDATE;
  1004. hr = GetAttributesOf(1, (LPCITEMIDLIST *)ppidl, &dwAttrib);
  1005. // Now, get the other attributes if they were requested
  1006. if (SUCCEEDED(hr) && pdwAttrib && *pdwAttrib)
  1007. {
  1008. hr = GetAttributesOf(1, (LPCITEMIDLIST *)ppidl, pdwAttrib);
  1009. }
  1010. }
  1011. return hr;
  1012. }
  1013. STDMETHODIMP CControlPanelFolder::GetAttributesOf(UINT cidl, LPCITEMIDLIST* apidl, ULONG* prgfInOut)
  1014. {
  1015. if ((*prgfInOut & SFGAO_VALIDATE) && cidl)
  1016. {
  1017. HRESULT hr = E_INVALIDARG;
  1018. LPIDCONTROL pidc = _IsValid(*apidl);
  1019. if (pidc)
  1020. {
  1021. TCHAR szModule[MAX_PATH];
  1022. hr = GetModuleMapped((LPIDCONTROL)*apidl, szModule, ARRAYSIZE(szModule), NULL, NULL, 0);
  1023. if (SUCCEEDED(hr))
  1024. {
  1025. if (PathFileExists(szModule))
  1026. hr = S_OK;
  1027. else
  1028. hr = E_FAIL;
  1029. }
  1030. }
  1031. return hr;
  1032. }
  1033. *prgfInOut &= SFGAO_CANLINK;
  1034. return S_OK;
  1035. }
  1036. STDMETHODIMP CControlPanelFolder::GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST* apidl,
  1037. REFIID riid, UINT *pres, void **ppv)
  1038. {
  1039. HRESULT hr = E_INVALIDARG;
  1040. LPIDCONTROL pidc = cidl && apidl ? _IsValid(apidl[0]) : NULL;
  1041. *ppv = NULL;
  1042. if (pidc && (IsEqualIID(riid, IID_IExtractIconA) || IsEqualIID(riid, IID_IExtractIconW)))
  1043. {
  1044. TCHAR achParams[MAX_PATH+1+32+1+MAX_CCH_CPLNAME]; // See wsprintf below
  1045. TCHAR szModule[MAX_PATH], szName[MAX_CCH_CPLNAME];
  1046. UINT idIcon;
  1047. // Map the icon ID for upgraded win95 shortcuts to CPLs
  1048. hr = GetModuleMapped(pidc, szModule, ARRAYSIZE(szModule), &idIcon, szName, ARRAYSIZE(szName));
  1049. if (SUCCEEDED(hr))
  1050. {
  1051. // Use the applet name in the pid if we didn't override the name in GetModuleMapped
  1052. if (*szName == 0)
  1053. {
  1054. hr = GetDisplayName(pidc, szName, ARRAYSIZE(szName));
  1055. }
  1056. if (SUCCEEDED(hr))
  1057. {
  1058. hr = StringCchPrintf(achParams, ARRAYSIZE(achParams), TEXT("%s,%d,%s"), szModule, idIcon, szName);
  1059. if (SUCCEEDED(hr))
  1060. {
  1061. hr = ControlExtractIcon_CreateInstance(achParams, riid, ppv);
  1062. }
  1063. }
  1064. }
  1065. }
  1066. else if (pidc && IsEqualIID(riid, IID_IContextMenu))
  1067. {
  1068. hr = CDefFolderMenu_Create(_pidl, hwnd, cidl, apidl,
  1069. SAFECAST(this, IShellFolder*), DFMCallBack, NULL, NULL, (IContextMenu**) ppv);
  1070. }
  1071. else if (pidc && IsEqualIID(riid, IID_IDataObject))
  1072. {
  1073. hr = CIDLData_CreateFromIDArray(_pidl, cidl, apidl, (IDataObject**) ppv);
  1074. }
  1075. else if (pidc && IsEqualIID(riid, IID_IQueryInfo))
  1076. {
  1077. TCHAR szTemp[MAX_CCH_CPLINFO];
  1078. hr = _GetDescription(pidc, szTemp, ARRAYSIZE(szTemp));
  1079. if (SUCCEEDED(hr))
  1080. {
  1081. hr = CreateInfoTipFromText(szTemp, riid, ppv);
  1082. }
  1083. }
  1084. return hr;
  1085. }
  1086. STDMETHODIMP CControlPanelFolder::GetDefaultSearchGUID(GUID *pGuid)
  1087. {
  1088. *pGuid = SRCID_SFileSearch;
  1089. return S_OK;
  1090. }
  1091. STDMETHODIMP CControlPanelFolder::EnumSearches(LPENUMEXTRASEARCH *ppenum)
  1092. {
  1093. *ppenum = NULL;
  1094. return E_NOTIMPL;
  1095. }
  1096. STDMETHODIMP CControlPanelFolder::EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppenum)
  1097. {
  1098. *ppenum = NULL;
  1099. if (!(grfFlags & SHCONTF_NONFOLDERS))
  1100. return S_FALSE;
  1101. HRESULT hr;
  1102. CControlPanelEnum* pesf = new CControlPanelEnum(grfFlags);
  1103. if (pesf)
  1104. {
  1105. // get list of module names
  1106. hr = pesf->Init();
  1107. if (SUCCEEDED(hr))
  1108. pesf->QueryInterface(IID_PPV_ARG(IEnumIDList, ppenum));
  1109. pesf->Release();
  1110. }
  1111. else
  1112. hr = E_OUTOFMEMORY;
  1113. return hr;
  1114. }
  1115. STDMETHODIMP CControlPanelFolder::BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void** ppv)
  1116. {
  1117. *ppv = NULL;
  1118. return E_NOTIMPL;
  1119. }
  1120. STDMETHODIMP CControlPanelFolder::CompareIDs(LPARAM iCol, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  1121. {
  1122. LPIDCONTROL pidc1 = _IsValid(pidl1);
  1123. LPIDCONTROL pidc2 = _IsValid(pidl2);
  1124. if (pidc1 && pidc2)
  1125. {
  1126. TCHAR szName1[max(MAX_CCH_CPLNAME, MAX_CCH_CPLINFO)];
  1127. TCHAR szName2[max(MAX_CCH_CPLNAME, MAX_CCH_CPLINFO)];
  1128. int iCmp;
  1129. switch (iCol)
  1130. {
  1131. case CPL_ICOL_COMMENT:
  1132. _GetDescription(pidc1, szName1, ARRAYSIZE(szName1));
  1133. _GetDescription(pidc2, szName2, ARRAYSIZE(szName2));
  1134. // They're both ANSI, so we can compare directly
  1135. iCmp = StrCmpLogicalRestricted(szName1, szName2);
  1136. if (iCmp != 0)
  1137. return ResultFromShort(iCmp);
  1138. // Fall through if the help field compares the same...
  1139. case CPL_ICOL_NAME:
  1140. default:
  1141. GetDisplayName(pidc1, szName1, ARRAYSIZE(szName1));
  1142. GetDisplayName(pidc2, szName2, ARRAYSIZE(szName2));
  1143. return ResultFromShort(StrCmpLogicalRestricted(szName1, szName2));
  1144. }
  1145. }
  1146. return E_INVALIDARG;
  1147. }
  1148. //
  1149. // background (no items) context menu callback
  1150. //
  1151. HRESULT CALLBACK CControls_DFMCallBackBG(IShellFolder *psf, HWND hwnd,
  1152. IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1153. {
  1154. HRESULT hr = S_OK;
  1155. switch (uMsg)
  1156. {
  1157. case DFM_MERGECONTEXTMENU:
  1158. break;
  1159. case DFM_GETHELPTEXT:
  1160. LoadStringA(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPSTR)lParam, HIWORD(wParam));;
  1161. break;
  1162. case DFM_GETHELPTEXTW:
  1163. LoadStringW(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPWSTR)lParam, HIWORD(wParam));;
  1164. break;
  1165. case DFM_INVOKECOMMAND:
  1166. hr = S_FALSE; // view menu items, use the default code.
  1167. break;
  1168. default:
  1169. hr = E_NOTIMPL;
  1170. break;
  1171. }
  1172. return hr;
  1173. }
  1174. STDMETHODIMP CControlPanelFolder::CreateViewObject(HWND hwnd, REFIID riid, void** ppv)
  1175. {
  1176. HRESULT hr;
  1177. if (IsEqualIID(riid, IID_IShellView))
  1178. {
  1179. if (SHRestricted(REST_NOCONTROLPANEL))
  1180. {
  1181. ShellMessageBox(HINST_THISDLL, hwnd, MAKEINTRESOURCE(IDS_RESTRICTIONS),
  1182. MAKEINTRESOURCE(IDS_RESTRICTIONSTITLE), MB_OK|MB_ICONSTOP);
  1183. hr = HRESULT_FROM_WIN32( ERROR_CANCELLED );
  1184. }
  1185. else
  1186. {
  1187. SFV_CREATE sSFV;
  1188. sSFV.cbSize = sizeof(sSFV);
  1189. sSFV.psvOuter = NULL;
  1190. sSFV.psfvcb = new CControlPanelViewCallback(this);
  1191. QueryInterface(IID_IShellFolder, (void**) &sSFV.pshf); // in case we are agregated
  1192. hr = SHCreateShellFolderView(&sSFV, (IShellView**) ppv);
  1193. if (sSFV.pshf)
  1194. sSFV.pshf->Release();
  1195. if (sSFV.psfvcb)
  1196. sSFV.psfvcb->Release();
  1197. }
  1198. }
  1199. else if (IsEqualIID(riid, IID_IContextMenu))
  1200. {
  1201. hr = CDefFolderMenu_Create(NULL, hwnd, 0, NULL,
  1202. SAFECAST(this, IShellFolder*), CControls_DFMCallBackBG, NULL, NULL, (IContextMenu**) ppv);
  1203. }
  1204. else
  1205. {
  1206. *ppv = NULL;
  1207. hr = E_NOINTERFACE;
  1208. }
  1209. return hr;
  1210. }
  1211. STDMETHODIMP CControlPanelFolder::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD dwFlags, STRRET* pstrret)
  1212. {
  1213. DBG_ENTER(FTF_CPANEL, "CControlPanelFolder::GetDisplayNameOf");
  1214. HRESULT hr = E_INVALIDARG;
  1215. LPIDCONTROL pidc = _IsValid(pidl);
  1216. if (pidc)
  1217. {
  1218. hr = S_OK;
  1219. TCHAR szName[max(MAX_PATH, MAX_CCH_CPLNAME)];
  1220. if ((dwFlags & (SHGDN_FORPARSING | SHGDN_INFOLDER | SHGDN_FORADDRESSBAR)) == ((SHGDN_FORPARSING | SHGDN_INFOLDER)))
  1221. {
  1222. hr = GetModule(pidc, szName, ARRAYSIZE(szName));
  1223. }
  1224. else
  1225. {
  1226. hr = GetDisplayName(pidc, szName, ARRAYSIZE(szName));
  1227. }
  1228. if (SUCCEEDED(hr))
  1229. {
  1230. hr = StringToStrRet(szName, pstrret);
  1231. }
  1232. }
  1233. else if (IsSelf(1, &pidl))
  1234. {
  1235. //
  1236. // Control Panel is registered with "WantsFORDISPLAY".
  1237. //
  1238. hr = _GetDisplayNameForSelf(dwFlags, pstrret);
  1239. }
  1240. DBG_EXIT_HRES(FTF_CPANEL, "CControlPanelFolder::GetDisplayNameOf", hr);
  1241. return THR(hr);
  1242. }
  1243. HRESULT
  1244. CControlPanelFolder::_GetDisplayNameForSelf(
  1245. DWORD dwFlags,
  1246. STRRET* pstrret
  1247. )
  1248. {
  1249. DBG_ENTER(FTF_CPANEL, "CControlPanelFolder::_GetDisplayNameForSelf");
  1250. //
  1251. // This code only formats the folder name for display purposes.
  1252. //
  1253. const bool bForParsing = (0 != (SHGDN_FORPARSING & dwFlags));
  1254. const bool bForAddressBar = (0 != (SHGDN_FORADDRESSBAR & dwFlags));
  1255. ASSERT(!bForParsing || bForAddressBar);
  1256. HRESULT hr = S_FALSE;
  1257. if (CPL::CategoryViewIsActive(NULL))
  1258. {
  1259. WCHAR szHidden[10];
  1260. szHidden[0] = L'\0';
  1261. ILGetHiddenStringW(_pidl, IDLHID_NAVIGATEMARKER, szHidden, ARRAYSIZE(szHidden));
  1262. if (L'\0' != szHidden[0])
  1263. {
  1264. //
  1265. // The folder pidl has a hidden navigation marker. For Control Panel,
  1266. // this is a category number. Translate the category number to
  1267. // the category title and use that in the display name for the folder.
  1268. //
  1269. WCHAR szCategory[MAX_PATH];
  1270. szCategory[0] = L'\0';
  1271. const CPL::eCPCAT eCategory = CPL::eCPCAT(StrToInt(szHidden));
  1272. hr = CPL::CplView_GetCategoryTitle(eCategory, szCategory, ARRAYSIZE(szCategory));
  1273. if (SUCCEEDED(hr))
  1274. {
  1275. //
  1276. // Ex: "Appearance and Themes"
  1277. //
  1278. hr = StringToStrRet(szCategory, pstrret);
  1279. }
  1280. }
  1281. }
  1282. DBG_EXIT_HRES(FTF_CPANEL, "CControlPanelFolder::_GetDisplayNameForSelf", hr);
  1283. return THR(hr);
  1284. }
  1285. STDMETHODIMP CControlPanelFolder::BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void** ppv)
  1286. {
  1287. *ppv = NULL;
  1288. return E_NOTIMPL;
  1289. }
  1290. STDMETHODIMP CControlPanelFolder::SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR lpszName,
  1291. DWORD dwReserved, LPITEMIDLIST* ppidlOut)
  1292. {
  1293. return E_FAIL;
  1294. }
  1295. STDMETHODIMP CControlPanelFolder::GetDefaultColumn(DWORD dwRes, ULONG* pSort, ULONG* pDisplay)
  1296. {
  1297. return E_NOTIMPL;
  1298. }
  1299. STDMETHODIMP CControlPanelFolder::GetDefaultColumnState(UINT iColumn, DWORD* pdwState)
  1300. {
  1301. return E_NOTIMPL;
  1302. }
  1303. //
  1304. // Implementing this to handle Categorization of CPL applets.
  1305. //
  1306. STDMETHODIMP CControlPanelFolder::GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID* pscid, VARIANT* pv)
  1307. {
  1308. HRESULT hr = E_FAIL;
  1309. LPIDCONTROL pidc = _IsValid(pidl);
  1310. if (pidc)
  1311. {
  1312. if (IsEqualSCID(*pscid, SCID_CONTROLPANELCATEGORY))
  1313. {
  1314. HKEY hkey;
  1315. TCHAR achCPLName[MAX_CPL_EXEC_NAME], achRegName[MAX_CPL_EXEC_NAME];
  1316. hr = _GetFullCPLName(pidc, achCPLName, ARRAYSIZE(achCPLName));
  1317. if (SUCCEEDED(hr))
  1318. {
  1319. hr = _GetExtPropsKey(HKEY_LOCAL_MACHINE, &hkey, pscid);
  1320. if (S_OK == hr)
  1321. {
  1322. hr = _GetExtPropRegValName(hkey, achCPLName, achRegName, ARRAYSIZE(achRegName));
  1323. if (SUCCEEDED(hr))
  1324. {
  1325. hr = GetVariantFromRegistryValue(hkey, achRegName, pv);
  1326. }
  1327. RegCloseKey(hkey);
  1328. }
  1329. }
  1330. }
  1331. else if (IsEqualSCID(*pscid, SCID_Comment))
  1332. {
  1333. TCHAR szDesc[MAX_PATH] = {0};
  1334. hr = _GetDescription(pidc, szDesc, ARRAYSIZE(szDesc));
  1335. if (SUCCEEDED(hr))
  1336. {
  1337. hr = InitVariantFromStr(pv, szDesc);
  1338. }
  1339. }
  1340. }
  1341. return hr;
  1342. }
  1343. //
  1344. // This function takes a registry key (hkey) and goes through all the value names under that key.
  1345. // It expands any environment variables in the value names and then compares them to the input value
  1346. // (pszFullName). On finding a match it returns the (unexpanded) key name in pszRegValName.
  1347. //
  1348. // pszFullName is of the form = C:\WINNT\System32\main.cpl,keyboard
  1349. //
  1350. // The corresponding registry value name would be = %SystemRoot%\System32\main.cpl,keyboard
  1351. // or it could only be the filepath portion = %SystemRoot%\System32\main.cpl
  1352. // if all the applets in that .cpl file belong to the same category.
  1353. //
  1354. // Returns:
  1355. // S_OK = Name found and returned.
  1356. // S_FALSE = Name not found.
  1357. // Error = Error occured. Name not returned.
  1358. //
  1359. HRESULT
  1360. CControlPanelFolder::_GetExtPropRegValName(
  1361. HKEY hkey,
  1362. LPTSTR pszFullName, // This is modified only temporarily.
  1363. LPTSTR pszRegValName,
  1364. UINT cch
  1365. )
  1366. {
  1367. ASSERT(NULL != hkey);
  1368. ASSERT(NULL != pszFullName);
  1369. ASSERT(NULL != pszRegValName);
  1370. ASSERT(0 < cch);
  1371. ASSERT(!IsBadWritePtr(pszRegValName, cch * sizeof(*pszRegValName)));
  1372. HRESULT hr = S_FALSE;
  1373. TCHAR szSearchKeyNormalized[MAX_CPL_EXEC_NAME];
  1374. *pszRegValName = 0;
  1375. //
  1376. // Normalize the CPL spec we're comparing with.
  1377. //
  1378. DWORD dwResult = TW32(_NormalizeCplSpec(pszFullName,
  1379. szSearchKeyNormalized,
  1380. ARRAYSIZE(szSearchKeyNormalized)));
  1381. if (ERROR_SUCCESS == dwResult)
  1382. {
  1383. //
  1384. // Look it up in the cache.
  1385. //
  1386. if (_LookupExtPropRegValName(hkey, szSearchKeyNormalized, pszRegValName, cch))
  1387. {
  1388. hr = S_OK;
  1389. }
  1390. }
  1391. else
  1392. {
  1393. hr = HRESULT_FROM_WIN32(dwResult);
  1394. }
  1395. return hr;
  1396. }
  1397. //
  1398. // ----------------------------------------------------------------------------
  1399. // What is 'normalization' and why do we need this caching?
  1400. //
  1401. // Starting with Windows XP, CPL applets can be categorized so that they
  1402. // appear in one of the several Control Panel categories. The 'category'
  1403. // of an applet is stored in the registry as an 'extended property' value.
  1404. // The categorization data in the registry is stored in name-value pairs.
  1405. // The 'name' is the CPL's filesystem path with an optional applet name
  1406. // appended. The 'value' is the CPL's category.
  1407. //
  1408. // When looking up the category for an applet, we build the CPL's 'name'
  1409. // from the path and applet name. See _GetFullCPLName().
  1410. // This string is then used as a 'key' to locate the associated category
  1411. // 'extended property' value in the registry.
  1412. //
  1413. // The problem is that it's possible for two filesystem paths to refer
  1414. // to the same file yet be lexically different from one another. Issues
  1415. // such as embedded environment variables and long (LFN) vs. short (SFN)
  1416. // file names can create these lexical differences. In order to properly
  1417. // compare paths, each path must be 'normalized'. This is done by expanding
  1418. // environment variables, converting any LFN paths to their SFN counterpart
  1419. // and removing any leading and trailing spaces. Only then can the paths
  1420. // be correctly compared. Windows XP bug 328304 illuminated this requirement.
  1421. //
  1422. // Normalizing a name is a bit expensive, especially the LFN->SFN conversion
  1423. // which must hit the filesystem. To minimize the number of normalizations,
  1424. // I've added a simple cache. Nothing fancy. It's unsorted and lookup
  1425. // is linear. The cache is initialized the first time it is needed and
  1426. // it remains available until the CControlPanelFolder object is destroyed.
  1427. //
  1428. // brianau - 03/08/01
  1429. //
  1430. // ----------------------------------------------------------------------------
  1431. //
  1432. // Retrieve the name of the 'extended property' reg value
  1433. // corresponding to a particular CPL. The 'key' is a 'normalized'
  1434. // CPL name string.
  1435. //
  1436. // Assume a search key:
  1437. //
  1438. // "C:\WINNT\System32\main.cpl,keyboard"
  1439. //
  1440. // If an 'extended property' reg value with this name is found...
  1441. //
  1442. // "C:\WINNT\System32\main.cpl,keyboard"
  1443. //
  1444. // ...then it is considered a match. This means that only the "keyboard"
  1445. // applet provided by main.cpl is in the category associated with this
  1446. // entry.
  1447. //
  1448. // If an 'extended property' reg value with this name is found...
  1449. //
  1450. // "C:\WINNT\System32\main.cpl"
  1451. //
  1452. // ...then it is also considered a match. This means that ALL applets
  1453. // provided by main.cpl are in the category associated with this
  1454. // entry.
  1455. //
  1456. BOOL
  1457. CControlPanelFolder::_LookupExtPropRegValName(
  1458. HKEY hkey,
  1459. LPTSTR pszSearchKeyNormalized,
  1460. LPTSTR pszRegValName,
  1461. UINT cch
  1462. )
  1463. {
  1464. ASSERT(NULL != hkey);
  1465. ASSERT(NULL != pszSearchKeyNormalized);
  1466. ASSERT(NULL != pszRegValName);
  1467. ASSERT(!IsBadWritePtr(pszRegValName, cch * sizeof(*pszRegValName)));
  1468. BOOL bFound = FALSE;
  1469. if (NULL == _hdsaExtPropRegVals)
  1470. {
  1471. //
  1472. // Create and initialize (fill) the cache.
  1473. //
  1474. TW32(_InitExtPropRegValNameCache(hkey));
  1475. }
  1476. if (NULL != _hdsaExtPropRegVals)
  1477. {
  1478. //
  1479. // Search is simply linear.
  1480. //
  1481. int const cEntries = DSA_GetItemCount(_hdsaExtPropRegVals);
  1482. for (int i = 0; i < cEntries && !bFound; i++)
  1483. {
  1484. EPRV_CACHE_ENTRY *pEntry = (EPRV_CACHE_ENTRY *)DSA_GetItemPtr(_hdsaExtPropRegVals, i);
  1485. TBOOL(NULL != pEntry);
  1486. if (NULL != pEntry)
  1487. {
  1488. //
  1489. // Compare the normalized values. First do a complete
  1490. // comparison of the entire string.
  1491. //
  1492. if (0 == StrCmpI(pEntry->pszRegValNameNormalized, pszSearchKeyNormalized))
  1493. {
  1494. bFound = TRUE;
  1495. }
  1496. else
  1497. {
  1498. LPTSTR pszComma = StrChr(pszSearchKeyNormalized, TEXT(','));
  1499. if (NULL != pszComma)
  1500. {
  1501. //
  1502. // Compare only the path parts.
  1503. //
  1504. const DWORD cchPath = pszComma - pszSearchKeyNormalized;
  1505. if (0 == StrCmpNI(pEntry->pszRegValNameNormalized,
  1506. pszSearchKeyNormalized,
  1507. cchPath))
  1508. {
  1509. bFound = TRUE;
  1510. }
  1511. }
  1512. }
  1513. if (bFound)
  1514. {
  1515. StringCchCopy(pszRegValName, cch, pEntry->pszRegValName);
  1516. }
  1517. }
  1518. }
  1519. }
  1520. return bFound;
  1521. }
  1522. //
  1523. // Create and initialize the 'extended properties' value name
  1524. // cache. The cache is simply a DSA of type EPRV_CACHE_ENTRY.
  1525. // Each entry contains the normalized form of the reg value
  1526. // name paired with the reg value name as read from the registry
  1527. // (not normalized). The normalized value is the 'key'.
  1528. //
  1529. DWORD
  1530. CControlPanelFolder::_InitExtPropRegValNameCache(
  1531. HKEY hkey
  1532. )
  1533. {
  1534. ASSERT(NULL != hkey);
  1535. ASSERT(NULL == _hdsaExtPropRegVals);
  1536. DWORD dwResult = ERROR_SUCCESS;
  1537. _hdsaExtPropRegVals = DSA_Create(sizeof(EPRV_CACHE_ENTRY), 32);
  1538. if (NULL == _hdsaExtPropRegVals)
  1539. {
  1540. dwResult = TW32(ERROR_OUTOFMEMORY);
  1541. }
  1542. else
  1543. {
  1544. TCHAR szRegValName[MAX_CPL_EXEC_NAME];
  1545. DWORD dwIndex = 0;
  1546. while (ERROR_SUCCESS == dwResult)
  1547. {
  1548. DWORD dwType;
  1549. DWORD dwSize = ARRAYSIZE(szRegValName);
  1550. dwResult = RegEnumValue(hkey,
  1551. dwIndex++,
  1552. szRegValName,
  1553. &dwSize,
  1554. NULL,
  1555. &dwType,
  1556. NULL,
  1557. NULL);
  1558. if (ERROR_SUCCESS == dwResult)
  1559. {
  1560. //
  1561. // We are interested in DWORD values only.
  1562. //
  1563. if (REG_DWORD == dwType)
  1564. {
  1565. //
  1566. // Normalize the value name to create the 'key'
  1567. // string then cache the pair.
  1568. //
  1569. TCHAR szRegValueNameNormalized[MAX_CPL_EXEC_NAME];
  1570. dwResult = TW32(_NormalizeCplSpec(szRegValName,
  1571. szRegValueNameNormalized,
  1572. ARRAYSIZE(szRegValueNameNormalized)));
  1573. if (ERROR_SUCCESS == dwResult)
  1574. {
  1575. dwResult = TW32(_CacheExtPropRegValName(szRegValueNameNormalized,
  1576. szRegValName));
  1577. }
  1578. else if (ERROR_INVALID_NAME == dwResult)
  1579. {
  1580. //
  1581. // If the path read from the registry is invalid,
  1582. // _NormalizeCplSpec will return ERROR_INVALID_NAME.
  1583. // This value is originally returned by GetShortPathName().
  1584. // We don't want an invalid path in one reg entry
  1585. // to prevent the caching of subsequent valid paths so
  1586. // we convert this error value to ERROR_SUCCESS.
  1587. //
  1588. dwResult = ERROR_SUCCESS;
  1589. }
  1590. }
  1591. }
  1592. }
  1593. if (ERROR_NO_MORE_ITEMS == dwResult)
  1594. {
  1595. dwResult = ERROR_SUCCESS;
  1596. }
  1597. }
  1598. return TW32(dwResult);
  1599. }
  1600. //
  1601. // Insert an entry into the 'extended properties' reg value name
  1602. // cache.
  1603. //
  1604. DWORD
  1605. CControlPanelFolder::_CacheExtPropRegValName(
  1606. LPCTSTR pszRegValNameNormalized,
  1607. LPCTSTR pszRegValName
  1608. )
  1609. {
  1610. ASSERT(NULL != _hdsaExtPropRegVals);
  1611. ASSERT(NULL != pszRegValNameNormalized);
  1612. ASSERT(NULL != pszRegValName);
  1613. DWORD dwResult = ERROR_SUCCESS;
  1614. EPRV_CACHE_ENTRY entry = { NULL, NULL };
  1615. if (FAILED(SHStrDup(pszRegValNameNormalized, &entry.pszRegValNameNormalized)) ||
  1616. FAILED(SHStrDup(pszRegValName, &entry.pszRegValName)) ||
  1617. (-1 == DSA_AppendItem(_hdsaExtPropRegVals, &entry)))
  1618. {
  1619. _DestroyExtPropsRegValEntry(&entry, NULL);
  1620. dwResult = ERROR_OUTOFMEMORY;
  1621. }
  1622. return TW32(dwResult);
  1623. }
  1624. //
  1625. // Destroy the contents of a EPRV_CACHE_ENTRY structure.
  1626. // This is used by DSA_DestroyCallback when the cache is
  1627. // destroyed.
  1628. //
  1629. int CALLBACK
  1630. CControlPanelFolder::_DestroyExtPropsRegValEntry( // [static]
  1631. void *p,
  1632. void *pData
  1633. )
  1634. {
  1635. EPRV_CACHE_ENTRY *pEntry = (EPRV_CACHE_ENTRY *)p;
  1636. ASSERT(NULL != pEntry);
  1637. //
  1638. // Checks for NULL are necessary as we also call this directly
  1639. // from _CacheExtPropRegValName in the case of a failure to
  1640. // add the entry to the cache.
  1641. //
  1642. if (NULL != pEntry->pszRegValName)
  1643. {
  1644. SHFree(pEntry->pszRegValName);
  1645. pEntry->pszRegValName = NULL;
  1646. }
  1647. if (NULL != pEntry->pszRegValNameNormalized)
  1648. {
  1649. SHFree(pEntry->pszRegValNameNormalized);
  1650. pEntry->pszRegValNameNormalized = NULL;
  1651. }
  1652. return 1;
  1653. }
  1654. //
  1655. // Given a CPL applet spec consisting of a path and an optional
  1656. // argument, this function returns the spec with the following:
  1657. //
  1658. // 1. Expands all embedded environment variables.
  1659. // 2. Shortens all LFN path strings to their SFN equivalents.
  1660. // 3. Removes leading and trailing whitespace from the path.
  1661. // 4. Removes leading and trailing whitespace from the arguments.
  1662. //
  1663. DWORD
  1664. CControlPanelFolder::_NormalizeCplSpec(
  1665. LPTSTR pszSpecIn,
  1666. LPTSTR pszSpecOut,
  1667. UINT cchSpecOut
  1668. )
  1669. {
  1670. ASSERT(NULL != pszSpecIn);
  1671. ASSERT(!IsBadWritePtr(pszSpecIn, sizeof(*pszSpecIn) * lstrlen(pszSpecIn) + 1));
  1672. ASSERT(NULL != pszSpecOut);
  1673. ASSERT(!IsBadWritePtr(pszSpecOut, cchSpecOut * sizeof(*pszSpecOut)));
  1674. //
  1675. // Temporarily truncate the spec at the end of the path part
  1676. // if the spec contains trialing arguments (i.e. an applet name in
  1677. // a multi-applet CPL). This is why the pszSpecIn argument can't be
  1678. // constant. I don't want to create a temporary buffer just for this
  1679. // so we modify the input string.
  1680. //
  1681. LPTSTR pszArgs = StrChr(pszSpecIn, TEXT(','));
  1682. if (NULL != pszArgs)
  1683. {
  1684. *pszArgs = 0;
  1685. }
  1686. DWORD dwResult = TW32(_NormalizePath(pszSpecIn, pszSpecOut, cchSpecOut));
  1687. if (NULL != pszArgs)
  1688. {
  1689. //
  1690. // Quick, put the comma back before anyone notices.
  1691. //
  1692. *pszArgs = TEXT(',');
  1693. }
  1694. if (ERROR_SUCCESS == dwResult)
  1695. {
  1696. //
  1697. // The name contained a comma so we need to copy it and the
  1698. // trailing argument to the output buffer.
  1699. //
  1700. if (NULL != pszArgs)
  1701. {
  1702. const UINT cchPath = lstrlen(pszSpecOut);
  1703. const UINT cchArgs = lstrlen(pszArgs);
  1704. HRESULT hr = StringCchCopy(pszSpecOut + cchPath,
  1705. cchSpecOut - cchPath,
  1706. pszArgs);
  1707. if (SUCCEEDED(hr))
  1708. {
  1709. //
  1710. // Trim leading and trailing whitespace around the argument(s).
  1711. // This will convert ", keyboard " to ",keyboard".
  1712. //
  1713. _TrimSpaces(pszSpecOut + cchPath + 1);
  1714. }
  1715. else
  1716. {
  1717. dwResult = TW32(HRESULT_CODE(hr));
  1718. }
  1719. }
  1720. }
  1721. return TW32(dwResult);
  1722. }
  1723. //
  1724. // Wraps _NormalizePathWorker to handle the possible stack overflow
  1725. // exception generated by the use of _alloca().
  1726. //
  1727. DWORD
  1728. CControlPanelFolder::_NormalizePath(
  1729. LPCTSTR pszPathIn,
  1730. LPTSTR pszPathOut,
  1731. UINT cchPathOut
  1732. )
  1733. {
  1734. //
  1735. // NormalizePathWorker uses _alloca() to allocate a stack buffer.
  1736. // Need to handle the case where the stack is all used up. Unlikely
  1737. // but it could happen.
  1738. //
  1739. DWORD dwResult;
  1740. __try
  1741. {
  1742. dwResult = TW32(_NormalizePathWorker(pszPathIn, pszPathOut, cchPathOut));
  1743. }
  1744. __except(_FilterStackOverflow(GetExceptionCode()))
  1745. {
  1746. dwResult = TW32(ERROR_STACK_OVERFLOW);
  1747. }
  1748. return TW32(dwResult);
  1749. }
  1750. INT
  1751. CControlPanelFolder::_FilterStackOverflow( // [static]
  1752. INT nException
  1753. )
  1754. {
  1755. if (STATUS_STACK_OVERFLOW == nException)
  1756. {
  1757. return EXCEPTION_EXECUTE_HANDLER;
  1758. }
  1759. return EXCEPTION_CONTINUE_SEARCH;
  1760. }
  1761. //
  1762. // Given a path string, this function expands all environment variables and
  1763. // converts all LFN strings to SFN strings. This looks like a large function.
  1764. // It's really just one call to ExpandEnvironmentStrings() and one call to
  1765. // GetShortPathName() wrapped with some extra housekeeping.
  1766. //
  1767. DWORD
  1768. CControlPanelFolder::_NormalizePathWorker(
  1769. LPCTSTR pszPathIn,
  1770. LPTSTR pszPathOut,
  1771. UINT cchPathOut
  1772. )
  1773. {
  1774. ASSERT(NULL != pszPathIn);
  1775. ASSERT(NULL != pszPathOut);
  1776. ASSERT(!IsBadWritePtr(pszPathOut, cchPathOut * sizeof(*pszPathOut)));
  1777. DWORD dwResult = ERROR_SUCCESS;
  1778. //
  1779. // _alloca generates a stack fault exception if insufficient stack space
  1780. // is available. It is not appropriate to check the return value.
  1781. // This function is wrapped by _NormalizePath to handle the exception.
  1782. //
  1783. const DWORD cchTemp = cchPathOut;
  1784. LPTSTR pszTemp = (LPTSTR)_alloca(cchPathOut * sizeof(*pszPathOut));
  1785. //
  1786. // Expand the entire string once to catch any env vars in either the path
  1787. // or in any arguments.
  1788. //
  1789. const DWORD cchExpanded = ExpandEnvironmentStrings(pszPathIn, pszTemp, cchTemp);
  1790. if (0 == cchExpanded)
  1791. {
  1792. dwResult = TW32(GetLastError());
  1793. }
  1794. else if (cchExpanded > cchTemp)
  1795. {
  1796. dwResult = TW32(ERROR_INSUFFICIENT_BUFFER);
  1797. }
  1798. else
  1799. {
  1800. //
  1801. // Trim any leading and trailing spaces.
  1802. //
  1803. _TrimSpaces(pszTemp);
  1804. //
  1805. // Convert the path part to it's short-name equivalent. This allows
  1806. // us to compare a LFN and a SFN that resolve to the same actual file.
  1807. // Note that this will fail if the file doesn't exist.
  1808. // GetShortPath supports the src and dest ptrs referencing the same memory.
  1809. //
  1810. const DWORD cchShort = GetShortPathName(pszTemp, pszPathOut, cchPathOut);
  1811. if (0 == cchShort)
  1812. {
  1813. dwResult = GetLastError();
  1814. if (ERROR_FILE_NOT_FOUND == dwResult ||
  1815. ERROR_PATH_NOT_FOUND == dwResult ||
  1816. ERROR_ACCESS_DENIED == dwResult)
  1817. {
  1818. //
  1819. // File doesn't exist or we don't have access.
  1820. // We can't get the SFN so simply return the path
  1821. // with env vars expanded.
  1822. //
  1823. dwResult = TW32(HRESULT_CODE(StringCchCopy(pszPathOut, cchPathOut, pszTemp)));
  1824. }
  1825. }
  1826. else if (cchShort > cchPathOut)
  1827. {
  1828. //
  1829. // Output buffer is too small to hold expanded SFN string.
  1830. //
  1831. dwResult = TW32(ERROR_INSUFFICIENT_BUFFER);
  1832. }
  1833. }
  1834. return TW32(dwResult);
  1835. }
  1836. //
  1837. // Remove leading and trailing spaces from a text string.
  1838. // Modifies the string in-place.
  1839. //
  1840. void
  1841. CControlPanelFolder::_TrimSpaces(
  1842. LPTSTR psz
  1843. )
  1844. {
  1845. ASSERT(NULL != psz);
  1846. ASSERT(!IsBadWritePtr(psz, sizeof(*psz) * (lstrlen(psz) + 1)));
  1847. LPTSTR pszRead = psz;
  1848. LPTSTR pszWrite = psz;
  1849. //
  1850. // Skip leading spaces.
  1851. //
  1852. while(0 != *pszRead && TEXT(' ') == *pszRead)
  1853. {
  1854. ++pszRead;
  1855. }
  1856. //
  1857. // Copy remainder up to the terminating nul.
  1858. //
  1859. LPTSTR pszLastNonSpaceChar = NULL;
  1860. while(0 != *pszRead)
  1861. {
  1862. if (TEXT(' ') != *pszRead)
  1863. {
  1864. pszLastNonSpaceChar = pszWrite;
  1865. }
  1866. *pszWrite++ = *pszRead++;
  1867. }
  1868. if (NULL != pszLastNonSpaceChar)
  1869. {
  1870. //
  1871. // The string contained at least one non-space character.
  1872. // Adjust the 'write' ptr so that we terminate the string
  1873. // immediately after the last one found.
  1874. // This trims trailing spaces.
  1875. //
  1876. ASSERT(TEXT(' ') != *pszLastNonSpaceChar && 0 != *pszLastNonSpaceChar);
  1877. pszWrite = pszLastNonSpaceChar + 1;
  1878. }
  1879. *pszWrite = 0;
  1880. }
  1881. //
  1882. // Method returns a string corresponding to what the registry value name should be
  1883. // for this CPL pidl (pidc). This string format is basically the same as the GetExecName
  1884. // format with the quotation marks stripped, so all we do is skip the first
  1885. // two " marks in the GetExecName string
  1886. //
  1887. // String in GetExecName format: "C:\WINNT\System32\main.cpl",keyboard
  1888. // String in registry format : C:\WINNT\System32\main.cpl,keyboard
  1889. //
  1890. //
  1891. HRESULT CControlPanelFolder::_GetFullCPLName(LPIDCONTROL pidc, LPTSTR achFullCPLName, UINT cchSize)
  1892. {
  1893. const TCHAR QUOTE = TEXT('\"');
  1894. HRESULT hr = GetExecName(pidc, achFullCPLName,cchSize);
  1895. if (SUCCEEDED(hr))
  1896. {
  1897. // first char must be a quote
  1898. ASSERTMSG ((QUOTE == *achFullCPLName), "CControlPanelFolder::_GetFullCPLName() GetExecName returned an invalid value");
  1899. if (QUOTE == *achFullCPLName) // I know we asserted, just being super paranoid
  1900. {
  1901. LPTSTR pszWrite = achFullCPLName;
  1902. LPCTSTR pszRead = achFullCPLName;
  1903. int cQuotes = 2; // we want to skip the first two quotes only
  1904. while(*pszRead)
  1905. {
  1906. if (0 < cQuotes && QUOTE == *pszRead)
  1907. {
  1908. --cQuotes;
  1909. ++pszRead;
  1910. }
  1911. else
  1912. {
  1913. *pszWrite++ = *pszRead++;
  1914. }
  1915. }
  1916. *pszWrite = TEXT('\0');
  1917. }
  1918. }
  1919. return hr;
  1920. }
  1921. STDMETHODIMP CControlPanelFolder::GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS* pDetails)
  1922. {
  1923. HRESULT hr = E_INVALIDARG;
  1924. if (pidl == NULL)
  1925. {
  1926. hr = GetDetailsOfInfo(c_cpl_cols, ARRAYSIZE(c_cpl_cols), iColumn, pDetails);
  1927. }
  1928. else
  1929. {
  1930. LPIDCONTROL pidc = _IsValid(pidl);
  1931. if (pidc)
  1932. {
  1933. TCHAR szTemp[max(max(MAX_PATH, MAX_CCH_CPLNAME), MAX_CCH_CPLINFO)];
  1934. pDetails->str.uType = STRRET_CSTR;
  1935. pDetails->str.cStr[0] = 0;
  1936. switch (iColumn)
  1937. {
  1938. case CPL_ICOL_NAME:
  1939. GetDisplayName(pidc, szTemp, ARRAYSIZE(szTemp));
  1940. break;
  1941. case CPL_ICOL_COMMENT:
  1942. _GetDescription(pidc, szTemp, ARRAYSIZE(szTemp));
  1943. break;
  1944. default:
  1945. szTemp[0] = 0;
  1946. break;
  1947. }
  1948. hr = StringToStrRet(szTemp, &pDetails->str);
  1949. }
  1950. }
  1951. return hr;
  1952. }
  1953. STDMETHODIMP CControlPanelFolder::MapColumnToSCID(UINT iColumn, SHCOLUMNID* pscid)
  1954. {
  1955. return MapColumnToSCIDImpl(c_cpl_cols, ARRAYSIZE(c_cpl_cols), iColumn, pscid);
  1956. }
  1957. STDMETHODIMP CControlPanelFolder::GetClassID(CLSID* pCLSID)
  1958. {
  1959. *pCLSID = CLSID_ControlPanel;
  1960. return S_OK;
  1961. }
  1962. STDMETHODIMP CControlPanelFolder::Initialize(LPCITEMIDLIST pidl)
  1963. {
  1964. if (NULL != _pidl)
  1965. {
  1966. ILFree(_pidl);
  1967. _pidl = NULL;
  1968. }
  1969. return SHILClone(pidl, &_pidl);
  1970. }
  1971. STDMETHODIMP CControlPanelFolder::GetCurFolder(LPITEMIDLIST* ppidl)
  1972. {
  1973. return GetCurFolderImpl(_pidl, ppidl);
  1974. }
  1975. //
  1976. // list of item context menu callback
  1977. //
  1978. HRESULT CALLBACK CControlPanelFolder::DFMCallBack(IShellFolder *psf, HWND hwndView,
  1979. IDataObject *pdtobj, UINT uMsg,
  1980. WPARAM wParam, LPARAM lParam)
  1981. {
  1982. HRESULT hr = E_NOTIMPL;
  1983. if (pdtobj)
  1984. {
  1985. STGMEDIUM medium;
  1986. LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
  1987. if (pida)
  1988. {
  1989. hr = S_OK;
  1990. switch(uMsg)
  1991. {
  1992. case DFM_MERGECONTEXTMENU:
  1993. {
  1994. LPQCMINFO pqcm = (LPQCMINFO)lParam;
  1995. int idCmdFirst = pqcm->idCmdFirst;
  1996. if (wParam & CMF_EXTENDEDVERBS)
  1997. {
  1998. // If the user is holding down shift, on NT5 we load the menu with both "Open" and "Run as..."
  1999. CDefFolderMenu_MergeMenu(HINST_THISDLL, MENU_GENERIC_CONTROLPANEL_VERBS, 0, pqcm);
  2000. }
  2001. else
  2002. {
  2003. // Just load the "Open" menu
  2004. CDefFolderMenu_MergeMenu(HINST_THISDLL, MENU_GENERIC_OPEN_VERBS, 0, pqcm);
  2005. }
  2006. SetMenuDefaultItem(pqcm->hmenu, 0, MF_BYPOSITION);
  2007. //
  2008. // Returning S_FALSE indicates no need to get verbs from
  2009. // extensions.
  2010. //
  2011. hr = S_FALSE;
  2012. break;
  2013. } // case DFM_MERGECONTEXTMENU
  2014. case DFM_GETHELPTEXT:
  2015. LoadStringA(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPSTR)lParam, HIWORD(wParam));;
  2016. break;
  2017. case DFM_GETHELPTEXTW:
  2018. LoadStringW(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPWSTR)lParam, HIWORD(wParam));;
  2019. break;
  2020. case DFM_INVOKECOMMAND:
  2021. {
  2022. for (int i = pida->cidl - 1; i >= 0; i--)
  2023. {
  2024. LPIDCONTROL pidc = _IsValid(IDA_GetIDListPtr(pida, i));
  2025. if (pidc)
  2026. {
  2027. switch(wParam)
  2028. {
  2029. case FSIDM_OPENPRN:
  2030. case FSIDM_RUNAS:
  2031. {
  2032. TCHAR achParam[MAX_CPL_EXEC_NAME]; // See wnsprintf in GetExecName
  2033. hr = GetExecName(pidc, achParam, ARRAYSIZE(achParam));
  2034. if (SUCCEEDED(hr))
  2035. {
  2036. SHRunControlPanelEx(achParam, hwndView, (wParam == FSIDM_RUNAS));
  2037. hr = S_OK;
  2038. }
  2039. break;
  2040. }
  2041. default:
  2042. hr = S_FALSE;
  2043. } // switch(wParam)
  2044. }
  2045. else
  2046. hr = E_FAIL;
  2047. }
  2048. }
  2049. break;
  2050. case DFM_MAPCOMMANDNAME:
  2051. if (lstrcmpi((LPCTSTR)lParam, c_szOpen) == 0)
  2052. {
  2053. *(UINT_PTR *)wParam = FSIDM_OPENPRN;
  2054. }
  2055. else
  2056. {
  2057. // command not found
  2058. hr = E_FAIL;
  2059. }
  2060. break;
  2061. default:
  2062. hr = E_NOTIMPL;
  2063. break;
  2064. } // switch (uMsg)
  2065. HIDA_ReleaseStgMedium(pida, &medium);
  2066. } // if (pida)
  2067. } // if (pdtobj)
  2068. return hr;
  2069. }
  2070. HRESULT MakeCPLCommandLine(LPCTSTR pszModule, LPCTSTR pszName, LPTSTR pszCommandLine, DWORD cchCommandLine)
  2071. {
  2072. RIP(pszCommandLine);
  2073. RIP(pszModule);
  2074. RIP(pszName);
  2075. return StringCchPrintf(pszCommandLine, cchCommandLine, TEXT("\"%s\",%s"), pszModule, pszName);
  2076. }
  2077. HRESULT CControlPanelFolder::GetExecName(LPIDCONTROL pidc, LPTSTR pszParseName, UINT cchParseName)
  2078. {
  2079. TCHAR szModule[MAX_PATH], szName[MAX_CCH_CPLNAME];
  2080. HRESULT hr = GetModuleMapped(pidc, szModule, ARRAYSIZE(szModule), NULL, szName, ARRAYSIZE(szName));
  2081. if (SUCCEEDED(hr))
  2082. {
  2083. // If our GetModuleMapped call didn't override the applet name, get it the old fashioned way
  2084. if (*szName == 0)
  2085. {
  2086. hr = GetDisplayName(pidc, szName, ARRAYSIZE(szName));
  2087. }
  2088. if (SUCCEEDED(hr))
  2089. {
  2090. hr = MakeCPLCommandLine(szModule, szName, pszParseName, cchParseName);
  2091. }
  2092. }
  2093. return hr;
  2094. }
  2095. typedef struct _OLDCPLMAPPING
  2096. {
  2097. LPCTSTR szOldModule;
  2098. UINT idOldIcon;
  2099. LPCTSTR szNewModule;
  2100. UINT idNewIcon;
  2101. LPCTSTR szApplet;
  2102. // Put TEXT("") in szApplet to use the applet name stored in the cpl shortcut
  2103. } OLDCPLMAPPING, *LPOLDCPLMAPPING;
  2104. const OLDCPLMAPPING g_rgOldCPLMapping[] =
  2105. {
  2106. // Win95 shortcuts that don't work correctly
  2107. // -----------------------------------------
  2108. // Add New Hardware
  2109. {TEXT("SYSDM.CPL"), 0xfffffda6, TEXT("HDWWIZ.CPL"), (UINT) -100, TEXT("@0")},
  2110. // ODBC 32 bit
  2111. {TEXT("ODBCCP32.CPL"), 0xfffffa61, TEXT("ODBCCP32.CPL"), 0xfffffa61, TEXT("@0")},
  2112. // Mail
  2113. {TEXT("MLCFG32.CPL"), 0xffffff7f, TEXT("MLCFG32.CPL"), 0xffffff7f, TEXT("@0")},
  2114. // Modem
  2115. {TEXT("MODEM.CPL"), 0xfffffc18, TEXT("TELEPHON.CPL"), (UINT) -100, TEXT("")},
  2116. // Multimedia
  2117. {TEXT("MMSYS.CPL"), 0xffffff9d, TEXT("MMSYS.CPL"), (UINT) -110, TEXT("")},
  2118. // Network
  2119. {TEXT("NETCPL.CPL"), 0xffffff9c, TEXT("NCPA.CPL"), 0xfffffc17, TEXT("@0")},
  2120. // Password
  2121. {TEXT("PASSWORD.CPL"), 0xfffffc18, TEXT("PASSWORD.CPL"), 0xfffffc18, TEXT("@0")},
  2122. // Regional Settings
  2123. {TEXT("INTL.CPL"), 0xffffff9b, TEXT("INTL.CPL"), (UINT) -200, TEXT("@0")},
  2124. // System
  2125. {TEXT("SYSDM.CPL"), 0xfffffda8, TEXT("SYSDM.CPL"), (UINT) -6, TEXT("")},
  2126. // Users
  2127. {TEXT("INETCPL.CPL"), 0xfffffad5, TEXT("INETCPL.CPL"), 0xfffffad5, TEXT("@0")},
  2128. // NT4 Shortcuts that don't work
  2129. // -----------------------------
  2130. // Multimedia
  2131. {TEXT("MMSYS.CPL"), 0xfffff444, TEXT("MMSYS.CPL"), 0xfffff444, TEXT("@0")},
  2132. // Network
  2133. {TEXT("NCPA.CPL"), 0xfffffc17, TEXT("NCPA.CPL"), 0xfffffc17, TEXT("@0")},
  2134. // UPS
  2135. {TEXT("UPS.CPL"), 0xffffff9c, TEXT("POWERCFG.CPL"), (UINT) -202, TEXT("@0")},
  2136. // Synonyms for hardware management
  2137. // Devices
  2138. {TEXT("SRVMGR.CPL"), 0xffffff67, TEXT("HDWWIZ.CPL"), (UINT) -100, TEXT("@0")},
  2139. // Ports
  2140. {TEXT("PORTS.CPL"), 0xfffffffe, TEXT("HDWWIZ.CPL"), (UINT) -100, TEXT("@0")},
  2141. // SCSI Adapters
  2142. {TEXT("DEVAPPS.CPL"), 0xffffff52, TEXT("HDWWIZ.CPL"), (UINT) -100, TEXT("@0")},
  2143. // Tape Devices
  2144. {TEXT("DEVAPPS.CPL"), 0xffffff97, TEXT("HDWWIZ.CPL"), (UINT) -100, TEXT("@0")},
  2145. };
  2146. HRESULT CControlPanelFolder::GetModuleMapped(LPIDCONTROL pidc, LPTSTR pszModule, UINT cchModule,
  2147. UINT* pidNewIcon, LPTSTR pszApplet, UINT cchApplet)
  2148. {
  2149. HRESULT hr = GetModule(pidc, pszModule, cchModule);
  2150. if (SUCCEEDED(hr))
  2151. {
  2152. hr = S_FALSE;
  2153. // Compare just the .cpl file name, not the full path: Get this file name from the full path
  2154. LPTSTR pszFilename = PathFindFileName(pszModule);
  2155. // Calculate the size of the buffer available for the filename
  2156. UINT cchFilenameBuffer = cchModule - (UINT)(pszFilename - pszModule);
  2157. if (((int) pidc->idIcon <= 0) && (pszFilename))
  2158. {
  2159. for (int i = 0; i < ARRAYSIZE(g_rgOldCPLMapping); i++)
  2160. {
  2161. // See if the module names and old icon IDs match those in this
  2162. // entry of our mapping
  2163. if (((UINT) pidc->idIcon == g_rgOldCPLMapping[i].idOldIcon) &&
  2164. (lstrcmpi(pszFilename, g_rgOldCPLMapping[i].szOldModule) == 0))
  2165. {
  2166. hr = S_OK;
  2167. // Set the return values to those of the found item
  2168. if (pidNewIcon != NULL)
  2169. *pidNewIcon = g_rgOldCPLMapping[i].idNewIcon;
  2170. hr = StringCchCopy(pszFilename, cchFilenameBuffer, g_rgOldCPLMapping[i].szNewModule);
  2171. if (SUCCEEDED(hr))
  2172. {
  2173. if (pszApplet != NULL)
  2174. {
  2175. hr = StringCchCopy(pszApplet, cchApplet, g_rgOldCPLMapping[i].szApplet);
  2176. }
  2177. }
  2178. break;
  2179. }
  2180. }
  2181. }
  2182. // Return old values if we didn't require a translation
  2183. if (S_OK != hr)
  2184. {
  2185. if (pidNewIcon != NULL)
  2186. *pidNewIcon = pidc->idIcon;
  2187. if (pszApplet != NULL)
  2188. *pszApplet = 0; //NULL String
  2189. }
  2190. if (SUCCEEDED(hr))
  2191. {
  2192. // If the .cpl file can't be found, this may be a Win95 shortcut specifying
  2193. // the old system directory - possibly an upgraded system. We try to make
  2194. // this work by changing the directory specified to the actual system
  2195. // directory. For example c:\windows\system\foo.cpl will become
  2196. // c:\winnt\system32\foo.cpl.
  2197. //
  2198. // Note: The path substitution is done unconditionally because if we
  2199. // can't find the file it doesn't matter where we can't find it...
  2200. if (!PathFileExists(pszModule))
  2201. {
  2202. TCHAR szNew[MAX_PATH], szSystem[MAX_PATH];
  2203. GetSystemDirectory(szSystem, ARRAYSIZE(szSystem));
  2204. if (PathCombine(szNew, szSystem, pszFilename))
  2205. {
  2206. hr = StringCchCopy(pszModule, cchModule, szNew);
  2207. }
  2208. else
  2209. {
  2210. hr = E_FAIL;
  2211. }
  2212. }
  2213. }
  2214. }
  2215. return hr;
  2216. }
  2217. //
  2218. // SHualUnicodeToTChar is like SHUnicodeToTChar except that it accepts
  2219. // an unaligned input string parameter.
  2220. //
  2221. #ifdef UNICODE
  2222. #define SHualUnicodeToTChar(src, dst, cch) ualstrcpyn(dst, src, cch)
  2223. #else // No ANSI platforms require alignment
  2224. #define SHualUnicodeToTChar SHUnicodeToTChar
  2225. #endif
  2226. HRESULT CControlPanelFolder::GetDisplayName(LPIDCONTROL pidc, LPTSTR pszName, UINT cchName)
  2227. {
  2228. LPIDCONTROLW pidcW = _IsUnicodeCPL(pidc);
  2229. if (pidcW)
  2230. SHualUnicodeToTChar(pidcW->cBufW + pidcW->oNameW, pszName, cchName);
  2231. else
  2232. SHAnsiToTChar(pidc->cBuf + pidc->oName, pszName, cchName);
  2233. return S_OK;
  2234. }
  2235. HRESULT CControlPanelFolder::GetModule(LPIDCONTROL pidc, LPTSTR pszModule, UINT cchModule)
  2236. {
  2237. LPIDCONTROLW pidcW = _IsUnicodeCPL(pidc);
  2238. if (pidcW)
  2239. {
  2240. if (!SHualUnicodeToTChar(pidcW->cBufW, pszModule, cchModule))
  2241. {
  2242. *pszModule = TEXT('\0');
  2243. }
  2244. }
  2245. else
  2246. {
  2247. if (!SHAnsiToTChar(pidc->cBuf, pszModule, cchModule))
  2248. {
  2249. *pszModule = TEXT('\0');
  2250. }
  2251. }
  2252. return S_OK;
  2253. }
  2254. HRESULT CControlPanelFolder::_GetDescription(LPIDCONTROL pidc, LPTSTR pszDesc, UINT cchDesc)
  2255. {
  2256. LPIDCONTROLW pidcW = _IsUnicodeCPL(pidc);
  2257. if (pidcW)
  2258. SHualUnicodeToTChar(pidcW->cBufW + pidcW->oInfoW, pszDesc, cchDesc);
  2259. else
  2260. SHAnsiToTChar(pidc->cBuf + pidc->oInfo, pszDesc, cchDesc);
  2261. return S_OK;
  2262. }
  2263. //
  2264. // Method opens a subkey corresponding to a SCID under ExtendedPoperties for control panel
  2265. //
  2266. HRESULT CControlPanelFolder::_GetExtPropsKey(HKEY hkeyParent, HKEY * pHkey, const SHCOLUMNID * pscid)
  2267. {
  2268. const TCHAR c_szRegPath[] = TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Control Panel\\Extended Properties\\");
  2269. const UINT cchRegPath = ARRAYSIZE (c_szRegPath);
  2270. TCHAR achPath[cchRegPath + SCIDSTR_MAX];
  2271. HRESULT hr = StringCchCopy(achPath, ARRAYSIZE(achPath), c_szRegPath);
  2272. if (SUCCEEDED(hr))
  2273. {
  2274. ASSERT (hkeyParent);
  2275. hr = S_FALSE;
  2276. if (0 < StringFromSCID(pscid, achPath + cchRegPath - 1, ARRAYSIZE(achPath) - cchRegPath))
  2277. {
  2278. DWORD dwResult = RegOpenKeyEx(hkeyParent,
  2279. achPath,
  2280. 0,
  2281. KEY_QUERY_VALUE,
  2282. pHkey);
  2283. if (ERROR_SUCCESS == dwResult)
  2284. {
  2285. hr = S_OK;
  2286. }
  2287. else
  2288. {
  2289. hr = HRESULT_FROM_WIN32(dwResult);
  2290. }
  2291. }
  2292. }
  2293. return hr;
  2294. }
  2295. #undef SHualUnicodeToTChar
  2296. CControlPanelEnum::CControlPanelEnum(UINT uFlags) :
  2297. _cRef (1),
  2298. _uFlags (uFlags),
  2299. _iModuleCur (0),
  2300. _cControlsOfCurrentModule (0),
  2301. _iControlCur (0),
  2302. _cControlsTotal (0),
  2303. _iRegControls (0)
  2304. {
  2305. ZeroMemory(&_minstCur, sizeof(_minstCur));
  2306. ZeroMemory(&_cplData, sizeof(_cplData));
  2307. }
  2308. CControlPanelEnum::~CControlPanelEnum()
  2309. {
  2310. CPLD_Destroy(&_cplData);
  2311. }
  2312. HRESULT CControlPanelEnum::Init()
  2313. {
  2314. HRESULT hr;
  2315. if (CPLD_GetModules(&_cplData))
  2316. {
  2317. CPLD_GetRegModules(&_cplData);
  2318. hr = S_OK;
  2319. }
  2320. else
  2321. {
  2322. hr = E_FAIL;
  2323. }
  2324. return hr;
  2325. }
  2326. STDMETHODIMP CControlPanelEnum::QueryInterface(REFIID riid, void **ppv)
  2327. {
  2328. static const QITAB qit[] = {
  2329. QITABENT(CControlPanelEnum, IEnumIDList),
  2330. { 0 }
  2331. };
  2332. return QISearch(this, qit, riid, ppv);
  2333. }
  2334. STDMETHODIMP_(ULONG) CControlPanelEnum::AddRef()
  2335. {
  2336. return InterlockedIncrement(&_cRef);
  2337. }
  2338. STDMETHODIMP_(ULONG) CControlPanelEnum::Release()
  2339. {
  2340. ASSERT( 0 != _cRef );
  2341. ULONG cRef = InterlockedDecrement(&_cRef);
  2342. if ( 0 == cRef )
  2343. {
  2344. delete this;
  2345. }
  2346. return cRef;
  2347. }
  2348. // for other ways to hide CPLs see control1.c, DontLoadCPL()
  2349. BOOL CControlPanelEnum::_DoesPolicyAllow(LPCTSTR pszName, LPCTSTR pszFileName)
  2350. {
  2351. BOOL bAllow = TRUE;
  2352. if (SHRestricted(REST_RESTRICTCPL) &&
  2353. !IsNameListedUnderKey(pszName, REGSTR_POLICIES_RESTRICTCPL) &&
  2354. !IsNameListedUnderKey(pszFileName, REGSTR_POLICIES_RESTRICTCPL))
  2355. {
  2356. bAllow = FALSE;
  2357. }
  2358. if (bAllow)
  2359. {
  2360. if (SHRestricted(REST_DISALLOWCPL) &&
  2361. (IsNameListedUnderKey(pszName, REGSTR_POLICIES_DISALLOWCPL) ||
  2362. IsNameListedUnderKey(pszFileName, REGSTR_POLICIES_DISALLOWCPL)))
  2363. {
  2364. bAllow = FALSE;
  2365. }
  2366. }
  2367. return bAllow;
  2368. }
  2369. STDMETHODIMP CControlPanelEnum::Next(ULONG celt, LPITEMIDLIST* ppidlOut, ULONG* pceltFetched)
  2370. {
  2371. ZeroMemory(ppidlOut, sizeof(ppidlOut[0])*celt);
  2372. if (pceltFetched)
  2373. *pceltFetched = 0;
  2374. if (!(_uFlags & SHCONTF_NONFOLDERS))
  2375. return S_FALSE;
  2376. // Loop through lpData->pRegCPLs and use what cached information we can.
  2377. while (_iRegControls < _cplData.cRegCPLs)
  2378. {
  2379. REG_CPL_INFO *pRegCPL = (REG_CPL_INFO *) DPA_GetPtr(_cplData.hRegCPLs, _iRegControls);
  2380. PMODULEINFO pmi;
  2381. TCHAR szFilePath[MAX_PATH];
  2382. StringCchCopy(szFilePath, ARRAYSIZE(szFilePath), REGCPL_FILENAME(pRegCPL));
  2383. LPTSTR pszFileName = PathFindFileName(szFilePath);
  2384. // find this module in the hamiModule list
  2385. for (int i = 0; i < _cplData.cModules; i++)
  2386. {
  2387. pmi = (PMODULEINFO) DSA_GetItemPtr(_cplData.hamiModule, i);
  2388. if (!lstrcmpi(pszFileName, pmi->pszModuleName))
  2389. break;
  2390. }
  2391. if (i < _cplData.cModules)
  2392. {
  2393. LPCTSTR pszDisplayName = REGCPL_CPLNAME(pRegCPL);
  2394. // If this cpl is not supposed to be displayed let's bail
  2395. if (!_DoesPolicyAllow(pszDisplayName, pszFileName))
  2396. {
  2397. _iRegControls++;
  2398. // we have to set this bit, so that the cpl doesn't get reregistered
  2399. pmi->flags |= MI_REG_ENUM;
  2400. continue;
  2401. }
  2402. // Get the module's creation time & size
  2403. if (!(pmi->flags & MI_FIND_FILE))
  2404. {
  2405. WIN32_FIND_DATA findData;
  2406. HANDLE hFindFile = FindFirstFile(pmi->pszModule, &findData);
  2407. if (hFindFile != INVALID_HANDLE_VALUE)
  2408. {
  2409. pmi->flags |= MI_FIND_FILE;
  2410. pmi->ftCreationTime = findData.ftCreationTime;
  2411. pmi->nFileSizeHigh = findData.nFileSizeHigh;
  2412. pmi->nFileSizeLow = findData.nFileSizeLow;
  2413. FindClose(hFindFile);
  2414. }
  2415. else
  2416. {
  2417. // this module no longer exists... Blow it away.
  2418. DebugMsg(DM_TRACE,TEXT("sh CPLS: very stange, couldn't get timestamps for %s"), REGCPL_FILENAME(pRegCPL));
  2419. goto RemoveRegCPL;
  2420. }
  2421. }
  2422. if (0 != CompareFileTime(&pmi->ftCreationTime, &pRegCPL->ftCreationTime) ||
  2423. pmi->nFileSizeHigh != pRegCPL->nFileSizeHigh ||
  2424. pmi->nFileSizeLow != pRegCPL->nFileSizeLow)
  2425. {
  2426. // this doesn't match -- remove it from pRegCPLs; it will
  2427. // get enumerated below.
  2428. DebugMsg(DM_TRACE,TEXT("sh CPLS: timestamps don't match for %s"), REGCPL_FILENAME(pRegCPL));
  2429. pmi->flags |= MI_REG_INVALID;
  2430. goto RemoveRegCPL;
  2431. }
  2432. // we have a match: mark this module so we skip it below
  2433. // and enumerate this cpl now
  2434. pmi->flags |= MI_REG_ENUM;
  2435. IDControlCreate(pmi->pszModule, EIRESID(pRegCPL->idIcon), REGCPL_CPLNAME(pRegCPL), REGCPL_CPLINFO(pRegCPL), ppidlOut);
  2436. _iRegControls++;
  2437. goto return_item;
  2438. }
  2439. else
  2440. {
  2441. DebugMsg(DM_TRACE,TEXT("sh CPLS: %s not in module list!"), REGCPL_FILENAME(pRegCPL));
  2442. }
  2443. RemoveRegCPL:
  2444. // Nuke this cpl entry from the registry
  2445. if (!(pRegCPL->flags & REGCPL_FROMREG))
  2446. LocalFree(pRegCPL);
  2447. DPA_DeletePtr(_cplData.hRegCPLs, _iRegControls);
  2448. _cplData.cRegCPLs--;
  2449. _cplData.fRegCPLChanged = TRUE;
  2450. }
  2451. // Have we enumerated all the cpls in this module?
  2452. LPCPLMODULE pcplm;
  2453. LPCPLITEM pcpli;
  2454. do
  2455. {
  2456. while (_iControlCur >= _cControlsOfCurrentModule || // no more
  2457. _cControlsOfCurrentModule < 0) // error getting modules
  2458. {
  2459. // Have we enumerated all the modules?
  2460. if (_iModuleCur >= _cplData.cModules)
  2461. {
  2462. CPLD_FlushRegModules(&_cplData); // flush changes for next guy
  2463. return S_FALSE;
  2464. }
  2465. // Was this module enumerated from the registry?
  2466. // Was the cached registry information found to be valid?
  2467. PMODULEINFO pmi = (PMODULEINFO) DSA_GetItemPtr(_cplData.hamiModule, _iModuleCur);
  2468. if (!(pmi->flags & MI_REG_ENUM) || (pmi->flags & MI_REG_INVALID))
  2469. {
  2470. // No. Load and init the module, set up counters.
  2471. pmi->flags |= MI_CPL_LOADED;
  2472. _cControlsOfCurrentModule = CPLD_InitModule(&_cplData, _iModuleCur, &_minstCur);
  2473. _iControlCur = 0;
  2474. }
  2475. ++_iModuleCur; // Point to next module
  2476. }
  2477. // We're enumerating the next control in this module
  2478. // Add the control to the registry
  2479. EVAL(CPLD_AddControlToReg(&_cplData, &_minstCur, _iControlCur));
  2480. // This shouldn't fail at all; that would mean that DSA_GetItemPtr() failed,
  2481. // and we've already called that successfully.
  2482. // Get the control's pidl name
  2483. pcplm = FindCPLModule(&_minstCur);
  2484. pcpli = (LPCPLITEM) DSA_GetItemPtr(pcplm->hacpli, _iControlCur);
  2485. ++_iControlCur;
  2486. } while (!_DoesPolicyAllow(pcpli->pszName, PathFindFileName(pcplm->szModule)));
  2487. IDControlCreate(pcplm->szModule, EIRESID(pcpli->idIcon), pcpli->pszName, pcpli->pszInfo, ppidlOut);
  2488. return_item:
  2489. HRESULT hr = *ppidlOut ? S_OK : E_OUTOFMEMORY;
  2490. if (SUCCEEDED(hr))
  2491. {
  2492. ++_cControlsTotal;
  2493. if (pceltFetched)
  2494. *pceltFetched = 1;
  2495. }
  2496. return hr;
  2497. }
  2498. STDMETHODIMP CControlPanelEnum::Reset()
  2499. {
  2500. _iModuleCur = 0;
  2501. _cControlsOfCurrentModule = 0;
  2502. _iControlCur = 0;
  2503. _cControlsTotal = 0;
  2504. _iRegControls = 0;
  2505. return S_OK;
  2506. }