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