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 
71 CControlPanelEnum::CControlPanelEnum()
72 {
73 }
74 
75 CControlPanelEnum::~CControlPanelEnum()
76 {
77 }
78 
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 
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  */
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 
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 
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 
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  */
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 
264 CControlPanelFolder::CControlPanelFolder()
265 {
266     pidlRoot = NULL;    /* absolute pidl */
267 }
268 
269 CControlPanelFolder::~CControlPanelFolder()
270 {
271     TRACE("-- destroying IShellFolder(%p)\n", this);
272     SHFree(pidlRoot);
273 }
274 
275 /**************************************************************************
276 *    CControlPanelFolder::ParseDisplayName
277 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 
535 HRESULT WINAPI CControlPanelFolder::GetDefaultSearchGUID(GUID *pguid)
536 {
537     FIXME("(%p)\n", this);
538     return E_NOTIMPL;
539 }
540 
541 HRESULT WINAPI CControlPanelFolder::EnumSearches(IEnumExtraSearch **ppenum)
542 {
543     FIXME("(%p)\n", this);
544     return E_NOTIMPL;
545 }
546 
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 
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 
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 
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 
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  */
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  */
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  */
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 
663 CCPLItemMenu::CCPLItemMenu()
664 {
665     m_apidl = NULL;
666     m_cidl = 0;
667 }
668 
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 
679 CCPLItemMenu::~CCPLItemMenu()
680 {
681     _ILFreeaPidl(m_apidl, m_cidl);
682 }
683 
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 */
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  */
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 */
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 
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 
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 
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 
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 
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 
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 
919 HRESULT WINAPI COpenControlPanel::GetCurrentView(CPVIEW *pView)
920 {
921     *pView = CPVIEW_CLASSIC;
922     return S_OK;
923 }
924