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