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.

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