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 /***********************************************************************
27 *   control panel implementation in shell namespace
28 */
29 
30 class CControlPanelEnum :
31     public CEnumIDListBase
32 {
33     public:
34         CControlPanelEnum();
35         ~CControlPanelEnum();
36         HRESULT WINAPI Initialize(DWORD dwFlags, IEnumIDList* pRegEnumerator);
37         BOOL RegisterCPanelApp(LPCWSTR path);
38         int RegisterRegistryCPanelApps(HKEY hkey_root, LPCWSTR szRepPath);
39         BOOL CreateCPanelEnumList(DWORD dwFlags);
40 
41         BEGIN_COM_MAP(CControlPanelEnum)
42         COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList)
43         END_COM_MAP()
44 };
45 
46 /***********************************************************************
47 *   IShellFolder [ControlPanel] implementation
48 */
49 
50 static const shvheader ControlPanelSFHeader[] = {
51     {IDS_SHV_COLUMN_NAME, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 20},/*FIXME*/
52     {IDS_SHV_COLUMN_COMMENTS, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 80},/*FIXME*/
53 };
54 
55 enum controlpanel_columns
56 {
57     CONTROLPANEL_COL_NAME,
58     CONTROLPANEL_COL_COMMENT,
59     CONTROLPANEL_COL_COUNT,
60 };
61 
62 CControlPanelEnum::CControlPanelEnum()
63 {
64 }
65 
66 CControlPanelEnum::~CControlPanelEnum()
67 {
68 }
69 
70 HRESULT WINAPI CControlPanelEnum::Initialize(DWORD dwFlags, IEnumIDList* pRegEnumerator)
71 {
72     if (CreateCPanelEnumList(dwFlags) == FALSE)
73         return E_FAIL;
74     AppendItemsFromEnumerator(pRegEnumerator);
75     return S_OK;
76 }
77 
78 static LPITEMIDLIST _ILCreateCPanelApplet(LPCWSTR pszName, LPCWSTR pszDisplayName, LPCWSTR pszComment, int iIconIdx)
79 {
80     PIDLCPanelStruct *pCP;
81     LPITEMIDLIST pidl;
82     LPPIDLDATA pData;
83     int cchName, cchDisplayName, cchComment, cbData;
84 
85     /* Calculate lengths of given strings */
86     cchName = wcslen(pszName);
87     cchDisplayName = wcslen(pszDisplayName);
88     cchComment = wcslen(pszComment);
89 
90     /* Allocate PIDL */
91     cbData = sizeof(pidl->mkid.cb) + sizeof(pData->type) + sizeof(pData->u.cpanel) - sizeof(pData->u.cpanel.szName)
92              + (cchName + cchDisplayName + cchComment + 3) * sizeof(WCHAR);
93     pidl = (LPITEMIDLIST)SHAlloc(cbData + sizeof(WORD));
94     if (!pidl)
95         return NULL;
96 
97     /* Copy data to allocated memory */
98     pidl->mkid.cb = cbData;
99     pData = (PIDLDATA *)pidl->mkid.abID;
100     pData->type = PT_CPLAPPLET;
101 
102     pCP = &pData->u.cpanel;
103     pCP->dummy = 0;
104     pCP->iconIdx = iIconIdx;
105     wcscpy(pCP->szName, pszName);
106     pCP->offsDispName = cchName + 1;
107     wcscpy(pCP->szName + pCP->offsDispName, pszDisplayName);
108     pCP->offsComment = pCP->offsDispName + cchDisplayName + 1;
109     wcscpy(pCP->szName + pCP->offsComment, pszComment);
110 
111     /* Add PIDL NULL terminator */
112     *(WORD*)(pCP->szName + pCP->offsComment + cchComment + 1) = 0;
113 
114     pcheck(pidl);
115 
116     return pidl;
117 }
118 
119 /**************************************************************************
120  *  _ILGetCPanelPointer()
121  * gets a pointer to the control panel struct stored in the pidl
122  */
123 static PIDLCPanelStruct *_ILGetCPanelPointer(LPCITEMIDLIST pidl)
124 {
125     LPPIDLDATA pdata = _ILGetDataPointer(pidl);
126 
127     if (pdata && pdata->type == PT_CPLAPPLET)
128         return (PIDLCPanelStruct *) & (pdata->u.cpanel);
129 
130     return NULL;
131 }
132 
133 HRESULT CCPLExtractIcon_CreateInstance(IShellFolder * psf, LPCITEMIDLIST pidl, REFIID riid, LPVOID * ppvOut)
134 {
135     PIDLCPanelStruct *pData = _ILGetCPanelPointer(pidl);
136     if (!pData)
137         return E_FAIL;
138 
139     CComPtr<IDefaultExtractIconInit> initIcon;
140     HRESULT hr = SHCreateDefaultExtractIcon(IID_PPV_ARG(IDefaultExtractIconInit, &initIcon));
141     if (FAILED_UNEXPECTEDLY(hr))
142         return hr;
143 
144     initIcon->SetNormalIcon(pData->szName, (int)pData->iconIdx != -1 ? pData->iconIdx : 0);
145 
146     return initIcon->QueryInterface(riid, ppvOut);
147 }
148 
149 BOOL CControlPanelEnum::RegisterCPanelApp(LPCWSTR wpath)
150 {
151     CPlApplet* applet = Control_LoadApplet(0, wpath, NULL);
152     int iconIdx;
153 
154     if (applet)
155     {
156         for (UINT i = 0; i < applet->count; ++i)
157         {
158             if (applet->info[i].idIcon > 0)
159                 iconIdx = -applet->info[i].idIcon; /* negative icon index instead of icon number */
160             else
161                 iconIdx = 0;
162 
163             LPITEMIDLIST pidl = _ILCreateCPanelApplet(wpath,
164                                                       applet->info[i].name,
165                                                       applet->info[i].info,
166                                                       iconIdx);
167 
168             if (pidl)
169                 AddToEnumList(pidl);
170         }
171         Control_UnloadApplet(applet);
172     }
173     return TRUE;
174 }
175 
176 int CControlPanelEnum::RegisterRegistryCPanelApps(HKEY hkey_root, LPCWSTR szRepPath)
177 {
178     WCHAR name[MAX_PATH];
179     WCHAR value[MAX_PATH];
180     HKEY hkey;
181 
182     int cnt = 0;
183 
184     if (RegOpenKeyW(hkey_root, szRepPath, &hkey) == ERROR_SUCCESS)
185     {
186         int idx = 0;
187 
188         for(; ; idx++)
189         {
190             DWORD nameLen = MAX_PATH;
191             DWORD valueLen = MAX_PATH;
192             WCHAR buffer[MAX_PATH];
193 
194             if (RegEnumValueW(hkey, idx, name, &nameLen, NULL, NULL, (LPBYTE)&value, &valueLen) != ERROR_SUCCESS)
195                 break;
196 
197             if (ExpandEnvironmentStringsW(value, buffer, MAX_PATH))
198             {
199                 wcscpy(value, buffer);
200             }
201 
202             if (RegisterCPanelApp(value))
203                 ++cnt;
204         }
205         RegCloseKey(hkey);
206     }
207 
208     return cnt;
209 }
210 
211 /**************************************************************************
212  *  CControlPanelEnum::CreateCPanelEnumList()
213  */
214 BOOL CControlPanelEnum::CreateCPanelEnumList(DWORD dwFlags)
215 {
216     WCHAR szPath[MAX_PATH];
217     WIN32_FIND_DATAW wfd;
218     HANDLE hFile;
219 
220     TRACE("(%p)->(flags=0x%08x)\n", this, dwFlags);
221 
222     /* enumerate the control panel applets */
223     if (dwFlags & SHCONTF_NONFOLDERS)
224     {
225         LPWSTR p;
226 
227         GetSystemDirectoryW(szPath, MAX_PATH);
228         p = PathAddBackslashW(szPath);
229         wcscpy(p, L"*.cpl");
230 
231         hFile = FindFirstFileW(szPath, &wfd);
232 
233         if (hFile != INVALID_HANDLE_VALUE)
234         {
235             do
236             {
237                 if (!(dwFlags & SHCONTF_INCLUDEHIDDEN) && (wfd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN))
238                     continue;
239 
240                 if (!(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
241                     wcscpy(p, wfd.cFileName);
242                     if (wcscmp(wfd.cFileName, L"ncpa.cpl"))
243                         RegisterCPanelApp(szPath);
244                 }
245             } while(FindNextFileW(hFile, &wfd));
246             FindClose(hFile);
247         }
248 
249         RegisterRegistryCPanelApps(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Control Panel\\Cpls");
250         RegisterRegistryCPanelApps(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Control Panel\\Cpls");
251     }
252     return TRUE;
253 }
254 
255 CControlPanelFolder::CControlPanelFolder()
256 {
257     pidlRoot = NULL;    /* absolute pidl */
258 }
259 
260 CControlPanelFolder::~CControlPanelFolder()
261 {
262     TRACE("-- destroying IShellFolder(%p)\n", this);
263     SHFree(pidlRoot);
264 }
265 
266 /**************************************************************************
267 *    CControlPanelFolder::ParseDisplayName
268 */
269 HRESULT WINAPI CControlPanelFolder::ParseDisplayName(
270     HWND hwndOwner,
271     LPBC pbc,
272     LPOLESTR lpszDisplayName,
273     DWORD *pchEaten,
274     PIDLIST_RELATIVE *ppidl,
275     DWORD *pdwAttributes)
276 {
277     /* We only support parsing guid names */
278     return m_regFolder->ParseDisplayName(hwndOwner, pbc, lpszDisplayName, pchEaten, ppidl, pdwAttributes);
279 }
280 
281 /**************************************************************************
282 *        CControlPanelFolder::EnumObjects
283 */
284 HRESULT WINAPI CControlPanelFolder::EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST *ppEnumIDList)
285 {
286     CComPtr<IEnumIDList> pRegEnumerator;
287     m_regFolder->EnumObjects(hwndOwner, dwFlags, &pRegEnumerator);
288 
289     return ShellObjectCreatorInit<CControlPanelEnum>(dwFlags, pRegEnumerator, IID_PPV_ARG(IEnumIDList, ppEnumIDList));
290 }
291 
292 /**************************************************************************
293 *        CControlPanelFolder::BindToObject
294 */
295 HRESULT WINAPI CControlPanelFolder::BindToObject(
296     PCUIDLIST_RELATIVE pidl,
297     LPBC pbcReserved,
298     REFIID riid,
299     LPVOID *ppvOut)
300 {
301     return m_regFolder->BindToObject(pidl, pbcReserved, riid, ppvOut);
302 }
303 
304 /**************************************************************************
305 *    CControlPanelFolder::BindToStorage
306 */
307 HRESULT WINAPI CControlPanelFolder::BindToStorage(
308     PCUIDLIST_RELATIVE pidl,
309     LPBC pbcReserved,
310     REFIID riid,
311     LPVOID *ppvOut)
312 {
313     FIXME("(%p)->(pidl=%p,%p,%s,%p) stub\n", this, pidl, pbcReserved, shdebugstr_guid(&riid), ppvOut);
314 
315     *ppvOut = NULL;
316     return E_NOTIMPL;
317 }
318 
319 /**************************************************************************
320 *     CControlPanelFolder::CompareIDs
321 */
322 HRESULT WINAPI CControlPanelFolder::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2)
323 {
324     /* Dont use SHELL32_CompareGuidItems because it would cause guid items to come first */
325     if (_ILIsSpecialFolder(pidl1) || _ILIsSpecialFolder(pidl2))
326     {
327         return SHELL32_CompareDetails(this, lParam, pidl1, pidl2);
328     }
329     PIDLCPanelStruct *pData1 = _ILGetCPanelPointer(pidl1);
330     PIDLCPanelStruct *pData2 = _ILGetCPanelPointer(pidl2);
331 
332     if (!pData1 || !pData2 || LOWORD(lParam) >= CONTROLPANEL_COL_COUNT)
333         return E_INVALIDARG;
334 
335     int result;
336     switch(LOWORD(lParam))
337     {
338         case CONTROLPANEL_COL_NAME:
339             result = wcsicmp(pData1->szName + pData1->offsDispName, pData2->szName + pData2->offsDispName);
340             break;
341         case CONTROLPANEL_COL_COMMENT:
342             result = wcsicmp(pData1->szName + pData1->offsComment, pData2->szName + pData2->offsComment);
343             break;
344         default:
345             ERR("Got wrong lParam!\n");
346             return E_INVALIDARG;
347     }
348 
349     return MAKE_COMPARE_HRESULT(result);
350 }
351 
352 /**************************************************************************
353 *    CControlPanelFolder::CreateViewObject
354 */
355 HRESULT WINAPI CControlPanelFolder::CreateViewObject(HWND hwndOwner, REFIID riid, LPVOID * ppvOut)
356 {
357     CComPtr<IShellView>                    pShellView;
358     HRESULT hr = E_INVALIDARG;
359 
360     TRACE("(%p)->(hwnd=%p,%s,%p)\n", this, hwndOwner, shdebugstr_guid(&riid), ppvOut);
361 
362     if (ppvOut) {
363         *ppvOut = NULL;
364 
365         if (IsEqualIID(riid, IID_IDropTarget)) {
366             WARN("IDropTarget not implemented\n");
367             hr = E_NOTIMPL;
368         } else if (IsEqualIID(riid, IID_IContextMenu)) {
369             WARN("IContextMenu not implemented\n");
370             hr = E_NOTIMPL;
371         } else if (IsEqualIID(riid, IID_IShellView)) {
372             SFV_CREATE sfvparams = {sizeof(SFV_CREATE), this};
373             hr = SHCreateShellFolderView(&sfvparams, (IShellView**)ppvOut);
374         }
375     }
376     TRACE("--(%p)->(interface=%p)\n", this, ppvOut);
377     return hr;
378 }
379 
380 /**************************************************************************
381 *  CControlPanelFolder::GetAttributesOf
382 */
383 HRESULT WINAPI CControlPanelFolder::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, DWORD * rgfInOut)
384 {
385     HRESULT hr = S_OK;
386     static const DWORD dwControlPanelAttributes =
387         SFGAO_HASSUBFOLDER | SFGAO_FOLDER | SFGAO_CANLINK;
388 
389     TRACE("(%p)->(cidl=%d apidl=%p mask=%p (0x%08x))\n",
390           this, cidl, apidl, rgfInOut, rgfInOut ? *rgfInOut : 0);
391 
392     if (!rgfInOut)
393         return E_INVALIDARG;
394     if (cidl && !apidl)
395         return E_INVALIDARG;
396 
397     if (*rgfInOut == 0)
398         *rgfInOut = ~0;
399 
400     if (!cidl)
401     {
402         *rgfInOut &= dwControlPanelAttributes;
403     }
404     else
405     {
406         while(cidl > 0 && *apidl)
407         {
408             pdump(*apidl);
409             if (_ILIsCPanelStruct(*apidl))
410                 *rgfInOut &= SFGAO_CANLINK;
411             else if (_ILIsSpecialFolder(*apidl))
412                 m_regFolder->GetAttributesOf(1, apidl, rgfInOut);
413             else
414                 ERR("Got an unknown pidl here!\n");
415             apidl++;
416             cidl--;
417         }
418     }
419     /* make sure SFGAO_VALIDATE is cleared, some apps depend on that */
420     *rgfInOut &= ~SFGAO_VALIDATE;
421 
422     TRACE("-- result=0x%08x\n", *rgfInOut);
423     return hr;
424 }
425 
426 /**************************************************************************
427 *    CControlPanelFolder::GetUIObjectOf
428 *
429 * PARAMETERS
430 *  HWND           hwndOwner, //[in ] Parent window for any output
431 *  UINT           cidl,      //[in ] array size
432 *  LPCITEMIDLIST* apidl,     //[in ] simple pidl array
433 *  REFIID         riid,      //[in ] Requested Interface
434 *  UINT*          prgfInOut, //[   ] reserved
435 *  LPVOID*        ppvObject) //[out] Resulting Interface
436 *
437 */
438 HRESULT WINAPI CControlPanelFolder::GetUIObjectOf(HWND hwndOwner,
439         UINT cidl, PCUITEMID_CHILD_ARRAY apidl, REFIID riid, UINT * prgfInOut, LPVOID * ppvOut)
440 {
441     LPVOID pObj = NULL;
442     HRESULT hr = E_INVALIDARG;
443 
444     TRACE("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n",
445           this, hwndOwner, cidl, apidl, shdebugstr_guid(&riid), prgfInOut, ppvOut);
446 
447     if (ppvOut) {
448         *ppvOut = NULL;
449 
450         if (IsEqualIID(riid, IID_IContextMenu) && (cidl >= 1)) {
451 
452             /* HACK: We should use callbacks from CDefaultContextMenu instead of creating one on our own */
453             BOOL bHasCpl = FALSE;
454             for (UINT i = 0; i < cidl; i++)
455             {
456                 if(_ILIsCPanelStruct(apidl[i]))
457                 {
458                     bHasCpl = TRUE;
459                 }
460             }
461 
462             if (bHasCpl)
463                 hr = ShellObjectCreatorInit<CCPLItemMenu>(cidl, apidl, riid, &pObj);
464             else
465                 hr = m_regFolder->GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, &pObj);
466         } else if (IsEqualIID(riid, IID_IDataObject) && (cidl >= 1)) {
467             hr = IDataObject_Constructor(hwndOwner, pidlRoot, apidl, cidl, TRUE, (IDataObject **)&pObj);
468         } else if ((IsEqualIID(riid, IID_IExtractIconA) || IsEqualIID(riid, IID_IExtractIconW)) && (cidl == 1)) {
469             if (_ILGetCPanelPointer(apidl[0]))
470                 hr = CCPLExtractIcon_CreateInstance(this, apidl[0], riid, &pObj);
471             else
472                 hr = m_regFolder->GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, &pObj);
473         } else {
474             hr = E_NOINTERFACE;
475         }
476 
477         if (SUCCEEDED(hr) && !pObj)
478             hr = E_OUTOFMEMORY;
479 
480         *ppvOut = pObj;
481     }
482     TRACE("(%p)->hr=0x%08x\n", this, hr);
483     return hr;
484 }
485 
486 /**************************************************************************
487 *    CControlPanelFolder::GetDisplayNameOf
488 */
489 HRESULT WINAPI CControlPanelFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD dwFlags, LPSTRRET strRet)
490 {
491     if (!pidl)
492         return S_FALSE;
493 
494     PIDLCPanelStruct *pCPanel = _ILGetCPanelPointer(pidl);
495 
496     if (pCPanel)
497     {
498         return SHSetStrRet(strRet, pCPanel->szName + pCPanel->offsDispName);
499     }
500     else if (_ILIsSpecialFolder(pidl))
501     {
502         return m_regFolder->GetDisplayNameOf(pidl, dwFlags, strRet);
503     }
504 
505     return E_FAIL;
506 }
507 
508 /**************************************************************************
509 *  CControlPanelFolder::SetNameOf
510 *  Changes the name of a file object or subfolder, possibly changing its item
511 *  identifier in the process.
512 *
513 * PARAMETERS
514 *  HWND          hwndOwner,  //[in ] Owner window for output
515 *  LPCITEMIDLIST pidl,       //[in ] simple pidl of item to change
516 *  LPCOLESTR     lpszName,   //[in ] the items new display name
517 *  DWORD         dwFlags,    //[in ] SHGNO formatting flags
518 *  LPITEMIDLIST* ppidlOut)   //[out] simple pidl returned
519 */
520 HRESULT WINAPI CControlPanelFolder::SetNameOf(HWND hwndOwner, PCUITEMID_CHILD pidl,    /*simple pidl */
521         LPCOLESTR lpName, DWORD dwFlags, PITEMID_CHILD *pPidlOut)
522 {
523     FIXME("(%p)->(%p,pidl=%p,%s,%u,%p)\n", this, hwndOwner, pidl, debugstr_w(lpName), dwFlags, pPidlOut);
524     return E_FAIL;
525 }
526 
527 HRESULT WINAPI CControlPanelFolder::GetDefaultSearchGUID(GUID *pguid)
528 {
529     FIXME("(%p)\n", this);
530     return E_NOTIMPL;
531 }
532 
533 HRESULT WINAPI CControlPanelFolder::EnumSearches(IEnumExtraSearch **ppenum)
534 {
535     FIXME("(%p)\n", this);
536     return E_NOTIMPL;
537 }
538 
539 HRESULT WINAPI CControlPanelFolder::GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay)
540 {
541     TRACE("(%p)\n", this);
542 
543     if (pSort) *pSort = 0;
544     if (pDisplay) *pDisplay = 0;
545     return S_OK;
546 }
547 
548 HRESULT WINAPI CControlPanelFolder::GetDefaultColumnState(UINT iColumn, DWORD *pcsFlags)
549 {
550     TRACE("(%p)\n", this);
551 
552     if (!pcsFlags || iColumn >= CONTROLPANEL_COL_COUNT)
553         return E_INVALIDARG;
554     *pcsFlags = ControlPanelSFHeader[iColumn].pcsFlags;
555     return S_OK;
556 }
557 
558 HRESULT WINAPI CControlPanelFolder::GetDetailsEx(PCUITEMID_CHILD pidl, const SHCOLUMNID *pscid, VARIANT *pv)
559 {
560     FIXME("(%p)\n", this);
561     return E_NOTIMPL;
562 }
563 
564 HRESULT WINAPI CControlPanelFolder::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDETAILS *psd)
565 {
566     if (!psd || iColumn >= CONTROLPANEL_COL_COUNT)
567         return E_INVALIDARG;
568 
569     if (!pidl)
570     {
571         psd->fmt = ControlPanelSFHeader[iColumn].fmt;
572         psd->cxChar = ControlPanelSFHeader[iColumn].cxChar;
573         return SHSetStrRet(&psd->str, shell32_hInstance, ControlPanelSFHeader[iColumn].colnameid);
574     }
575     else if (_ILIsSpecialFolder(pidl))
576     {
577         return m_regFolder->GetDetailsOf(pidl, iColumn, psd);
578     }
579     else
580     {
581         PIDLCPanelStruct *pCPanel = _ILGetCPanelPointer(pidl);
582 
583         if (!pCPanel)
584             return E_FAIL;
585 
586         switch(iColumn)
587         {
588             case CONTROLPANEL_COL_NAME:
589                 return SHSetStrRet(&psd->str, pCPanel->szName + pCPanel->offsDispName);
590             case CONTROLPANEL_COL_COMMENT:
591                 return SHSetStrRet(&psd->str, pCPanel->szName + pCPanel->offsComment);
592         }
593     }
594 
595     return E_FAIL;
596 }
597 
598 HRESULT WINAPI CControlPanelFolder::MapColumnToSCID(UINT column, SHCOLUMNID *pscid)
599 {
600     FIXME("(%p)\n", this);
601     return E_NOTIMPL;
602 }
603 
604 /************************************************************************
605  *    CControlPanelFolder::GetClassID
606  */
607 HRESULT WINAPI CControlPanelFolder::GetClassID(CLSID *lpClassId)
608 {
609     TRACE("(%p)\n", this);
610 
611     if (!lpClassId)
612         return E_POINTER;
613     *lpClassId = CLSID_ControlPanel;
614 
615     return S_OK;
616 }
617 
618 /************************************************************************
619  *    CControlPanelFolder::Initialize
620  *
621  * NOTES: it makes no sense to change the pidl
622  */
623 HRESULT WINAPI CControlPanelFolder::Initialize(PCIDLIST_ABSOLUTE pidl)
624 {
625     if (pidlRoot)
626         SHFree((LPVOID)pidlRoot);
627 
628     pidlRoot = ILClone(pidl);
629 
630     /* Create the inner reg folder */
631     HRESULT hr;
632     hr = CRegFolder_CreateInstance(&CLSID_ControlPanel,
633                                    pidlRoot,
634                                    L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}",
635                                    L"ControlPanel",
636                                    IID_PPV_ARG(IShellFolder2, &m_regFolder));
637     if (FAILED_UNEXPECTEDLY(hr))
638         return hr;
639 
640     return S_OK;
641 }
642 
643 /**************************************************************************
644  *    CControlPanelFolder::GetCurFolder
645  */
646 HRESULT WINAPI CControlPanelFolder::GetCurFolder(PIDLIST_ABSOLUTE * pidl)
647 {
648     TRACE("(%p)->(%p)\n", this, pidl);
649 
650     if (!pidl)
651         return E_POINTER;
652     *pidl = ILClone(pidlRoot);
653     return S_OK;
654 }
655 
656 CCPLItemMenu::CCPLItemMenu()
657 {
658     m_apidl = NULL;
659     m_cidl = 0;
660 }
661 
662 HRESULT WINAPI CCPLItemMenu::Initialize(UINT cidl, PCUITEMID_CHILD_ARRAY apidl)
663 {
664     m_cidl = cidl;
665     m_apidl = _ILCopyaPidl(apidl, m_cidl);
666     if (m_cidl && !m_apidl)
667         return E_OUTOFMEMORY;
668 
669     return S_OK;
670 }
671 
672 CCPLItemMenu::~CCPLItemMenu()
673 {
674     _ILFreeaPidl(m_apidl, m_cidl);
675 }
676 
677 HRESULT WINAPI CCPLItemMenu::QueryContextMenu(
678     HMENU hMenu,
679     UINT indexMenu,
680     UINT idCmdFirst,
681     UINT idCmdLast,
682     UINT uFlags)
683 {
684     _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst, MFT_STRING, MAKEINTRESOURCEW(IDS_OPEN), MFS_DEFAULT);
685     _InsertMenuItemW(hMenu, indexMenu++, TRUE, IDC_STATIC, MFT_SEPARATOR, NULL, MFS_ENABLED);
686     _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + 1, MFT_STRING, MAKEINTRESOURCEW(IDS_CREATELINK), MFS_ENABLED);
687 
688     return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 2);
689 }
690 
691 EXTERN_C
692 void WINAPI Control_RunDLLW(HWND hWnd, HINSTANCE hInst, LPCWSTR cmd, DWORD nCmdShow);
693 
694 /**************************************************************************
695 * ICPanel_IContextMenu_InvokeCommand()
696 */
697 HRESULT WINAPI CCPLItemMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
698 {
699     HRESULT hResult;
700 
701     PIDLCPanelStruct *pCPanel = _ILGetCPanelPointer(m_apidl[0]);
702     if(!pCPanel)
703         return E_FAIL;
704 
705     TRACE("(%p)->(invcom=%p verb=%p wnd=%p)\n", this, lpcmi, lpcmi->lpVerb, lpcmi->hwnd);
706 
707     if (lpcmi->lpVerb == MAKEINTRESOURCEA(0))
708     {
709         /* Hardcode the command here; Executing a cpl file would be fine but we also need to run things like console.dll */
710         WCHAR wszParams[MAX_PATH];
711         PCWSTR wszFile = L"rundll32.exe";
712         PCWSTR wszFormat = L"shell32.dll,Control_RunDLL %s,%s";
713 
714         wsprintfW(wszParams, wszFormat, pCPanel->szName, pCPanel->szName + pCPanel->offsDispName);
715 
716         /* Note: we pass the applet name to Control_RunDLL to distinguish between multiple applets in one .cpl file */
717         ShellExecuteW(NULL, NULL, wszFile, wszParams, NULL, 0);
718     }
719     else if (lpcmi->lpVerb == MAKEINTRESOURCEA(1)) //FIXME
720     {
721         CComPtr<IDataObject> pDataObj;
722         LPITEMIDLIST pidl = _ILCreateControlPanel();
723 
724         hResult = SHCreateDataObject(pidl, m_cidl, m_apidl, NULL, IID_PPV_ARG(IDataObject, &pDataObj));
725         if (FAILED(hResult))
726             return hResult;
727 
728         SHFree(pidl);
729 
730         //FIXME: Use SHCreateLinks
731         CComPtr<IShellFolder> psf;
732         CComPtr<IDropTarget> pDT;
733 
734         hResult = SHGetDesktopFolder(&psf);
735         if (FAILED(hResult))
736             return hResult;
737 
738         hResult = psf->CreateViewObject(NULL, IID_PPV_ARG(IDropTarget, &pDT));
739         if (FAILED(hResult))
740             return hResult;
741 
742         SHSimulateDrop(pDT, pDataObj, MK_CONTROL|MK_SHIFT, NULL, NULL);
743     }
744     return S_OK;
745 }
746 
747 /**************************************************************************
748  *  ICPanel_IContextMenu_GetCommandString()
749  *
750  */
751 HRESULT WINAPI CCPLItemMenu::GetCommandString(
752     UINT_PTR idCommand,
753     UINT uFlags,
754     UINT* lpReserved,
755     LPSTR lpszName,
756     UINT uMaxNameLen)
757 {
758     TRACE("(%p)->(idcom=%lx flags=%x %p name=%p len=%x)\n", this, idCommand, uFlags, lpReserved, lpszName, uMaxNameLen);
759 
760     FIXME("unknown command string\n");
761     return E_FAIL;
762 }
763 
764 /**************************************************************************
765 * ICPanel_IContextMenu_HandleMenuMsg()
766 */
767 HRESULT WINAPI CCPLItemMenu::HandleMenuMsg(
768     UINT uMsg,
769     WPARAM wParam,
770     LPARAM lParam)
771 {
772     TRACE("ICPanel_IContextMenu_HandleMenuMsg (%p)->(msg=%x wp=%lx lp=%lx)\n", this, uMsg, wParam, lParam);
773 
774     return E_NOTIMPL;
775 }
776