1 /*
2 * Control panel folder
3 *
4 * Copyright 2003 Martin Fuchs
5 * Copyright 2009 Andrew Hill
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 */
21
22 #include <precomp.h>
23
24 WINE_DEFAULT_DEBUG_CHANNEL(shell);
25
26 static const REGFOLDERINFO g_RegFolderInfo =
27 {
28 PT_CONTROLS_NEWREGITEM,
29 0, NULL,
30 CLSID_ControlPanel,
31 L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}",
32 L"ControlPanel",
33 };
34
35 /***********************************************************************
36 * control panel implementation in shell namespace
37 */
38
39 class CControlPanelEnum :
40 public CEnumIDListBase
41 {
42 public:
43 CControlPanelEnum();
44 ~CControlPanelEnum();
45 HRESULT WINAPI Initialize(DWORD dwFlags, IEnumIDList* pRegEnumerator);
46 BOOL RegisterCPanelApp(LPCWSTR path);
47 int RegisterRegistryCPanelApps(HKEY hkey_root, LPCWSTR szRepPath);
48 BOOL CreateCPanelEnumList(DWORD dwFlags);
49
50 BEGIN_COM_MAP(CControlPanelEnum)
51 COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList)
52 END_COM_MAP()
53 };
54
55 /***********************************************************************
56 * IShellFolder [ControlPanel] implementation
57 */
58
59 static const shvheader ControlPanelSFHeader[] = {
60 {IDS_SHV_COLUMN_NAME, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 20},/*FIXME*/
61 {IDS_SHV_COLUMN_COMMENTS, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 80},/*FIXME*/
62 };
63
64 enum controlpanel_columns
65 {
66 CONTROLPANEL_COL_NAME,
67 CONTROLPANEL_COL_COMMENT,
68 CONTROLPANEL_COL_COUNT,
69 };
70
CControlPanelEnum()71 CControlPanelEnum::CControlPanelEnum()
72 {
73 }
74
~CControlPanelEnum()75 CControlPanelEnum::~CControlPanelEnum()
76 {
77 }
78
Initialize(DWORD dwFlags,IEnumIDList * pRegEnumerator)79 HRESULT WINAPI CControlPanelEnum::Initialize(DWORD dwFlags, IEnumIDList* pRegEnumerator)
80 {
81 if (CreateCPanelEnumList(dwFlags) == FALSE)
82 return E_FAIL;
83 AppendItemsFromEnumerator(pRegEnumerator);
84 return S_OK;
85 }
86
_ILCreateCPanelApplet(LPCWSTR pszName,LPCWSTR pszDisplayName,LPCWSTR pszComment,int iIconIdx)87 static LPITEMIDLIST _ILCreateCPanelApplet(LPCWSTR pszName, LPCWSTR pszDisplayName, LPCWSTR pszComment, int iIconIdx)
88 {
89 PIDLCPanelStruct *pCP;
90 LPITEMIDLIST pidl;
91 LPPIDLDATA pData;
92 int cchName, cchDisplayName, cchComment, cbData;
93
94 /* Calculate lengths of given strings */
95 cchName = wcslen(pszName);
96 cchDisplayName = wcslen(pszDisplayName);
97 cchComment = wcslen(pszComment);
98
99 /* Allocate PIDL */
100 cbData = sizeof(pidl->mkid.cb) + sizeof(pData->type) + sizeof(pData->u.cpanel) - sizeof(pData->u.cpanel.szName)
101 + (cchName + cchDisplayName + cchComment + 3) * sizeof(WCHAR);
102 pidl = (LPITEMIDLIST)SHAlloc(cbData + sizeof(WORD));
103 if (!pidl)
104 return NULL;
105
106 /* Copy data to allocated memory */
107 pidl->mkid.cb = cbData;
108 pData = (PIDLDATA *)pidl->mkid.abID;
109 pData->type = PT_CPLAPPLET;
110
111 pCP = &pData->u.cpanel;
112 pCP->dummy = 0;
113 pCP->iconIdx = iIconIdx;
114 wcscpy(pCP->szName, pszName);
115 pCP->offsDispName = cchName + 1;
116 wcscpy(pCP->szName + pCP->offsDispName, pszDisplayName);
117 pCP->offsComment = pCP->offsDispName + cchDisplayName + 1;
118 wcscpy(pCP->szName + pCP->offsComment, pszComment);
119
120 /* Add PIDL NULL terminator */
121 *(WORD*)(pCP->szName + pCP->offsComment + cchComment + 1) = 0;
122
123 pcheck(pidl);
124
125 return pidl;
126 }
127
128 /**************************************************************************
129 * _ILGetCPanelPointer()
130 * gets a pointer to the control panel struct stored in the pidl
131 */
_ILGetCPanelPointer(LPCITEMIDLIST pidl)132 static PIDLCPanelStruct *_ILGetCPanelPointer(LPCITEMIDLIST pidl)
133 {
134 LPPIDLDATA pdata = _ILGetDataPointer(pidl);
135
136 if (pdata && pdata->type == PT_CPLAPPLET)
137 return (PIDLCPanelStruct *) & (pdata->u.cpanel);
138
139 return NULL;
140 }
141
CCPLExtractIcon_CreateInstance(IShellFolder * psf,LPCITEMIDLIST pidl,REFIID riid,LPVOID * ppvOut)142 HRESULT CCPLExtractIcon_CreateInstance(IShellFolder * psf, LPCITEMIDLIST pidl, REFIID riid, LPVOID * ppvOut)
143 {
144 PIDLCPanelStruct *pData = _ILGetCPanelPointer(pidl);
145 if (!pData)
146 return E_FAIL;
147
148 CComPtr<IDefaultExtractIconInit> initIcon;
149 HRESULT hr = SHCreateDefaultExtractIcon(IID_PPV_ARG(IDefaultExtractIconInit, &initIcon));
150 if (FAILED_UNEXPECTEDLY(hr))
151 return hr;
152
153 initIcon->SetNormalIcon(pData->szName, (int)pData->iconIdx != -1 ? pData->iconIdx : 0);
154
155 return initIcon->QueryInterface(riid, ppvOut);
156 }
157
RegisterCPanelApp(LPCWSTR wpath)158 BOOL CControlPanelEnum::RegisterCPanelApp(LPCWSTR wpath)
159 {
160 CPlApplet* applet = Control_LoadApplet(0, wpath, NULL);
161 int iconIdx;
162
163 if (applet)
164 {
165 for (UINT i = 0; i < applet->count; ++i)
166 {
167 if (applet->info[i].idIcon > 0)
168 iconIdx = -applet->info[i].idIcon; /* negative icon index instead of icon number */
169 else
170 iconIdx = 0;
171
172 LPITEMIDLIST pidl = _ILCreateCPanelApplet(wpath,
173 applet->info[i].name,
174 applet->info[i].info,
175 iconIdx);
176
177 if (pidl)
178 AddToEnumList(pidl);
179 }
180 Control_UnloadApplet(applet);
181 }
182 return TRUE;
183 }
184
RegisterRegistryCPanelApps(HKEY hkey_root,LPCWSTR szRepPath)185 int CControlPanelEnum::RegisterRegistryCPanelApps(HKEY hkey_root, LPCWSTR szRepPath)
186 {
187 WCHAR name[MAX_PATH];
188 WCHAR value[MAX_PATH];
189 HKEY hkey;
190
191 int cnt = 0;
192
193 if (RegOpenKeyW(hkey_root, szRepPath, &hkey) == ERROR_SUCCESS)
194 {
195 int idx = 0;
196
197 for(; ; idx++)
198 {
199 DWORD nameLen = MAX_PATH;
200 DWORD valueLen = MAX_PATH;
201 WCHAR buffer[MAX_PATH];
202
203 if (RegEnumValueW(hkey, idx, name, &nameLen, NULL, NULL, (LPBYTE)&value, &valueLen) != ERROR_SUCCESS)
204 break;
205
206 if (ExpandEnvironmentStringsW(value, buffer, MAX_PATH))
207 {
208 wcscpy(value, buffer);
209 }
210
211 if (RegisterCPanelApp(value))
212 ++cnt;
213 }
214 RegCloseKey(hkey);
215 }
216
217 return cnt;
218 }
219
220 /**************************************************************************
221 * CControlPanelEnum::CreateCPanelEnumList()
222 */
CreateCPanelEnumList(DWORD dwFlags)223 BOOL CControlPanelEnum::CreateCPanelEnumList(DWORD dwFlags)
224 {
225 WCHAR szPath[MAX_PATH];
226 WIN32_FIND_DATAW wfd;
227 HANDLE hFile;
228
229 TRACE("(%p)->(flags=0x%08x)\n", this, dwFlags);
230
231 /* enumerate the control panel applets */
232 if (dwFlags & SHCONTF_NONFOLDERS)
233 {
234 LPWSTR p;
235
236 GetSystemDirectoryW(szPath, MAX_PATH);
237 p = PathAddBackslashW(szPath);
238 wcscpy(p, L"*.cpl");
239
240 hFile = FindFirstFileW(szPath, &wfd);
241
242 if (hFile != INVALID_HANDLE_VALUE)
243 {
244 do
245 {
246 if (!(dwFlags & SHCONTF_INCLUDEHIDDEN) && (wfd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN))
247 continue;
248
249 if (!(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
250 wcscpy(p, wfd.cFileName);
251 if (wcscmp(wfd.cFileName, L"ncpa.cpl"))
252 RegisterCPanelApp(szPath);
253 }
254 } while(FindNextFileW(hFile, &wfd));
255 FindClose(hFile);
256 }
257
258 RegisterRegistryCPanelApps(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Control Panel\\Cpls");
259 RegisterRegistryCPanelApps(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Control Panel\\Cpls");
260 }
261 return TRUE;
262 }
263
CControlPanelFolder()264 CControlPanelFolder::CControlPanelFolder()
265 {
266 pidlRoot = NULL; /* absolute pidl */
267 }
268
~CControlPanelFolder()269 CControlPanelFolder::~CControlPanelFolder()
270 {
271 TRACE("-- destroying IShellFolder(%p)\n", this);
272 SHFree(pidlRoot);
273 }
274
275 /**************************************************************************
276 * CControlPanelFolder::ParseDisplayName
277 */
ParseDisplayName(HWND hwndOwner,LPBC pbc,LPOLESTR lpszDisplayName,DWORD * pchEaten,PIDLIST_RELATIVE * ppidl,DWORD * pdwAttributes)278 HRESULT WINAPI CControlPanelFolder::ParseDisplayName(
279 HWND hwndOwner,
280 LPBC pbc,
281 LPOLESTR lpszDisplayName,
282 DWORD *pchEaten,
283 PIDLIST_RELATIVE *ppidl,
284 DWORD *pdwAttributes)
285 {
286 /* We only support parsing guid names */
287 return m_regFolder->ParseDisplayName(hwndOwner, pbc, lpszDisplayName, pchEaten, ppidl, pdwAttributes);
288 }
289
290 /**************************************************************************
291 * CControlPanelFolder::EnumObjects
292 */
EnumObjects(HWND hwndOwner,DWORD dwFlags,LPENUMIDLIST * ppEnumIDList)293 HRESULT WINAPI CControlPanelFolder::EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST *ppEnumIDList)
294 {
295 CComPtr<IEnumIDList> pRegEnumerator;
296 m_regFolder->EnumObjects(hwndOwner, dwFlags, &pRegEnumerator);
297
298 return ShellObjectCreatorInit<CControlPanelEnum>(dwFlags, pRegEnumerator, IID_PPV_ARG(IEnumIDList, ppEnumIDList));
299 }
300
301 /**************************************************************************
302 * CControlPanelFolder::BindToObject
303 */
BindToObject(PCUIDLIST_RELATIVE pidl,LPBC pbcReserved,REFIID riid,LPVOID * ppvOut)304 HRESULT WINAPI CControlPanelFolder::BindToObject(
305 PCUIDLIST_RELATIVE pidl,
306 LPBC pbcReserved,
307 REFIID riid,
308 LPVOID *ppvOut)
309 {
310 return m_regFolder->BindToObject(pidl, pbcReserved, riid, ppvOut);
311 }
312
313 /**************************************************************************
314 * CControlPanelFolder::BindToStorage
315 */
BindToStorage(PCUIDLIST_RELATIVE pidl,LPBC pbcReserved,REFIID riid,LPVOID * ppvOut)316 HRESULT WINAPI CControlPanelFolder::BindToStorage(
317 PCUIDLIST_RELATIVE pidl,
318 LPBC pbcReserved,
319 REFIID riid,
320 LPVOID *ppvOut)
321 {
322 FIXME("(%p)->(pidl=%p,%p,%s,%p) stub\n", this, pidl, pbcReserved, shdebugstr_guid(&riid), ppvOut);
323
324 *ppvOut = NULL;
325 return E_NOTIMPL;
326 }
327
328 /**************************************************************************
329 * CControlPanelFolder::CompareIDs
330 */
CompareIDs(LPARAM lParam,PCUIDLIST_RELATIVE pidl1,PCUIDLIST_RELATIVE pidl2)331 HRESULT WINAPI CControlPanelFolder::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2)
332 {
333 /* Dont use SHELL32_CompareGuidItems because it would cause guid items to come first */
334 if (_ILIsSpecialFolder(pidl1) || _ILIsSpecialFolder(pidl2))
335 {
336 return SHELL32_CompareDetails(this, lParam, pidl1, pidl2);
337 }
338 PIDLCPanelStruct *pData1 = _ILGetCPanelPointer(pidl1);
339 PIDLCPanelStruct *pData2 = _ILGetCPanelPointer(pidl2);
340
341 if (!pData1 || !pData2 || LOWORD(lParam) >= CONTROLPANEL_COL_COUNT)
342 return E_INVALIDARG;
343
344 int result;
345 switch(LOWORD(lParam))
346 {
347 case CONTROLPANEL_COL_NAME:
348 result = _wcsicmp(pData1->szName + pData1->offsDispName, pData2->szName + pData2->offsDispName);
349 break;
350 case CONTROLPANEL_COL_COMMENT:
351 result = _wcsicmp(pData1->szName + pData1->offsComment, pData2->szName + pData2->offsComment);
352 break;
353 default:
354 ERR("Got wrong lParam!\n");
355 return E_INVALIDARG;
356 }
357
358 return MAKE_COMPARE_HRESULT(result);
359 }
360
361 /**************************************************************************
362 * CControlPanelFolder::CreateViewObject
363 */
CreateViewObject(HWND hwndOwner,REFIID riid,LPVOID * ppvOut)364 HRESULT WINAPI CControlPanelFolder::CreateViewObject(HWND hwndOwner, REFIID riid, LPVOID * ppvOut)
365 {
366 CComPtr<IShellView> pShellView;
367 HRESULT hr = E_INVALIDARG;
368
369 TRACE("(%p)->(hwnd=%p,%s,%p)\n", this, hwndOwner, shdebugstr_guid(&riid), ppvOut);
370
371 if (ppvOut) {
372 *ppvOut = NULL;
373
374 if (IsEqualIID(riid, IID_IDropTarget)) {
375 WARN("IDropTarget not implemented\n");
376 hr = E_NOTIMPL;
377 } else if (IsEqualIID(riid, IID_IContextMenu)) {
378 WARN("IContextMenu not implemented\n");
379 hr = E_NOTIMPL;
380 } else if (IsEqualIID(riid, IID_IShellView)) {
381 SFV_CREATE sfvparams = {sizeof(SFV_CREATE), this};
382 hr = SHCreateShellFolderView(&sfvparams, (IShellView**)ppvOut);
383 }
384 }
385 TRACE("--(%p)->(interface=%p)\n", this, ppvOut);
386 return hr;
387 }
388
389 /**************************************************************************
390 * CControlPanelFolder::GetAttributesOf
391 */
GetAttributesOf(UINT cidl,PCUITEMID_CHILD_ARRAY apidl,DWORD * rgfInOut)392 HRESULT WINAPI CControlPanelFolder::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, DWORD * rgfInOut)
393 {
394 HRESULT hr = S_OK;
395 static const DWORD dwControlPanelAttributes =
396 SFGAO_HASSUBFOLDER | SFGAO_FOLDER | SFGAO_CANLINK;
397
398 TRACE("(%p)->(cidl=%d apidl=%p mask=%p (0x%08x))\n",
399 this, cidl, apidl, rgfInOut, rgfInOut ? *rgfInOut : 0);
400
401 if (!rgfInOut)
402 return E_INVALIDARG;
403 if (cidl && !apidl)
404 return E_INVALIDARG;
405
406 if (*rgfInOut == 0)
407 *rgfInOut = ~0;
408
409 if (!cidl)
410 {
411 *rgfInOut &= dwControlPanelAttributes;
412 }
413 else
414 {
415 while(cidl > 0 && *apidl)
416 {
417 pdump(*apidl);
418 if (_ILIsCPanelStruct(*apidl))
419 *rgfInOut &= SFGAO_CANLINK;
420 else if (_ILIsSpecialFolder(*apidl))
421 m_regFolder->GetAttributesOf(1, apidl, rgfInOut);
422 else
423 ERR("Got unknown pidl\n");
424 apidl++;
425 cidl--;
426 }
427 }
428 /* make sure SFGAO_VALIDATE is cleared, some apps depend on that */
429 *rgfInOut &= ~SFGAO_VALIDATE;
430
431 TRACE("-- result=0x%08x\n", *rgfInOut);
432 return hr;
433 }
434
435 /**************************************************************************
436 * CControlPanelFolder::GetUIObjectOf
437 *
438 * PARAMETERS
439 * HWND hwndOwner, //[in ] Parent window for any output
440 * UINT cidl, //[in ] array size
441 * LPCITEMIDLIST* apidl, //[in ] simple pidl array
442 * REFIID riid, //[in ] Requested Interface
443 * UINT* prgfInOut, //[ ] reserved
444 * LPVOID* ppvObject) //[out] Resulting Interface
445 *
446 */
GetUIObjectOf(HWND hwndOwner,UINT cidl,PCUITEMID_CHILD_ARRAY apidl,REFIID riid,UINT * prgfInOut,LPVOID * ppvOut)447 HRESULT WINAPI CControlPanelFolder::GetUIObjectOf(HWND hwndOwner,
448 UINT cidl, PCUITEMID_CHILD_ARRAY apidl, REFIID riid, UINT * prgfInOut, LPVOID * ppvOut)
449 {
450 LPVOID pObj = NULL;
451 HRESULT hr = E_INVALIDARG;
452
453 TRACE("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n",
454 this, hwndOwner, cidl, apidl, shdebugstr_guid(&riid), prgfInOut, ppvOut);
455
456 if (ppvOut) {
457 *ppvOut = NULL;
458
459 if (IsEqualIID(riid, IID_IContextMenu) && (cidl >= 1)) {
460 /* HACK: We should use callbacks from CDefaultContextMenu instead of creating one on our own */
461 BOOL bHasCpl = FALSE;
462 for (UINT i = 0; i < cidl; i++)
463 {
464 if(_ILIsCPanelStruct(apidl[i]))
465 {
466 bHasCpl = TRUE;
467 }
468 }
469
470 if (bHasCpl)
471 hr = ShellObjectCreatorInit<CCPLItemMenu>(cidl, apidl, riid, &pObj);
472 else
473 hr = m_regFolder->GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, &pObj);
474 } else if (IsEqualIID(riid, IID_IDataObject) && (cidl >= 1)) {
475 hr = IDataObject_Constructor(hwndOwner, pidlRoot, apidl, cidl, TRUE, (IDataObject **)&pObj);
476 } else if ((IsEqualIID(riid, IID_IExtractIconA) || IsEqualIID(riid, IID_IExtractIconW)) && (cidl == 1)) {
477 if (_ILGetCPanelPointer(apidl[0]))
478 hr = CCPLExtractIcon_CreateInstance(this, apidl[0], riid, &pObj);
479 else
480 hr = m_regFolder->GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, &pObj);
481 } else {
482 hr = E_NOINTERFACE;
483 }
484
485 if (SUCCEEDED(hr) && !pObj)
486 hr = E_OUTOFMEMORY;
487
488 *ppvOut = pObj;
489 }
490 TRACE("(%p)->hr=0x%08x\n", this, hr);
491 return hr;
492 }
493
494 /**************************************************************************
495 * CControlPanelFolder::GetDisplayNameOf
496 */
GetDisplayNameOf(PCUITEMID_CHILD pidl,DWORD dwFlags,LPSTRRET strRet)497 HRESULT WINAPI CControlPanelFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD dwFlags, LPSTRRET strRet)
498 {
499 if (!pidl)
500 return S_FALSE;
501
502 PIDLCPanelStruct *pCPanel = _ILGetCPanelPointer(pidl);
503
504 if (pCPanel)
505 {
506 return SHSetStrRet(strRet, pCPanel->szName + pCPanel->offsDispName);
507 }
508 else if (_ILIsSpecialFolder(pidl))
509 {
510 return m_regFolder->GetDisplayNameOf(pidl, dwFlags, strRet);
511 }
512
513 return E_FAIL;
514 }
515
516 /**************************************************************************
517 * CControlPanelFolder::SetNameOf
518 * Changes the name of a file object or subfolder, possibly changing its item
519 * identifier in the process.
520 *
521 * PARAMETERS
522 * HWND hwndOwner, //[in ] Owner window for output
523 * LPCITEMIDLIST pidl, //[in ] simple pidl of item to change
524 * LPCOLESTR lpszName, //[in ] the items new display name
525 * DWORD dwFlags, //[in ] SHGNO formatting flags
526 * LPITEMIDLIST* ppidlOut) //[out] simple pidl returned
527 */
SetNameOf(HWND hwndOwner,PCUITEMID_CHILD pidl,LPCOLESTR lpName,DWORD dwFlags,PITEMID_CHILD * pPidlOut)528 HRESULT WINAPI CControlPanelFolder::SetNameOf(HWND hwndOwner, PCUITEMID_CHILD pidl, /*simple pidl */
529 LPCOLESTR lpName, DWORD dwFlags, PITEMID_CHILD *pPidlOut)
530 {
531 FIXME("(%p)->(%p,pidl=%p,%s,%u,%p)\n", this, hwndOwner, pidl, debugstr_w(lpName), dwFlags, pPidlOut);
532 return E_FAIL;
533 }
534
GetDefaultSearchGUID(GUID * pguid)535 HRESULT WINAPI CControlPanelFolder::GetDefaultSearchGUID(GUID *pguid)
536 {
537 FIXME("(%p)\n", this);
538 return E_NOTIMPL;
539 }
540
EnumSearches(IEnumExtraSearch ** ppenum)541 HRESULT WINAPI CControlPanelFolder::EnumSearches(IEnumExtraSearch **ppenum)
542 {
543 FIXME("(%p)\n", this);
544 return E_NOTIMPL;
545 }
546
GetDefaultColumn(DWORD dwRes,ULONG * pSort,ULONG * pDisplay)547 HRESULT WINAPI CControlPanelFolder::GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay)
548 {
549 TRACE("(%p)\n", this);
550
551 if (pSort) *pSort = 0;
552 if (pDisplay) *pDisplay = 0;
553 return S_OK;
554 }
555
GetDefaultColumnState(UINT iColumn,DWORD * pcsFlags)556 HRESULT WINAPI CControlPanelFolder::GetDefaultColumnState(UINT iColumn, DWORD *pcsFlags)
557 {
558 TRACE("(%p)\n", this);
559
560 if (!pcsFlags || iColumn >= CONTROLPANEL_COL_COUNT)
561 return E_INVALIDARG;
562 *pcsFlags = ControlPanelSFHeader[iColumn].colstate;
563 return S_OK;
564 }
565
GetDetailsEx(PCUITEMID_CHILD pidl,const SHCOLUMNID * pscid,VARIANT * pv)566 HRESULT WINAPI CControlPanelFolder::GetDetailsEx(PCUITEMID_CHILD pidl, const SHCOLUMNID *pscid, VARIANT *pv)
567 {
568 FIXME("(%p)\n", this);
569 return E_NOTIMPL;
570 }
571
GetDetailsOf(PCUITEMID_CHILD pidl,UINT iColumn,SHELLDETAILS * psd)572 HRESULT WINAPI CControlPanelFolder::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDETAILS *psd)
573 {
574 if (!psd || iColumn >= CONTROLPANEL_COL_COUNT)
575 return E_INVALIDARG;
576
577 if (!pidl)
578 {
579 psd->fmt = ControlPanelSFHeader[iColumn].fmt;
580 psd->cxChar = ControlPanelSFHeader[iColumn].cxChar;
581 return SHSetStrRet(&psd->str, shell32_hInstance, ControlPanelSFHeader[iColumn].colnameid);
582 }
583 else if (_ILIsSpecialFolder(pidl))
584 {
585 return m_regFolder->GetDetailsOf(pidl, iColumn, psd);
586 }
587 else
588 {
589 PIDLCPanelStruct *pCPanel = _ILGetCPanelPointer(pidl);
590
591 if (!pCPanel)
592 return E_FAIL;
593
594 switch(iColumn)
595 {
596 case CONTROLPANEL_COL_NAME:
597 return SHSetStrRet(&psd->str, pCPanel->szName + pCPanel->offsDispName);
598 case CONTROLPANEL_COL_COMMENT:
599 return SHSetStrRet(&psd->str, pCPanel->szName + pCPanel->offsComment);
600 }
601 }
602
603 return E_FAIL;
604 }
605
MapColumnToSCID(UINT column,SHCOLUMNID * pscid)606 HRESULT WINAPI CControlPanelFolder::MapColumnToSCID(UINT column, SHCOLUMNID *pscid)
607 {
608 FIXME("(%p)\n", this);
609 return E_NOTIMPL;
610 }
611
612 /************************************************************************
613 * CControlPanelFolder::GetClassID
614 */
GetClassID(CLSID * lpClassId)615 HRESULT WINAPI CControlPanelFolder::GetClassID(CLSID *lpClassId)
616 {
617 TRACE("(%p)\n", this);
618
619 if (!lpClassId)
620 return E_POINTER;
621 *lpClassId = CLSID_ControlPanel;
622
623 return S_OK;
624 }
625
626 /************************************************************************
627 * CControlPanelFolder::Initialize
628 *
629 * NOTES: it makes no sense to change the pidl
630 */
Initialize(PCIDLIST_ABSOLUTE pidl)631 HRESULT WINAPI CControlPanelFolder::Initialize(PCIDLIST_ABSOLUTE pidl)
632 {
633 if (pidlRoot)
634 SHFree((LPVOID)pidlRoot);
635
636 pidlRoot = ILClone(pidl);
637
638 /* Create the inner reg folder */
639 REGFOLDERINITDATA RegInit = { static_cast<IShellFolder*>(this), &g_RegFolderInfo };
640 HRESULT hr;
641 hr = CRegFolder_CreateInstance(&RegInit,
642 pidlRoot,
643 IID_PPV_ARG(IShellFolder2, &m_regFolder));
644 if (FAILED_UNEXPECTEDLY(hr))
645 return hr;
646
647 return S_OK;
648 }
649
650 /**************************************************************************
651 * CControlPanelFolder::GetCurFolder
652 */
GetCurFolder(PIDLIST_ABSOLUTE * pidl)653 HRESULT WINAPI CControlPanelFolder::GetCurFolder(PIDLIST_ABSOLUTE * pidl)
654 {
655 TRACE("(%p)->(%p)\n", this, pidl);
656
657 if (!pidl)
658 return E_POINTER;
659 *pidl = ILClone(pidlRoot);
660 return S_OK;
661 }
662
CCPLItemMenu()663 CCPLItemMenu::CCPLItemMenu()
664 {
665 m_apidl = NULL;
666 m_cidl = 0;
667 }
668
Initialize(UINT cidl,PCUITEMID_CHILD_ARRAY apidl)669 HRESULT WINAPI CCPLItemMenu::Initialize(UINT cidl, PCUITEMID_CHILD_ARRAY apidl)
670 {
671 m_cidl = cidl;
672 m_apidl = _ILCopyaPidl(apidl, m_cidl);
673 if (m_cidl && !m_apidl)
674 return E_OUTOFMEMORY;
675
676 return S_OK;
677 }
678
~CCPLItemMenu()679 CCPLItemMenu::~CCPLItemMenu()
680 {
681 _ILFreeaPidl(m_apidl, m_cidl);
682 }
683
QueryContextMenu(HMENU hMenu,UINT indexMenu,UINT idCmdFirst,UINT idCmdLast,UINT uFlags)684 HRESULT WINAPI CCPLItemMenu::QueryContextMenu(
685 HMENU hMenu,
686 UINT indexMenu,
687 UINT idCmdFirst,
688 UINT idCmdLast,
689 UINT uFlags)
690 {
691 _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst, MFT_STRING, MAKEINTRESOURCEW(IDS_OPEN), MFS_DEFAULT);
692 _InsertMenuItemW(hMenu, indexMenu++, TRUE, IDC_STATIC, MFT_SEPARATOR, NULL, MFS_ENABLED);
693 _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + 1, MFT_STRING, MAKEINTRESOURCEW(IDS_CREATELINK), MFS_ENABLED);
694
695 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 2);
696 }
697
698 EXTERN_C
699 void WINAPI Control_RunDLLW(HWND hWnd, HINSTANCE hInst, LPCWSTR cmd, DWORD nCmdShow);
700
701 /**************************************************************************
702 * ICPanel_IContextMenu_InvokeCommand()
703 */
InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)704 HRESULT WINAPI CCPLItemMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
705 {
706 HRESULT hResult;
707
708 PIDLCPanelStruct *pCPanel = _ILGetCPanelPointer(m_apidl[0]);
709 if(!pCPanel)
710 return E_FAIL;
711
712 TRACE("(%p)->(invcom=%p verb=%p wnd=%p)\n", this, lpcmi, lpcmi->lpVerb, lpcmi->hwnd);
713
714 if (lpcmi->lpVerb == MAKEINTRESOURCEA(0))
715 {
716 /* Hardcode the command here; Executing a cpl file would be fine but we also need to run things like console.dll */
717 WCHAR wszParams[MAX_PATH];
718 PCWSTR wszFile = L"rundll32.exe";
719 PCWSTR wszFormat = L"shell32.dll,Control_RunDLL %s,%s";
720
721 wsprintfW(wszParams, wszFormat, pCPanel->szName, pCPanel->szName + pCPanel->offsDispName);
722
723 /* Note: we pass the applet name to Control_RunDLL to distinguish between multiple applets in one .cpl file */
724 ShellExecuteW(NULL, NULL, wszFile, wszParams, NULL, 0);
725 }
726 else if (lpcmi->lpVerb == MAKEINTRESOURCEA(1)) //FIXME
727 {
728 CComPtr<IDataObject> pDataObj;
729 LPITEMIDLIST pidl = _ILCreateControlPanel();
730
731 hResult = SHCreateDataObject(pidl, m_cidl, m_apidl, NULL, IID_PPV_ARG(IDataObject, &pDataObj));
732 if (FAILED(hResult))
733 return hResult;
734
735 SHFree(pidl);
736
737 //FIXME: Use SHCreateLinks
738 CComPtr<IShellFolder> psf;
739 CComPtr<IDropTarget> pDT;
740
741 hResult = SHGetDesktopFolder(&psf);
742 if (FAILED(hResult))
743 return hResult;
744
745 hResult = psf->CreateViewObject(NULL, IID_PPV_ARG(IDropTarget, &pDT));
746 if (FAILED(hResult))
747 return hResult;
748
749 SHSimulateDrop(pDT, pDataObj, MK_CONTROL|MK_SHIFT, NULL, NULL);
750 }
751 return S_OK;
752 }
753
754 /**************************************************************************
755 * ICPanel_IContextMenu_GetCommandString()
756 *
757 */
GetCommandString(UINT_PTR idCommand,UINT uFlags,UINT * lpReserved,LPSTR lpszName,UINT uMaxNameLen)758 HRESULT WINAPI CCPLItemMenu::GetCommandString(
759 UINT_PTR idCommand,
760 UINT uFlags,
761 UINT* lpReserved,
762 LPSTR lpszName,
763 UINT uMaxNameLen)
764 {
765 TRACE("(%p)->(idcom=%lx flags=%x %p name=%p len=%x)\n", this, idCommand, uFlags, lpReserved, lpszName, uMaxNameLen);
766
767 FIXME("unknown command string\n");
768 return E_FAIL;
769 }
770
771 /**************************************************************************
772 * ICPanel_IContextMenu_HandleMenuMsg()
773 */
HandleMenuMsg(UINT uMsg,WPARAM wParam,LPARAM lParam)774 HRESULT WINAPI CCPLItemMenu::HandleMenuMsg(
775 UINT uMsg,
776 WPARAM wParam,
777 LPARAM lParam)
778 {
779 TRACE("ICPanel_IContextMenu_HandleMenuMsg (%p)->(msg=%x wp=%lx lp=%lx)\n", this, uMsg, wParam, lParam);
780
781 return E_NOTIMPL;
782 }
783
784 /**************************************************************************
785 * COpenControlPanel
786 */
787
GetParsingName(PCIDLIST_ABSOLUTE pidl,PWSTR * Name)788 static HRESULT GetParsingName(PCIDLIST_ABSOLUTE pidl, PWSTR*Name)
789 {
790 PIDLIST_ABSOLUTE pidlFree = NULL;
791 if (IS_INTRESOURCE(pidl))
792 {
793 HRESULT hr = SHGetSpecialFolderLocation(NULL, (UINT)(SIZE_T)pidl, &pidlFree);
794 if (FAILED(hr))
795 return hr;
796 pidl = pidlFree;
797 }
798 HRESULT hr = SHGetNameFromIDList(pidl, SIGDN_DESKTOPABSOLUTEPARSING, Name);
799 ILFree(pidlFree);
800 return hr;
801 }
802
CreateCplAbsoluteParsingPath(LPCWSTR Prefix,LPCWSTR InFolderParse,PWSTR Buf,UINT cchBuf)803 static HRESULT CreateCplAbsoluteParsingPath(LPCWSTR Prefix, LPCWSTR InFolderParse, PWSTR Buf, UINT cchBuf)
804 {
805 PWSTR cpfolder;
806 HRESULT hr = GetParsingName((PCIDLIST_ABSOLUTE)CSIDL_CONTROLS, &cpfolder);
807 if (SUCCEEDED(hr))
808 {
809 hr = StringCchPrintfW(Buf, cchBuf, L"%s\\%s%s", cpfolder, Prefix, InFolderParse);
810 SHFree(cpfolder);
811 }
812 return hr;
813 }
814
FindExeCplClass(LPCWSTR Canonical,HKEY hKey,BOOL Wow64,LPWSTR clsid)815 static HRESULT FindExeCplClass(LPCWSTR Canonical, HKEY hKey, BOOL Wow64, LPWSTR clsid)
816 {
817 HRESULT hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
818 HKEY hNSKey;
819 WCHAR key[MAX_PATH], buf[MAX_PATH];
820 wsprintfW(key, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\%s\\NameSpace",
821 Wow64 ? L"ControlPanelWOW64" : L"ControlPanel");
822 LSTATUS error = RegOpenKeyExW(hKey, key, 0, KEY_READ, &hNSKey);
823 if (error)
824 return HRESULT_FROM_WIN32(error);
825 for (DWORD i = 0; RegEnumKeyW(hNSKey, i, key, _countof(key)) == ERROR_SUCCESS; ++i)
826 {
827 IID validate;
828 if (SUCCEEDED(IIDFromString(key, &validate)))
829 {
830 wsprintfW(buf, L"CLSID\\%s", key);
831 DWORD cb = sizeof(buf);
832 if (RegGetValueW(HKEY_CLASSES_ROOT, buf, L"System.ApplicationName",
833 RRF_RT_REG_SZ, NULL, buf, &cb) == ERROR_SUCCESS)
834 {
835 if (!lstrcmpiW(buf, Canonical))
836 {
837 lstrcpyW(clsid, key);
838 hr = S_OK;
839 }
840 }
841 }
842 }
843 RegCloseKey(hNSKey);
844 return hr;
845 }
846
FindExeCplClass(LPCWSTR Canonical,LPWSTR clsid)847 static HRESULT FindExeCplClass(LPCWSTR Canonical, LPWSTR clsid)
848 {
849 HRESULT hr = E_FAIL;
850 if (FAILED(hr))
851 hr = FindExeCplClass(Canonical, HKEY_CURRENT_USER, FALSE, clsid);
852 if (FAILED(hr))
853 hr = FindExeCplClass(Canonical, HKEY_CURRENT_USER, TRUE, clsid);
854 if (FAILED(hr))
855 hr = FindExeCplClass(Canonical, HKEY_LOCAL_MACHINE, FALSE, clsid);
856 if (FAILED(hr))
857 hr = FindExeCplClass(Canonical, HKEY_LOCAL_MACHINE, TRUE, clsid);
858 return hr;
859 }
860
Open(LPCWSTR pszName,LPCWSTR pszPage,IUnknown * punkSite)861 HRESULT WINAPI COpenControlPanel::Open(LPCWSTR pszName, LPCWSTR pszPage, IUnknown *punkSite)
862 {
863 WCHAR path[MAX_PATH], clspath[MAX_PATH];
864 HRESULT hr = S_OK;
865 SHELLEXECUTEINFOW sei = { sizeof(sei), SEE_MASK_FLAG_DDEWAIT };
866 sei.lpFile = path;
867 sei.nShow = SW_SHOW;
868 if (!pszName)
869 {
870 GetSystemDirectoryW(path, _countof(path));
871 PathAppendW(path, L"control.exe");
872 }
873 else
874 {
875 LPWSTR clsid = clspath + wsprintfW(clspath, L"CLSID\\");
876 if (SUCCEEDED(hr = FindExeCplClass(pszName, clsid)))
877 {
878 if (SUCCEEDED(hr = CreateCplAbsoluteParsingPath(L"::", clsid, path, _countof(path))))
879 {
880 // NT6 will execute "::{26EE0668-A00A-44D7-9371-BEB064C98683}\0\::{clsid}[\pszPage]"
881 // but we don't support parsing that so we force the class instead.
882 sei.fMask |= SEE_MASK_CLASSNAME;
883 sei.lpClass = clspath;
884 }
885 }
886 }
887
888 if (SUCCEEDED(hr))
889 {
890 DWORD error = ShellExecuteExW(&sei) ? ERROR_SUCCESS : GetLastError();
891 hr = HRESULT_FROM_WIN32(error);
892 }
893 return hr;
894 }
895
GetPath(LPCWSTR pszName,LPWSTR pszPath,UINT cchPath)896 HRESULT WINAPI COpenControlPanel::GetPath(LPCWSTR pszName, LPWSTR pszPath, UINT cchPath)
897 {
898 HRESULT hr;
899 if (!pszName)
900 {
901 PWSTR cpfolder;
902 if (SUCCEEDED(hr = GetParsingName((PCIDLIST_ABSOLUTE)CSIDL_CONTROLS, &cpfolder)))
903 {
904 hr = StringCchCopyW(pszPath, cchPath, cpfolder);
905 SHFree(cpfolder);
906 }
907 }
908 else
909 {
910 WCHAR clsid[38 + 1];
911 if (SUCCEEDED(hr = FindExeCplClass(pszName, clsid)))
912 {
913 hr = CreateCplAbsoluteParsingPath(L"::", clsid, pszPath, cchPath);
914 }
915 }
916 return hr;
917 }
918
GetCurrentView(CPVIEW * pView)919 HRESULT WINAPI COpenControlPanel::GetCurrentView(CPVIEW *pView)
920 {
921 *pView = CPVIEW_CLASSIC;
922 return S_OK;
923 }
924