1 /* 2 * Network Places (Neighbourhood) folder 3 * 4 * Copyright 1997 Marcus Meissner 5 * Copyright 1998, 1999, 2002 Juergen Schmied 6 * Copyright 2003 Mike McCormack for Codeweavers 7 * Copyright 2009 Andrew Hill 8 * 9 * This library is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU Lesser General Public 11 * License as published by the Free Software Foundation; either 12 * version 2.1 of the License, or (at your option) any later version. 13 * 14 * This library is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * Lesser General Public License for more details. 18 * 19 * You should have received a copy of the GNU Lesser General Public 20 * License along with this library; if not, write to the Free Software 21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 22 */ 23 24 #include <precomp.h> 25 26 WINE_DEFAULT_DEBUG_CHANNEL (shell); 27 28 #define HACKY_UNC_PATHS 29 30 #ifdef HACKY_UNC_PATHS 31 LPITEMIDLIST ILCreateFromNetworkPlaceW(LPCWSTR lpNetworkPlace) 32 { 33 int cbData = sizeof(WORD) + sizeof(WCHAR) * (wcslen(lpNetworkPlace)+1); 34 LPITEMIDLIST pidl = (LPITEMIDLIST)SHAlloc(cbData + sizeof(WORD)); 35 if (!pidl) 36 return NULL; 37 38 pidl->mkid.cb = cbData; 39 wcscpy((WCHAR*)&pidl->mkid.abID[0], lpNetworkPlace); 40 *(WORD*)((char*)pidl + cbData) = 0; 41 42 return pidl; 43 } 44 #endif 45 46 /*********************************************************************** 47 * IShellFolder implementation 48 */ 49 50 HRESULT CNetFolderExtractIcon_CreateInstance(LPCITEMIDLIST pidl, REFIID riid, LPVOID * ppvOut) 51 { 52 CComPtr<IDefaultExtractIconInit> initIcon; 53 HRESULT hr = SHCreateDefaultExtractIcon(IID_PPV_ARG(IDefaultExtractIconInit, &initIcon)); 54 if (FAILED_UNEXPECTEDLY(hr)) 55 return hr; 56 57 initIcon->SetNormalIcon(swShell32Name, -IDI_SHELL_NETWORK_FOLDER); 58 59 return initIcon->QueryInterface(riid, ppvOut); 60 } 61 62 HRESULT CALLBACK NetFolderMenuCallback(IShellFolder *psf, 63 HWND hwnd, 64 IDataObject *pdtobj, 65 UINT uMsg, 66 WPARAM wParam, 67 LPARAM lParam) 68 { 69 switch (uMsg) 70 { 71 case DFM_MERGECONTEXTMENU: 72 return S_OK; 73 case DFM_INVOKECOMMAND: 74 case DFM_INVOKECOMMANDEX: 75 case DFM_GETDEFSTATICID: // Required for Windows 7 to pick a default 76 return S_FALSE; 77 } 78 return E_NOTIMPL; 79 } 80 81 class CNetFolderEnum : 82 public CEnumIDListBase 83 { 84 public: 85 CNetFolderEnum(); 86 ~CNetFolderEnum(); 87 HRESULT WINAPI Initialize(HWND hwndOwner, DWORD dwFlags); 88 BOOL CreateMyCompEnumList(DWORD dwFlags); 89 BOOL EnumerateRec(LPNETRESOURCE lpNet); 90 91 BEGIN_COM_MAP(CNetFolderEnum) 92 COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList) 93 END_COM_MAP() 94 }; 95 96 static shvheader NetworkPlacesSFHeader[] = { 97 {IDS_SHV_COLUMN_NAME, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 15}, 98 {IDS_SHV_COLUMN_CATEGORY, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10}, 99 {IDS_SHV_COLUMN_WORKGROUP, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 15}, 100 {IDS_SHV_COLUMN_NETLOCATION, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 15} 101 }; 102 103 #define COLUMN_NAME 0 104 #define COLUMN_CATEGORY 1 105 #define COLUMN_WORKGROUP 2 106 #define COLUMN_NETLOCATION 3 107 108 #define NETWORKPLACESSHELLVIEWCOLUMNS 4 109 110 CNetFolderEnum::CNetFolderEnum() 111 { 112 } 113 114 CNetFolderEnum::~CNetFolderEnum() 115 { 116 } 117 118 HRESULT WINAPI CNetFolderEnum::Initialize(HWND hwndOwner, DWORD dwFlags) 119 { 120 if (CreateMyCompEnumList(dwFlags) == FALSE) 121 return E_FAIL; 122 123 return S_OK; 124 } 125 126 /************************************************************************** 127 * CDrivesFolderEnum::CreateMyCompEnumList() 128 */ 129 130 BOOL CNetFolderEnum::EnumerateRec(LPNETRESOURCE lpNet) 131 { 132 BOOL bRet = TRUE; 133 DWORD dRet; 134 HANDLE hEnum; 135 LPNETRESOURCE lpRes; 136 DWORD dSize = 0x1000; 137 DWORD dCount = -1; 138 LPNETRESOURCE lpCur; 139 140 dRet = WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_DISK, 0, lpNet, &hEnum); 141 if (dRet != WN_SUCCESS) 142 { 143 ERR("WNetOpenEnum() failed: %x\n", dRet); 144 return FALSE; 145 } 146 147 lpRes = (LPNETRESOURCE)CoTaskMemAlloc(dSize); 148 if (!lpRes) 149 { 150 ERR("CoTaskMemAlloc() failed\n"); 151 WNetCloseEnum(hEnum); 152 return FALSE; 153 } 154 155 do 156 { 157 dSize = 0x1000; 158 dCount = -1; 159 160 memset(lpRes, 0, dSize); 161 dRet = WNetEnumResource(hEnum, &dCount, lpRes, &dSize); 162 if (dRet == WN_SUCCESS || dRet == WN_MORE_DATA) 163 { 164 lpCur = lpRes; 165 for (; dCount; dCount--) 166 { 167 TRACE("lpRemoteName: %S\n", lpCur->lpRemoteName); 168 169 if ((lpCur->dwUsage & RESOURCEUSAGE_CONTAINER) == RESOURCEUSAGE_CONTAINER) 170 { 171 TRACE("Found provider: %S\n", lpCur->lpProvider); 172 EnumerateRec(lpCur); 173 } 174 else 175 { 176 LPITEMIDLIST pidl; 177 178 #ifdef HACKY_UNC_PATHS 179 pidl = ILCreateFromNetworkPlaceW(lpCur->lpRemoteName); 180 #endif 181 if (pidl != NULL) 182 bRet = AddToEnumList(pidl); 183 else 184 { 185 ERR("ILCreateFromPathW() failed\n"); 186 bRet = FALSE; 187 break; 188 } 189 } 190 191 lpCur++; 192 } 193 } 194 } while (dRet != WN_NO_MORE_ENTRIES); 195 196 CoTaskMemFree(lpRes); 197 WNetCloseEnum(hEnum); 198 199 TRACE("Done: %u\n", bRet); 200 201 return bRet; 202 } 203 204 BOOL CNetFolderEnum::CreateMyCompEnumList(DWORD dwFlags) 205 { 206 BOOL bRet = TRUE; 207 208 TRACE("(%p)->(flags=0x%08x)\n", this, dwFlags); 209 210 /* enumerate the folders */ 211 if (dwFlags & SHCONTF_FOLDERS) 212 { 213 bRet = EnumerateRec(NULL); 214 } 215 216 return bRet; 217 } 218 219 CNetFolder::CNetFolder() 220 { 221 pidlRoot = NULL; 222 } 223 224 CNetFolder::~CNetFolder() 225 { 226 if (pidlRoot) 227 SHFree(pidlRoot); 228 } 229 230 /************************************************************************** 231 * CNetFolder::ParseDisplayName 232 */ 233 HRESULT WINAPI CNetFolder::ParseDisplayName(HWND hwndOwner, LPBC pbcReserved, LPOLESTR lpszDisplayName, 234 DWORD *pchEaten, PIDLIST_RELATIVE *ppidl, DWORD *pdwAttributes) 235 { 236 HRESULT hr = E_UNEXPECTED; 237 #ifdef HACKY_UNC_PATHS 238 /* FIXME: the code below is an ugly hack */ 239 240 /* Can we use a CFSFolder on that path? */ 241 DWORD attrs = GetFileAttributes(lpszDisplayName); 242 if ((attrs & FILE_ATTRIBUTE_DIRECTORY)) 243 { 244 if (pchEaten) 245 *pchEaten = 0; /* strange but like the original */ 246 247 /* YES WE CAN */ 248 249 /* Create our hacky pidl */ 250 LPITEMIDLIST pidl = ILCreateFromNetworkPlaceW(lpszDisplayName); 251 252 *ppidl = pidl; 253 if (pdwAttributes) 254 *pdwAttributes = SFGAO_FILESYSTEM | SFGAO_CANLINK | SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_FILESYSANCESTOR; 255 return S_OK; 256 } 257 #endif 258 259 TRACE("(%p)->(HWND=%p,%p,%p=%s,%p,pidl=%p,%p)\n", this, 260 hwndOwner, pbcReserved, lpszDisplayName, debugstr_w (lpszDisplayName), 261 pchEaten, ppidl, pdwAttributes); 262 263 *ppidl = 0; 264 if (pchEaten) 265 *pchEaten = 0; /* strange but like the original */ 266 267 TRACE("(%p)->(-- ret=0x%08x)\n", this, hr); 268 269 return hr; 270 } 271 272 /************************************************************************** 273 * CNetFolder::EnumObjects 274 */ 275 HRESULT WINAPI CNetFolder::EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST *ppEnumIDList) 276 { 277 return ShellObjectCreatorInit<CNetFolderEnum>(hwndOwner, dwFlags, IID_PPV_ARG(IEnumIDList, ppEnumIDList)); 278 } 279 280 /************************************************************************** 281 * CNetFolder::BindToObject 282 */ 283 HRESULT WINAPI CNetFolder::BindToObject(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut) 284 { 285 #ifdef HACKY_UNC_PATHS 286 /* Create the target folder info */ 287 PERSIST_FOLDER_TARGET_INFO pfti = {0}; 288 pfti.dwAttributes = -1; 289 pfti.csidl = -1; 290 StringCchCopyW(pfti.szTargetParsingName, MAX_PATH, (WCHAR*)pidl->mkid.abID); 291 292 return SHELL32_BindToSF(pidlRoot, &pfti, pidl, &CLSID_ShellFSFolder, riid, ppvOut); 293 #else 294 return E_NOTIMPL; 295 #endif 296 } 297 298 /************************************************************************** 299 * CNetFolder::BindToStorage 300 */ 301 HRESULT WINAPI CNetFolder::BindToStorage(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut) 302 { 303 FIXME("(%p)->(pidl=%p,%p,%s,%p) stub\n", this, 304 pidl, pbcReserved, shdebugstr_guid (&riid), ppvOut); 305 306 *ppvOut = NULL; 307 return E_NOTIMPL; 308 } 309 310 /************************************************************************** 311 * CNetFolder::CompareIDs 312 */ 313 314 HRESULT WINAPI CNetFolder::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2) 315 { 316 return E_NOTIMPL; 317 } 318 319 /************************************************************************** 320 * CNetFolder::CreateViewObject 321 */ 322 HRESULT WINAPI CNetFolder::CreateViewObject(HWND hwndOwner, REFIID riid, LPVOID *ppvOut) 323 { 324 CComPtr<IShellView> pShellView; 325 HRESULT hr = E_INVALIDARG; 326 327 TRACE("(%p)->(hwnd=%p,%s,%p)\n", this, 328 hwndOwner, shdebugstr_guid (&riid), ppvOut); 329 330 if (!ppvOut) 331 return hr; 332 333 *ppvOut = NULL; 334 335 if (IsEqualIID(riid, IID_IDropTarget)) 336 { 337 WARN("IDropTarget not implemented\n"); 338 hr = E_NOTIMPL; 339 } 340 else if (IsEqualIID(riid, IID_IContextMenu)) 341 { 342 WARN("IContextMenu not implemented\n"); 343 hr = E_NOTIMPL; 344 } 345 else if (IsEqualIID(riid, IID_IShellView)) 346 { 347 SFV_CREATE sfvparams = {sizeof(SFV_CREATE), this}; 348 hr = SHCreateShellFolderView(&sfvparams, (IShellView**)ppvOut); 349 } 350 TRACE("-- (%p)->(interface=%p)\n", this, ppvOut); 351 return hr; 352 } 353 354 /************************************************************************** 355 * CNetFolder::GetAttributesOf 356 */ 357 HRESULT WINAPI CNetFolder::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, DWORD *rgfInOut) 358 { 359 static const DWORD dwNethoodAttributes = 360 SFGAO_STORAGE | SFGAO_HASPROPSHEET | SFGAO_STORAGEANCESTOR | 361 SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_HASSUBFOLDER | SFGAO_CANRENAME | SFGAO_CANDELETE; 362 HRESULT hr = S_OK; 363 364 TRACE("(%p)->(cidl=%d apidl=%p mask=%p (0x%08x))\n", this, 365 cidl, apidl, rgfInOut, rgfInOut ? *rgfInOut : 0); 366 367 if (!rgfInOut) 368 return E_INVALIDARG; 369 if (cidl && !apidl) 370 return E_INVALIDARG; 371 372 if (*rgfInOut == 0) 373 *rgfInOut = ~0; 374 375 if(cidl == 0) 376 *rgfInOut = dwNethoodAttributes; 377 else 378 { 379 /* FIXME: Implement when enumerating items is implemented */ 380 #ifdef HACKY_UNC_PATHS 381 *rgfInOut = SFGAO_FILESYSTEM | SFGAO_CANLINK | SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_FILESYSANCESTOR; 382 #endif 383 } 384 385 /* make sure SFGAO_VALIDATE is cleared, some apps depend on that */ 386 *rgfInOut &= ~SFGAO_VALIDATE; 387 388 TRACE("-- result=0x%08x\n", *rgfInOut); 389 return hr; 390 } 391 392 /************************************************************************** 393 * CNetFolder::GetUIObjectOf 394 * 395 * PARAMETERS 396 * hwndOwner [in] Parent window for any output 397 * cidl [in] array size 398 * apidl [in] simple pidl array 399 * riid [in] Requested Interface 400 * prgfInOut [ ] reserved 401 * ppvObject [out] Resulting Interface 402 * 403 */ 404 HRESULT WINAPI CNetFolder::GetUIObjectOf(HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, REFIID riid, 405 UINT * prgfInOut, LPVOID * ppvOut) 406 { 407 LPVOID pObj = NULL; 408 HRESULT hr = E_INVALIDARG; 409 410 TRACE("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n", this, 411 hwndOwner, cidl, apidl, shdebugstr_guid (&riid), prgfInOut, ppvOut); 412 413 if (!ppvOut) 414 return hr; 415 416 *ppvOut = NULL; 417 418 if (IsEqualIID(riid, IID_IContextMenu) && (cidl >= 1)) 419 { 420 IContextMenu * pCm = NULL; 421 HKEY hkey; 422 UINT cKeys = 0; 423 AddClassKeyToArray(L"Folder", &hkey, &cKeys); 424 hr = CDefFolderMenu_Create2(pidlRoot, hwndOwner, cidl, apidl, this, NetFolderMenuCallback, cKeys, &hkey, &pCm); 425 pObj = pCm; 426 } 427 else if (IsEqualIID(riid, IID_IDataObject) && (cidl >= 1)) 428 { 429 IDataObject * pDo = NULL; 430 hr = IDataObject_Constructor (hwndOwner, pidlRoot, apidl, cidl, TRUE, &pDo); 431 pObj = pDo; 432 } 433 else if ((IsEqualIID(riid, IID_IExtractIconA) || IsEqualIID(riid, IID_IExtractIconW)) && (cidl == 1)) 434 { 435 hr = CNetFolderExtractIcon_CreateInstance(apidl[0], riid, &pObj); 436 } 437 else if (IsEqualIID(riid, IID_IDropTarget) && (cidl >= 1)) 438 { 439 IDropTarget * pDt = NULL; 440 hr = this->QueryInterface(IID_PPV_ARG(IDropTarget, &pDt)); 441 pObj = pDt; 442 } 443 else 444 hr = E_NOINTERFACE; 445 446 if (SUCCEEDED(hr) && !pObj) 447 hr = E_OUTOFMEMORY; 448 449 *ppvOut = pObj; 450 TRACE("(%p)->hr=0x%08x\n", this, hr); 451 return hr; 452 } 453 454 /************************************************************************** 455 * CNetFolder::GetDisplayNameOf 456 * 457 */ 458 HRESULT WINAPI CNetFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD dwFlags, LPSTRRET strRet) 459 { 460 if (!strRet || !pidl || !pidl->mkid.cb) 461 return E_INVALIDARG; 462 463 #ifdef HACKY_UNC_PATHS 464 return SHSetStrRet(strRet, (LPCWSTR)pidl->mkid.abID); 465 #endif 466 return E_NOTIMPL; 467 } 468 469 /************************************************************************** 470 * CNetFolder::SetNameOf 471 * Changes the name of a file object or subfolder, possibly changing its item 472 * identifier in the process. 473 * 474 * PARAMETERS 475 * hwndOwner [in] Owner window for output 476 * pidl [in] simple pidl of item to change 477 * lpszName [in] the items new display name 478 * dwFlags [in] SHGNO formatting flags 479 * ppidlOut [out] simple pidl returned 480 */ 481 HRESULT WINAPI CNetFolder::SetNameOf(HWND hwndOwner, PCUITEMID_CHILD pidl, /*simple pidl */ 482 LPCOLESTR lpName, DWORD dwFlags, PITEMID_CHILD *pPidlOut) 483 { 484 FIXME("(%p)->(%p,pidl=%p,%s,%u,%p)\n", this, 485 hwndOwner, pidl, debugstr_w (lpName), dwFlags, pPidlOut); 486 return E_FAIL; 487 } 488 489 HRESULT WINAPI CNetFolder::GetDefaultSearchGUID(GUID *pguid) 490 { 491 FIXME("(%p)\n", this); 492 return E_NOTIMPL; 493 } 494 495 HRESULT WINAPI CNetFolder::EnumSearches(IEnumExtraSearch ** ppenum) 496 { 497 FIXME("(%p)\n", this); 498 return E_NOTIMPL; 499 } 500 501 HRESULT WINAPI CNetFolder::GetDefaultColumn (DWORD dwRes, ULONG *pSort, ULONG *pDisplay) 502 { 503 TRACE("(%p)\n", this); 504 505 if (pSort) 506 *pSort = 0; 507 if (pDisplay) 508 *pDisplay = 0; 509 510 return S_OK; 511 } 512 513 HRESULT WINAPI CNetFolder::GetDefaultColumnState(UINT iColumn, DWORD *pcsFlags) 514 { 515 TRACE("(%p)\n", this); 516 517 if (!pcsFlags || iColumn >= NETWORKPLACESSHELLVIEWCOLUMNS) 518 return E_INVALIDARG; 519 *pcsFlags = NetworkPlacesSFHeader[iColumn].pcsFlags; 520 return S_OK; 521 } 522 523 HRESULT WINAPI CNetFolder::GetDetailsEx(PCUITEMID_CHILD pidl, const SHCOLUMNID *pscid, VARIANT *pv) 524 { 525 FIXME("(%p)\n", this); 526 return E_NOTIMPL; 527 } 528 529 HRESULT WINAPI CNetFolder::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDETAILS *psd) 530 { 531 if (iColumn >= NETWORKPLACESSHELLVIEWCOLUMNS) 532 return E_FAIL; 533 534 psd->fmt = NetworkPlacesSFHeader[iColumn].fmt; 535 psd->cxChar = NetworkPlacesSFHeader[iColumn].cxChar; 536 if (pidl == NULL) 537 return SHSetStrRet(&psd->str, NetworkPlacesSFHeader[iColumn].colnameid); 538 539 if (iColumn == COLUMN_NAME) 540 return GetDisplayNameOf(pidl, SHGDN_NORMAL, &psd->str); 541 542 FIXME("(%p)->(%p %i %p)\n", this, pidl, iColumn, psd); 543 544 return E_NOTIMPL; 545 } 546 547 HRESULT WINAPI CNetFolder::MapColumnToSCID(UINT column, SHCOLUMNID *pscid) 548 { 549 FIXME("(%p)\n", this); 550 551 return E_NOTIMPL; 552 } 553 554 /************************************************************************ 555 * CNetFolder::GetClassID 556 */ 557 HRESULT WINAPI CNetFolder::GetClassID(CLSID *lpClassId) 558 { 559 TRACE("(%p)\n", this); 560 561 if (!lpClassId) 562 return E_POINTER; 563 564 *lpClassId = CLSID_NetworkPlaces; 565 566 return S_OK; 567 } 568 569 /************************************************************************ 570 * CNetFolder::Initialize 571 * 572 * NOTES: it makes no sense to change the pidl 573 */ 574 HRESULT WINAPI CNetFolder::Initialize(PCIDLIST_ABSOLUTE pidl) 575 { 576 if (pidlRoot) 577 SHFree((LPVOID)pidlRoot); 578 579 pidlRoot = ILClone(pidl); 580 return S_OK; 581 } 582 583 /************************************************************************** 584 * CNetFolder::GetCurFolder 585 */ 586 HRESULT WINAPI CNetFolder::GetCurFolder(PIDLIST_ABSOLUTE *pidl) 587 { 588 TRACE("(%p)->(%p)\n", this, pidl); 589 590 if (!pidl) 591 return E_POINTER; 592 593 *pidl = ILClone(pidlRoot); 594 595 return S_OK; 596 } 597