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