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