1 /* 2 * PROJECT: ReactOS shell extensions 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: dll/shellext/ntobjshex/ntobjfolder.cpp 5 * PURPOSE: NT Object Namespace shell extension 6 * PROGRAMMERS: David Quintana <gigaherz@gmail.com> 7 */ 8 9 #include "precomp.h" 10 11 #include <wine/unicode.h> 12 13 // {845B0FB2-66E0-416B-8F91-314E23F7C12D} 14 const GUID CLSID_NtObjectFolder = { 0x845b0fb2, 0x66e0, 0x416b, { 0x8f, 0x91, 0x31, 0x4e, 0x23, 0xf7, 0xc1, 0x2d } }; 15 16 // {F4C430C3-3A8D-4B56-A018-E598DA60C2E0} 17 static const GUID GUID_NtObjectColumns = { 0xf4c430c3, 0x3a8d, 0x4b56, { 0xa0, 0x18, 0xe5, 0x98, 0xda, 0x60, 0xc2, 0xe0 } }; 18 19 enum NtObjectColumns 20 { 21 NTOBJECT_COLUMN_NAME = 0, 22 NTOBJECT_COLUMN_TYPE, 23 NTOBJECT_COLUMN_LINKTARGET, 24 NTOBJECT_COLUMN_END 25 }; 26 27 CNtObjectFolderExtractIcon::CNtObjectFolderExtractIcon() : 28 m_NtPath(NULL), 29 m_pcidlChild(NULL) 30 { 31 32 } 33 34 CNtObjectFolderExtractIcon::~CNtObjectFolderExtractIcon() 35 { 36 if (m_pcidlChild) 37 ILFree((LPITEMIDLIST) m_pcidlChild); 38 } 39 40 HRESULT CNtObjectFolderExtractIcon::Initialize(LPCWSTR ntPath, PCIDLIST_ABSOLUTE parent, UINT cidl, PCUITEMID_CHILD_ARRAY apidl) 41 { 42 m_NtPath = ntPath; 43 if (cidl != 1) 44 return E_INVALIDARG; 45 m_pcidlChild = ILClone(apidl[0]); 46 return S_OK; 47 } 48 49 HRESULT STDMETHODCALLTYPE CNtObjectFolderExtractIcon::GetIconLocation( 50 UINT uFlags, 51 LPWSTR szIconFile, 52 UINT cchMax, 53 INT *piIndex, 54 UINT *pwFlags) 55 { 56 const NtPidlEntry * entry = (NtPidlEntry *) m_pcidlChild; 57 58 if ((entry->cb < sizeof(NtPidlEntry)) || (entry->magic != NT_OBJECT_PIDL_MAGIC)) 59 return E_INVALIDARG; 60 61 UINT flags = 0; 62 63 switch (entry->objectType) 64 { 65 case DIRECTORY_OBJECT: 66 case SYMBOLICLINK_OBJECT: 67 GetModuleFileNameW(g_hInstance, szIconFile, cchMax); 68 *piIndex = -((uFlags & GIL_OPENICON) ? IDI_NTOBJECTDIROPEN : IDI_NTOBJECTDIR); 69 *pwFlags = flags; 70 return S_OK; 71 case DEVICE_OBJECT: 72 GetModuleFileNameW(g_hInstance, szIconFile, cchMax); 73 *piIndex = -IDI_NTOBJECTDEVICE; 74 *pwFlags = flags; 75 return S_OK; 76 case PORT_OBJECT: 77 GetModuleFileNameW(g_hInstance, szIconFile, cchMax); 78 *piIndex = -IDI_NTOBJECTPORT; 79 *pwFlags = flags; 80 return S_OK; 81 case KEY_OBJECT: 82 GetModuleFileNameW(g_hInstance, szIconFile, cchMax); 83 *piIndex = -IDI_REGISTRYKEY; 84 *pwFlags = flags; 85 return S_OK; 86 default: 87 GetModuleFileNameW(g_hInstance, szIconFile, cchMax); 88 *piIndex = -IDI_NTOBJECTITEM; 89 *pwFlags = flags; 90 return S_OK; 91 } 92 } 93 94 HRESULT STDMETHODCALLTYPE CNtObjectFolderExtractIcon::Extract( 95 LPCWSTR pszFile, 96 UINT nIconIndex, 97 HICON *phiconLarge, 98 HICON *phiconSmall, 99 UINT nIconSize) 100 { 101 return SHDefExtractIconW(pszFile, nIconIndex, 0, phiconLarge, phiconSmall, nIconSize); 102 } 103 104 //----------------------------------------------------------------------------- 105 // CNtObjectFolder 106 107 CNtObjectFolder::CNtObjectFolder() 108 { 109 } 110 111 CNtObjectFolder::~CNtObjectFolder() 112 { 113 } 114 115 // IShellFolder 116 117 HRESULT STDMETHODCALLTYPE CNtObjectFolder::EnumObjects( 118 HWND hwndOwner, 119 SHCONTF grfFlags, 120 IEnumIDList **ppenumIDList) 121 { 122 return GetEnumNTDirectory(m_NtPath, ppenumIDList); 123 } 124 125 BOOL STDMETHODCALLTYPE CNtObjectFolder::IsSymLink(const NtPidlEntry * info) 126 { 127 return info->objectType == SYMBOLICLINK_OBJECT; 128 } 129 130 HRESULT STDMETHODCALLTYPE CNtObjectFolder::ResolveSymLink( 131 const NtPidlEntry * info, 132 LPITEMIDLIST * fullPidl) 133 { 134 WCHAR wbLink[MAX_PATH] = { 0 }; 135 UNICODE_STRING link; 136 RtlInitEmptyUnicodeString(&link, wbLink, sizeof(wbLink)); 137 138 *fullPidl = NULL; 139 140 HRESULT hr = GetNTObjectSymbolicLinkTarget(m_NtPath, info->entryName, &link); 141 if (FAILED_UNEXPECTEDLY(hr)) 142 return hr; 143 144 WCHAR path[MAX_PATH]; 145 146 if (link.Length == 0) 147 return E_UNEXPECTED; 148 149 if (link.Buffer[1] == L':' && isalphaW(link.Buffer[0])) 150 { 151 StringCbCopyNW(path, _countof(path), link.Buffer, link.Length); 152 153 CComPtr<IShellFolder> psfDesktop; 154 hr = SHGetDesktopFolder(&psfDesktop); 155 if (FAILED_UNEXPECTEDLY(hr)) 156 return hr; 157 158 return psfDesktop->ParseDisplayName(NULL, NULL, path, NULL, fullPidl, NULL); 159 } 160 161 StringCbCopyW(path, _countof(path), L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{845B0FB2-66E0-416B-8F91-314E23F7C12D}"); 162 PathAppend(path, link.Buffer); 163 164 CComPtr<IShellFolder> psfDesktop; 165 hr = SHGetDesktopFolder(&psfDesktop); 166 if (FAILED_UNEXPECTEDLY(hr)) 167 return hr; 168 169 LPITEMIDLIST pidl; 170 171 hr = psfDesktop->ParseDisplayName(NULL, NULL, path, NULL, &pidl, NULL); 172 if (FAILED(hr)) 173 return hr; 174 175 *fullPidl = pidl; 176 177 DumpIdList(pidl); 178 179 return S_OK; 180 } 181 182 HRESULT STDMETHODCALLTYPE CNtObjectFolder::InternalBindToObject( 183 PWSTR path, 184 const NtPidlEntry * info, 185 LPITEMIDLIST first, 186 LPCITEMIDLIST rest, 187 LPITEMIDLIST fullPidl, 188 LPBC pbcReserved, 189 IShellFolder** ppsfChild) 190 { 191 192 if (info->objectType == KEY_OBJECT) 193 { 194 return ShellObjectCreatorInit<CRegistryFolder>(fullPidl, path, (HKEY) NULL, IID_PPV_ARG(IShellFolder, ppsfChild)); 195 } 196 197 return ShellObjectCreatorInit<CNtObjectFolder>(fullPidl, path, IID_PPV_ARG(IShellFolder, ppsfChild)); 198 } 199 200 // IPersistFolder 201 HRESULT STDMETHODCALLTYPE CNtObjectFolder::Initialize(LPCITEMIDLIST pidl) 202 { 203 m_shellPidl = ILClone(pidl); 204 205 StringCbCopy(m_NtPath, _countof(m_NtPath), L"\\"); 206 207 return S_OK; 208 } 209 210 // Internal 211 HRESULT STDMETHODCALLTYPE CNtObjectFolder::Initialize(LPCITEMIDLIST pidl, PCWSTR ntPath) 212 { 213 m_shellPidl = ILClone(pidl); 214 215 StringCbCopy(m_NtPath, _countof(m_NtPath), ntPath); 216 217 return S_OK; 218 } 219 220 HRESULT STDMETHODCALLTYPE CNtObjectFolder::GetDefaultColumnState( 221 UINT iColumn, 222 SHCOLSTATEF *pcsFlags) 223 { 224 switch (iColumn) 225 { 226 case NTOBJECT_COLUMN_NAME: 227 *pcsFlags = SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT; 228 return S_OK; 229 case NTOBJECT_COLUMN_TYPE: 230 *pcsFlags = SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT; 231 return S_OK; 232 case NTOBJECT_COLUMN_LINKTARGET: 233 *pcsFlags = SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT | SHCOLSTATE_SLOW; 234 return S_OK; 235 } 236 237 return E_INVALIDARG; 238 } 239 240 HRESULT STDMETHODCALLTYPE CNtObjectFolder::GetDetailsEx( 241 LPCITEMIDLIST pidl, 242 const SHCOLUMNID *pscid, 243 VARIANT *pv) 244 { 245 const NtPidlEntry * info; 246 247 TRACE("GetDetailsEx\n"); 248 249 if (pidl) 250 { 251 HRESULT hr = GetInfoFromPidl(pidl, &info); 252 if (FAILED_UNEXPECTEDLY(hr)) 253 return hr; 254 255 static const GUID storage = PSGUID_STORAGE; 256 if (IsEqualGUID(pscid->fmtid, storage)) 257 { 258 if (pscid->pid == PID_STG_NAME) 259 { 260 return MakeVariantString(pv, info->entryName); 261 } 262 else if (pscid->pid == PID_STG_STORAGETYPE) 263 { 264 if (info->objectType < 0) 265 { 266 NtPidlTypeData * td = (NtPidlTypeData*) (((PBYTE) info) + FIELD_OFFSET(NtPidlEntry, entryName) + info->entryNameLength + sizeof(WCHAR)); 267 268 if (td->typeNameLength > 0) 269 { 270 return MakeVariantString(pv, td->typeName); 271 } 272 else 273 { 274 return MakeVariantString(pv, L"Unknown"); 275 } 276 } 277 else 278 { 279 return MakeVariantString(pv, ObjectTypeNames[info->objectType]); 280 } 281 } 282 } 283 else if (IsEqualGUID(pscid->fmtid, GUID_NtObjectColumns)) 284 { 285 if (pscid->pid == NTOBJECT_COLUMN_LINKTARGET && info->objectType == SYMBOLICLINK_OBJECT) 286 { 287 WCHAR wbLink[MAX_PATH] = { 0 }; 288 UNICODE_STRING link; 289 RtlInitEmptyUnicodeString(&link, wbLink, sizeof(wbLink)); 290 291 HRESULT hr = GetNTObjectSymbolicLinkTarget(m_NtPath, info->entryName, &link); 292 293 if (!FAILED_UNEXPECTEDLY(hr) && link.Length > 0) 294 { 295 return MakeVariantString(pv, link.Buffer); 296 } 297 } 298 299 V_VT(pv) = VT_EMPTY; 300 return S_OK; 301 } 302 } 303 304 return E_INVALIDARG; 305 } 306 307 HRESULT STDMETHODCALLTYPE CNtObjectFolder::GetDetailsOf( 308 LPCITEMIDLIST pidl, 309 UINT iColumn, 310 SHELLDETAILS *psd) 311 { 312 const NtPidlEntry * info; 313 314 TRACE("GetDetailsOf\n"); 315 316 if (pidl) 317 { 318 HRESULT hr = GetInfoFromPidl(pidl, &info); 319 if (FAILED_UNEXPECTEDLY(hr)) 320 return hr; 321 322 switch (iColumn) 323 { 324 case NTOBJECT_COLUMN_NAME: 325 psd->fmt = LVCFMT_LEFT; 326 327 MakeStrRetFromString(info->entryName, info->entryNameLength, &(psd->str)); 328 return S_OK; 329 case NTOBJECT_COLUMN_TYPE: 330 psd->fmt = LVCFMT_LEFT; 331 332 if (info->objectType < 0) 333 { 334 NtPidlTypeData * td = (NtPidlTypeData*) (((PBYTE) info) + FIELD_OFFSET(NtPidlEntry, entryName) + info->entryNameLength + sizeof(WCHAR)); 335 336 if (td->typeNameLength > 0) 337 MakeStrRetFromString(td->typeName, td->typeNameLength, &(psd->str)); 338 else 339 MakeStrRetFromString(L"Unknown", &(psd->str)); 340 } 341 else 342 MakeStrRetFromString(ObjectTypeNames[info->objectType], &(psd->str)); 343 return S_OK; 344 case NTOBJECT_COLUMN_LINKTARGET: 345 { 346 psd->fmt = LVCFMT_LEFT; 347 348 if (info->objectType == SYMBOLICLINK_OBJECT) 349 { 350 WCHAR wbLink[MAX_PATH] = { 0 }; 351 UNICODE_STRING link; 352 RtlInitEmptyUnicodeString(&link, wbLink, sizeof(wbLink)); 353 354 HRESULT hr = GetNTObjectSymbolicLinkTarget(m_NtPath, info->entryName, &link); 355 356 if (!FAILED_UNEXPECTEDLY(hr) && link.Length > 0) 357 { 358 MakeStrRetFromString(link.Buffer, link.Length, &(psd->str)); 359 return S_OK; 360 } 361 } 362 363 MakeStrRetFromString(L"", &(psd->str)); 364 return S_OK; 365 } 366 } 367 } 368 else 369 { 370 switch (iColumn) 371 { 372 case NTOBJECT_COLUMN_NAME: 373 psd->fmt = LVCFMT_LEFT; 374 psd->cxChar = 30; 375 376 // TODO: Make localizable 377 MakeStrRetFromString(L"Object Name", &(psd->str)); 378 return S_OK; 379 case NTOBJECT_COLUMN_TYPE: 380 psd->fmt = LVCFMT_LEFT; 381 psd->cxChar = 20; 382 383 // TODO: Make localizable 384 MakeStrRetFromString(L"Object Type", &(psd->str)); 385 return S_OK; 386 case NTOBJECT_COLUMN_LINKTARGET: 387 psd->fmt = LVCFMT_LEFT; 388 psd->cxChar = 30; 389 390 // TODO: Make localizable 391 MakeStrRetFromString(L"Symlink Target", &(psd->str)); 392 return S_OK; 393 } 394 } 395 396 return E_INVALIDARG; 397 } 398 399 HRESULT STDMETHODCALLTYPE CNtObjectFolder::MapColumnToSCID( 400 UINT iColumn, 401 SHCOLUMNID *pscid) 402 { 403 static const GUID storage = PSGUID_STORAGE; 404 switch (iColumn) 405 { 406 case NTOBJECT_COLUMN_NAME: 407 pscid->fmtid = storage; 408 pscid->pid = PID_STG_NAME; 409 return S_OK; 410 case NTOBJECT_COLUMN_TYPE: 411 pscid->fmtid = storage; 412 pscid->pid = PID_STG_STORAGETYPE; 413 return S_OK; 414 case NTOBJECT_COLUMN_LINKTARGET: 415 pscid->fmtid = GUID_NtObjectColumns; 416 pscid->pid = NTOBJECT_COLUMN_LINKTARGET; 417 return S_OK; 418 } 419 return E_INVALIDARG; 420 } 421 422 HRESULT CNtObjectFolder::CompareIDs(LPARAM lParam, const NtPidlEntry * first, const NtPidlEntry * second) 423 { 424 HRESULT hr; 425 426 DWORD sortMode = lParam & 0xFFFF0000; 427 DWORD column = lParam & 0x0000FFFF; 428 429 if (sortMode == SHCIDS_ALLFIELDS) 430 { 431 if (column != 0) 432 return E_INVALIDARG; 433 434 int minsize = min(first->cb, second->cb); 435 hr = MAKE_COMPARE_HRESULT(memcmp(second, first, minsize)); 436 if (hr != S_EQUAL) 437 return hr; 438 439 return MAKE_COMPARE_HRESULT(second->cb - first->cb); 440 } 441 442 switch (column) 443 { 444 case NTOBJECT_COLUMN_NAME: 445 return CompareName(lParam, first, second); 446 447 case NTOBJECT_COLUMN_TYPE: 448 return MAKE_COMPARE_HRESULT(second->objectType - first->objectType); 449 450 case NTOBJECT_COLUMN_LINKTARGET: 451 // Can't sort by link target yet 452 return E_INVALIDARG; 453 } 454 455 DbgPrint("Unsupported sorting mode.\n"); 456 return E_INVALIDARG; 457 } 458 459 ULONG CNtObjectFolder::ConvertAttributes(const NtPidlEntry * entry, PULONG inMask) 460 { 461 ULONG mask = inMask ? *inMask : 0xFFFFFFFF; 462 ULONG flags = SFGAO_HASPROPSHEET | SFGAO_CANLINK; 463 464 if (entry->objectType == DIRECTORY_OBJECT) 465 flags |= SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_BROWSABLE; 466 467 if (entry->objectType == SYMBOLICLINK_OBJECT) 468 flags |= SFGAO_LINK | SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_BROWSABLE; 469 470 if (entry->objectType == KEY_OBJECT) 471 flags |= SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_BROWSABLE; 472 473 return flags & mask; 474 } 475 476 BOOL CNtObjectFolder::IsFolder(const NtPidlEntry * info) 477 { 478 return (info->objectType == DIRECTORY_OBJECT) || 479 (info->objectType == SYMBOLICLINK_OBJECT) || 480 (info->objectType == KEY_OBJECT); 481 } 482 483 HRESULT CNtObjectFolder::GetInfoFromPidl(LPCITEMIDLIST pcidl, const NtPidlEntry ** pentry) 484 { 485 NtPidlEntry * entry = (NtPidlEntry*) &(pcidl->mkid); 486 487 if (entry->cb < sizeof(NtPidlEntry)) 488 { 489 DbgPrint("PCIDL too small %l (required %l)\n", entry->cb, sizeof(NtPidlEntry)); 490 return E_INVALIDARG; 491 } 492 493 if (entry->magic != NT_OBJECT_PIDL_MAGIC) 494 { 495 DbgPrint("PCIDL magic mismatch %04x (expected %04x)\n", entry->magic, NT_OBJECT_PIDL_MAGIC); 496 return E_INVALIDARG; 497 } 498 499 *pentry = entry; 500 return S_OK; 501 }