1 /* 2 * PROJECT: NT Object Namespace shell extension 3 * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) 4 * PURPOSE: NT Object Namespace enumeration functions 5 * COPYRIGHT: Copyright 2004-2005 Martin Fuchs <martin-fuchs@gmx.net> 6 */ 7 8 #include "precomp.h" 9 #include <strsafe.h> 10 11 static struct RootKeyEntry { 12 HKEY key; 13 PCWSTR keyName; 14 } RootKeys [] = { 15 { HKEY_CLASSES_ROOT, L"HKEY_CLASSES_ROOT" }, 16 { HKEY_CURRENT_USER, L"HKEY_CURRENT_USER" }, 17 { HKEY_LOCAL_MACHINE, L"HKEY_LOCAL_MACHINE" }, 18 { HKEY_USERS, L"HKEY_USERS" }, 19 { HKEY_CURRENT_CONFIG, L"HKEY_CURRENT_CONFIG" } 20 }; 21 22 typedef NTSTATUS(__stdcall* pfnNtGenericOpen)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES); 23 typedef NTSTATUS(__stdcall* pfnNtOpenFile)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PIO_STATUS_BLOCK, ULONG, ULONG); 24 25 const LPCWSTR ObjectTypeNames [] = { 26 L"Directory", L"SymbolicLink", 27 L"Mutant", L"Section", L"Event", L"Semaphore", 28 L"Timer", L"Key", L"EventPair", L"IoCompletion", 29 L"Device", L"File", L"Controller", L"Profile", 30 L"Type", L"Desktop", L"WindowStation", L"Driver", 31 L"Token", L"Process", L"Thread", L"Adapter", L"Port", 32 0 33 }; 34 35 const LPCWSTR RegistryTypeNames [] = { 36 L"REG_NONE", 37 L"REG_SZ", 38 L"REG_EXPAND_SZ", 39 L"REG_BINARY", 40 L"REG_DWORD", 41 L"REG_DWORD_BIG_ENDIAN", 42 L"REG_LINK", 43 L"REG_MULTI_SZ", 44 L"REG_RESOURCE_LIST", 45 L"REG_FULL_RESOURCE_DESCRIPTOR", 46 L"REG_RESOURCE_REQUIREMENTS_LIST", 47 L"REG_QWORD" 48 }; 49 50 static DWORD NtOpenObject(OBJECT_TYPE type, PHANDLE phandle, DWORD access, LPCWSTR path) 51 { 52 UNICODE_STRING ustr; 53 54 RtlInitUnicodeString(&ustr, path); 55 56 OBJECT_ATTRIBUTES open_struct = { sizeof(OBJECT_ATTRIBUTES), 0x00, &ustr, 0x40 }; 57 58 if (type != FILE_OBJECT) 59 access |= STANDARD_RIGHTS_READ; 60 61 IO_STATUS_BLOCK ioStatusBlock; 62 63 switch (type) 64 { 65 case DIRECTORY_OBJECT: return NtOpenDirectoryObject(phandle, access, &open_struct); 66 case SYMBOLICLINK_OBJECT: return NtOpenSymbolicLinkObject(phandle, access, &open_struct); 67 case MUTANT_OBJECT: return NtOpenMutant(phandle, access, &open_struct); 68 case SECTION_OBJECT: return NtOpenSection(phandle, access, &open_struct); 69 case EVENT_OBJECT: return NtOpenEvent(phandle, access, &open_struct); 70 case SEMAPHORE_OBJECT: return NtOpenSemaphore(phandle, access, &open_struct); 71 case TIMER_OBJECT: return NtOpenTimer(phandle, access, &open_struct); 72 case KEY_OBJECT: return NtOpenKey(phandle, access, &open_struct); 73 case EVENTPAIR_OBJECT: return NtOpenEventPair(phandle, access, &open_struct); 74 case IOCOMPLETION_OBJECT: return NtOpenIoCompletion(phandle, access, &open_struct); 75 case FILE_OBJECT: return NtOpenFile(phandle, access, &open_struct, &ioStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0); 76 default: 77 return ERROR_INVALID_FUNCTION; 78 } 79 } 80 81 OBJECT_TYPE MapTypeNameToType(LPCWSTR TypeName, DWORD cbTypeName) 82 { 83 if (!TypeName) 84 return UNKNOWN_OBJECT_TYPE; 85 86 for (UINT i = 0; i < _countof(ObjectTypeNames); i++) 87 { 88 LPCWSTR typeName = ObjectTypeNames[i]; 89 if (!StrCmpNW(typeName, TypeName, cbTypeName / sizeof(WCHAR))) 90 { 91 return (OBJECT_TYPE) i; 92 } 93 } 94 95 return UNKNOWN_OBJECT_TYPE; 96 } 97 98 HRESULT ReadRegistryValue(HKEY root, PCWSTR path, PCWSTR valueName, PVOID * valueData, PDWORD valueLength) 99 { 100 HKEY hkey; 101 102 DWORD res; 103 if (root) 104 { 105 res = RegOpenKeyExW(root, *path == '\\' ? path + 1 : path, 0, STANDARD_RIGHTS_READ | KEY_QUERY_VALUE, &hkey); 106 } 107 else 108 { 109 res = NtOpenObject(KEY_OBJECT, (PHANDLE) &hkey, STANDARD_RIGHTS_READ | KEY_QUERY_VALUE, path); 110 } 111 if (!NT_SUCCESS(res)) 112 { 113 ERR("RegOpenKeyExW failed for path %S with status=%x\n", path, res); 114 return HRESULT_FROM_NT(res); 115 } 116 117 res = RegQueryValueExW(hkey, valueName, NULL, NULL, NULL, valueLength); 118 if (!NT_SUCCESS(res)) 119 { 120 ERR("RegQueryValueExW failed for path %S with status=%x\n", path, res); 121 return HRESULT_FROM_NT(res); 122 } 123 124 if (*valueLength > 0) 125 { 126 PBYTE data = (PBYTE) CoTaskMemAlloc(*valueLength); 127 *valueData = data; 128 129 res = RegQueryValueExW(hkey, valueName, NULL, NULL, data, valueLength); 130 if (!NT_SUCCESS(res)) 131 { 132 CoTaskMemFree(data); 133 *valueData = NULL; 134 135 RegCloseKey(hkey); 136 137 ERR("RegOpenKeyExW failed for path %S with status=%x\n", path, res); 138 return HRESULT_FROM_NT(res); 139 } 140 } 141 else 142 { 143 *valueData = NULL; 144 } 145 146 RegCloseKey(hkey); 147 148 return S_OK; 149 } 150 151 HRESULT GetNTObjectSymbolicLinkTarget(LPCWSTR path, LPCWSTR entryName, PUNICODE_STRING LinkTarget) 152 { 153 HANDLE handle; 154 WCHAR buffer[MAX_PATH]; 155 LPWSTR pend = buffer; 156 157 StringCbCopyExW(buffer, sizeof(buffer), path, &pend, NULL, 0); 158 159 if (pend[-1] != '\\') 160 { 161 *pend++ = '\\'; 162 *pend = 0; 163 } 164 165 StringCbCatW(buffer, sizeof(buffer), entryName); 166 167 DbgPrint("GetNTObjectSymbolicLinkTarget %d\n", buffer); 168 169 LinkTarget->Length = 0; 170 171 DWORD err = NtOpenObject(SYMBOLICLINK_OBJECT, &handle, SYMBOLIC_LINK_QUERY, buffer); 172 if (!NT_SUCCESS(err)) 173 return HRESULT_FROM_NT(err); 174 175 err = NtQuerySymbolicLinkObject(handle, LinkTarget, NULL); 176 if (!NT_SUCCESS(err)) 177 return HRESULT_FROM_NT(err); 178 179 NtClose(handle); 180 181 return S_OK; 182 } 183 184 class CEnumRegRoot : 185 public CComObjectRootEx<CComMultiThreadModelNoCS>, 186 public IEnumIDList 187 { 188 UINT m_idx; 189 190 public: 191 CEnumRegRoot() 192 : m_idx(0) 193 { 194 } 195 196 ~CEnumRegRoot() 197 { 198 } 199 200 HRESULT EnumerateNext(LPITEMIDLIST* ppidl) 201 { 202 if (m_idx >= _countof(RootKeys)) 203 return S_FALSE; 204 205 RootKeyEntry& key = RootKeys[m_idx++]; 206 207 PCWSTR name = key.keyName; 208 DWORD cchName = wcslen(name); 209 210 REG_ENTRY_TYPE otype = REG_ENTRY_ROOT; 211 212 DWORD entryBufferLength = FIELD_OFFSET(RegPidlEntry, entryName) + sizeof(WCHAR) + cchName * sizeof(WCHAR); 213 214 // allocate space for the terminator 215 entryBufferLength += FIELD_OFFSET(SHITEMID, abID); 216 217 RegPidlEntry* entry = (RegPidlEntry*) CoTaskMemAlloc(entryBufferLength); 218 if (!entry) 219 return E_OUTOFMEMORY; 220 221 memset(entry, 0, entryBufferLength); 222 223 entry->cb = FIELD_OFFSET(RegPidlEntry, entryName); 224 entry->magic = REGISTRY_PIDL_MAGIC; 225 entry->entryType = otype; 226 entry->rootKey = key.key; 227 228 if (cchName > 0) 229 { 230 entry->entryNameLength = cchName * sizeof(WCHAR); 231 StringCbCopyNW(entry->entryName, entryBufferLength, name, entry->entryNameLength); 232 entry->cb += entry->entryNameLength + sizeof(WCHAR); 233 } 234 else 235 { 236 entry->entryNameLength = 0; 237 entry->entryName[0] = 0; 238 entry->cb += sizeof(WCHAR); 239 } 240 241 if (ppidl) 242 *ppidl = (LPITEMIDLIST) entry; 243 return S_OK; 244 } 245 246 STDMETHODIMP Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched) override 247 { 248 if (pceltFetched) 249 *pceltFetched = 0; 250 251 while (celt-- > 0) 252 { 253 HRESULT hr = EnumerateNext(rgelt); 254 if (hr != S_OK) 255 return hr; 256 257 if (pceltFetched) 258 (*pceltFetched)++; 259 if (rgelt) 260 rgelt++; 261 } 262 263 return S_OK; 264 } 265 266 STDMETHODIMP Skip(ULONG celt) override 267 { 268 while (celt > 0) 269 { 270 HRESULT hr = EnumerateNext(NULL); 271 if (FAILED(hr)) 272 return hr; 273 if (hr != S_OK) 274 break; 275 } 276 277 return S_OK; 278 } 279 280 STDMETHODIMP Reset() override 281 { 282 return E_NOTIMPL; 283 } 284 285 STDMETHODIMP Clone(IEnumIDList **ppenum) override 286 { 287 return E_NOTIMPL; 288 } 289 290 DECLARE_NOT_AGGREGATABLE(CEnumRegRoot) 291 DECLARE_PROTECT_FINAL_CONSTRUCT() 292 293 BEGIN_COM_MAP(CEnumRegRoot) 294 COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList) 295 END_COM_MAP() 296 297 }; 298 299 class CEnumRegKey : 300 public CComObjectRootEx<CComMultiThreadModelNoCS>, 301 public IEnumIDList 302 { 303 PCWSTR m_path; 304 HKEY m_hkey; 305 BOOL m_values; 306 int m_idx; 307 308 public: 309 CEnumRegKey() 310 : m_path(NULL), m_hkey(NULL), m_values(FALSE), m_idx(0) 311 { 312 } 313 314 ~CEnumRegKey() 315 { 316 RegCloseKey(m_hkey); 317 } 318 319 HRESULT Initialize(PCWSTR path, HKEY root) 320 { 321 m_path = path; 322 323 DWORD res; 324 if (root) 325 { 326 res = RegOpenKeyExW(root, *path == '\\' ? path + 1 : path, 0, STANDARD_RIGHTS_READ | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &m_hkey); 327 } 328 else 329 { 330 res = NtOpenObject(KEY_OBJECT, (PHANDLE) &m_hkey, STANDARD_RIGHTS_READ | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, path); 331 } 332 if (!NT_SUCCESS(res)) 333 { 334 ERR("RegOpenKeyExW failed for path %S with status=%x\n", path, res); 335 return HRESULT_FROM_NT(res); 336 } 337 338 return S_OK; 339 } 340 341 HRESULT NextKey(LPITEMIDLIST* ppidl) 342 { 343 WCHAR name[MAX_PATH]; 344 DWORD cchName = _countof(name); 345 346 WCHAR className[MAX_PATH]; 347 DWORD cchClass = _countof(className); 348 349 if (RegEnumKeyExW(m_hkey, m_idx++, name, &cchName, 0, className, &cchClass, NULL)) 350 return S_FALSE; 351 352 name[cchName] = 0; 353 className[cchClass] = 0; 354 355 REG_ENTRY_TYPE otype = REG_ENTRY_KEY; 356 357 DWORD entryBufferLength = FIELD_OFFSET(RegPidlEntry, entryName) + sizeof(WCHAR) + cchName * sizeof(WCHAR); 358 359 if (cchClass > 0) 360 { 361 entryBufferLength += sizeof(WCHAR) + cchClass * sizeof(WCHAR); 362 } 363 364 // allocate space for the terminator 365 entryBufferLength += FIELD_OFFSET(SHITEMID, abID); 366 367 RegPidlEntry* entry = (RegPidlEntry*) CoTaskMemAlloc(entryBufferLength); 368 if (!entry) 369 return E_OUTOFMEMORY; 370 371 memset(entry, 0, entryBufferLength); 372 373 entry->cb = FIELD_OFFSET(RegPidlEntry, entryName); 374 entry->magic = REGISTRY_PIDL_MAGIC; 375 entry->entryType = otype; 376 377 if (cchName > 0) 378 { 379 entry->entryNameLength = cchName * sizeof(WCHAR); 380 StringCbCopyNW(entry->entryName, entryBufferLength, name, entry->entryNameLength); 381 entry->cb += entry->entryNameLength + sizeof(WCHAR); 382 } 383 else 384 { 385 entry->entryNameLength = 0; 386 entry->entryName[0] = 0; 387 entry->cb += sizeof(WCHAR); 388 } 389 390 if (cchClass) 391 { 392 PWSTR contentData = (PWSTR) ((PBYTE) entry + entry->cb); 393 DWORD remainingSpace = entryBufferLength - entry->cb; 394 395 entry->contentsLength = cchClass * sizeof(WCHAR); 396 StringCbCopyNW(contentData, remainingSpace, className, entry->contentsLength); 397 398 entry->cb += entry->contentsLength + sizeof(WCHAR); 399 } 400 401 if (ppidl) 402 *ppidl = (LPITEMIDLIST) entry; 403 return S_OK; 404 } 405 406 HRESULT NextValue(LPITEMIDLIST* ppidl) 407 { 408 WCHAR name[MAX_PATH]; 409 DWORD cchName = _countof(name); 410 DWORD type = 0; 411 DWORD dataSize = 0; 412 413 if (RegEnumValueW(m_hkey, m_idx++, name, &cchName, 0, &type, NULL, &dataSize)) 414 return S_FALSE; 415 416 REG_ENTRY_TYPE otype = REG_ENTRY_VALUE; 417 418 DWORD entryBufferLength = FIELD_OFFSET(RegPidlEntry, entryName) + sizeof(WCHAR) + cchName * sizeof(WCHAR); 419 420 #define MAX_EMBEDDED_DATA 32 421 BOOL copyData = dataSize <= MAX_EMBEDDED_DATA; 422 if (copyData) 423 { 424 entryBufferLength += dataSize + sizeof(WCHAR); 425 426 otype = REG_ENTRY_VALUE_WITH_CONTENT; 427 } 428 429 // allocate space for the terminator 430 entryBufferLength += FIELD_OFFSET(SHITEMID, abID); 431 432 RegPidlEntry* entry = (RegPidlEntry*) CoTaskMemAlloc(entryBufferLength); 433 if (!entry) 434 return E_OUTOFMEMORY; 435 436 memset(entry, 0, entryBufferLength); 437 438 entry->cb = FIELD_OFFSET(RegPidlEntry, entryName); 439 entry->magic = REGISTRY_PIDL_MAGIC; 440 entry->entryType = otype; 441 entry->contentType = type; 442 443 if (cchName > 0) 444 { 445 entry->entryNameLength = cchName * sizeof(WCHAR); 446 StringCbCopyNW(entry->entryName, entryBufferLength, name, entry->entryNameLength); 447 entry->cb += entry->entryNameLength + sizeof(WCHAR); 448 } 449 else 450 { 451 entry->entryNameLength = 0; 452 entry->entryName[0] = 0; 453 entry->cb += sizeof(WCHAR); 454 } 455 456 if (copyData) 457 { 458 PBYTE contentData = (PBYTE) ((PBYTE) entry + entry->cb); 459 460 entry->contentsLength = dataSize; 461 462 // In case it's an unterminated string, RegGetValue will add the NULL termination 463 dataSize += sizeof(WCHAR); 464 465 if (!RegQueryValueExW(m_hkey, name, NULL, NULL, contentData, &dataSize)) 466 { 467 entry->cb += entry->contentsLength + sizeof(WCHAR); 468 } 469 else 470 { 471 entry->contentsLength = 0; 472 entry->cb += sizeof(WCHAR); 473 } 474 475 } 476 477 if (ppidl) 478 *ppidl = (LPITEMIDLIST) entry; 479 return S_OK; 480 } 481 482 HRESULT EnumerateNext(LPITEMIDLIST* ppidl) 483 { 484 if (!m_values) 485 { 486 HRESULT hr = NextKey(ppidl); 487 if (hr != S_FALSE) 488 return hr; 489 490 // switch to values. 491 m_values = TRUE; 492 m_idx = 0; 493 } 494 495 return NextValue(ppidl); 496 } 497 498 STDMETHODIMP Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched) override 499 { 500 if (pceltFetched) 501 *pceltFetched = 0; 502 503 while (celt-- > 0) 504 { 505 HRESULT hr = EnumerateNext(rgelt); 506 if (hr != S_OK) 507 return hr; 508 509 if (pceltFetched) 510 (*pceltFetched)++; 511 if (rgelt) 512 rgelt++; 513 } 514 515 return S_OK; 516 } 517 518 STDMETHODIMP Skip(ULONG celt) override 519 { 520 while (celt > 0) 521 { 522 HRESULT hr = EnumerateNext(NULL); 523 if (FAILED(hr)) 524 return hr; 525 if (hr != S_OK) 526 break; 527 } 528 529 return S_OK; 530 } 531 532 STDMETHODIMP Reset() override 533 { 534 return E_NOTIMPL; 535 } 536 537 STDMETHODIMP Clone(IEnumIDList **ppenum) override 538 { 539 return E_NOTIMPL; 540 } 541 542 DECLARE_NOT_AGGREGATABLE(CEnumRegKey) 543 DECLARE_PROTECT_FINAL_CONSTRUCT() 544 545 BEGIN_COM_MAP(CEnumRegKey) 546 COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList) 547 END_COM_MAP() 548 }; 549 550 class CEnumNTDirectory : 551 public CComObjectRootEx<CComMultiThreadModelNoCS>, 552 public IEnumIDList 553 { 554 WCHAR buffer[MAX_PATH]; 555 HANDLE m_directory; 556 BOOL m_first; 557 ULONG m_enumContext; 558 PWSTR m_pend; 559 560 public: 561 CEnumNTDirectory() 562 : m_directory(NULL), m_first(TRUE), m_enumContext(0), m_pend(NULL) 563 { 564 } 565 566 ~CEnumNTDirectory() 567 { 568 NtClose(m_directory); 569 } 570 571 HRESULT Initialize(PCWSTR path) 572 { 573 StringCbCopyExW(buffer, sizeof(buffer), path, &m_pend, NULL, 0); 574 575 DWORD err = NtOpenObject(DIRECTORY_OBJECT, &m_directory, FILE_LIST_DIRECTORY, buffer); 576 if (!NT_SUCCESS(err)) 577 { 578 ERR("NtOpenDirectoryObject failed for path %S with status=%x\n", buffer, err); 579 return HRESULT_FROM_NT(err); 580 } 581 582 if (m_pend[-1] != '\\') 583 *m_pend++ = '\\'; 584 585 return S_OK; 586 } 587 588 HRESULT EnumerateNext(LPITEMIDLIST* ppidl) 589 { 590 BYTE dirbuffer[2048]; 591 if (!NT_SUCCESS(NtQueryDirectoryObject(m_directory, dirbuffer, 2048, TRUE, m_first, &m_enumContext, NULL))) 592 return S_FALSE; 593 594 m_first = FALSE; 595 596 // if ppidl is NULL, assume the caller was Skip(), 597 // so we don't care about the info 598 if (!ppidl) 599 return S_OK; 600 601 POBJECT_DIRECTORY_INFORMATION info = (POBJECT_DIRECTORY_INFORMATION) dirbuffer; 602 603 if (info->Name.Buffer) 604 { 605 StringCbCopyNW(m_pend, sizeof(buffer), info->Name.Buffer, info->Name.Length); 606 } 607 608 OBJECT_TYPE otype = MapTypeNameToType(info->TypeName.Buffer, info->TypeName.Length); 609 610 DWORD entryBufferLength = FIELD_OFFSET(NtPidlEntry, entryName) + sizeof(WCHAR); 611 if (info->Name.Buffer) 612 entryBufferLength += info->Name.Length; 613 614 if (otype < 0) 615 { 616 entryBufferLength += FIELD_OFFSET(NtPidlTypeData, typeName) + sizeof(WCHAR); 617 618 if (info->TypeName.Buffer) 619 { 620 entryBufferLength += info->TypeName.Length; 621 } 622 } 623 624 // allocate space for the terminator 625 entryBufferLength += FIELD_OFFSET(SHITEMID, abID); 626 627 NtPidlEntry* entry = (NtPidlEntry*) CoTaskMemAlloc(entryBufferLength); 628 if (!entry) 629 return E_OUTOFMEMORY; 630 631 memset(entry, 0, entryBufferLength); 632 633 entry->cb = FIELD_OFFSET(NtPidlEntry, entryName); 634 entry->magic = NT_OBJECT_PIDL_MAGIC; 635 entry->objectType = otype; 636 637 if (info->Name.Buffer) 638 { 639 entry->entryNameLength = info->Name.Length; 640 StringCbCopyNW(entry->entryName, entryBufferLength, info->Name.Buffer, info->Name.Length); 641 entry->cb += entry->entryNameLength + sizeof(WCHAR); 642 } 643 else 644 { 645 entry->entryNameLength = 0; 646 entry->entryName[0] = 0; 647 entry->cb += sizeof(WCHAR); 648 } 649 650 if (otype < 0) 651 { 652 NtPidlTypeData * typedata = (NtPidlTypeData*) ((PBYTE) entry + entry->cb); 653 DWORD remainingSpace = entryBufferLength - ((PBYTE) (typedata->typeName) - (PBYTE) entry); 654 655 if (info->TypeName.Buffer) 656 { 657 typedata->typeNameLength = info->TypeName.Length; 658 StringCbCopyNW(typedata->typeName, remainingSpace, info->TypeName.Buffer, info->TypeName.Length); 659 660 entry->cb += typedata->typeNameLength + sizeof(WCHAR); 661 } 662 else 663 { 664 typedata->typeNameLength = 0; 665 typedata->typeName[0] = 0; 666 entry->cb += typedata->typeNameLength + sizeof(WCHAR); 667 } 668 } 669 670 *ppidl = (LPITEMIDLIST) entry; 671 672 return S_OK; 673 } 674 675 STDMETHODIMP Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched) override 676 { 677 if (pceltFetched) 678 *pceltFetched = 0; 679 680 while (celt-- > 0) 681 { 682 HRESULT hr = EnumerateNext(rgelt); 683 if (hr != S_OK) 684 return hr; 685 686 if (pceltFetched) 687 (*pceltFetched)++; 688 if (rgelt) 689 rgelt++; 690 } 691 692 return S_OK; 693 } 694 695 STDMETHODIMP Skip(ULONG celt) override 696 { 697 while (celt > 0) 698 { 699 HRESULT hr = EnumerateNext(NULL); 700 if (FAILED(hr)) 701 return hr; 702 if (hr != S_OK) 703 break; 704 } 705 706 return S_OK; 707 } 708 709 STDMETHODIMP Reset() override 710 { 711 return E_NOTIMPL; 712 } 713 714 STDMETHODIMP Clone(IEnumIDList **ppenum) override 715 { 716 return E_NOTIMPL; 717 } 718 719 DECLARE_NOT_AGGREGATABLE(CEnumNTDirectory) 720 DECLARE_PROTECT_FINAL_CONSTRUCT() 721 722 BEGIN_COM_MAP(CEnumNTDirectory) 723 COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList) 724 END_COM_MAP() 725 }; 726 727 HRESULT GetEnumRegistryRoot(IEnumIDList ** ppil) 728 { 729 return ShellObjectCreator<CEnumRegRoot>(IID_PPV_ARG(IEnumIDList, ppil)); 730 } 731 732 HRESULT GetEnumRegistryKey(LPCWSTR path, HKEY root, IEnumIDList ** ppil) 733 { 734 return ShellObjectCreatorInit<CEnumRegKey>(path, root, IID_PPV_ARG(IEnumIDList, ppil)); 735 } 736 737 HRESULT GetEnumNTDirectory(LPCWSTR path, IEnumIDList ** ppil) 738 { 739 return ShellObjectCreatorInit<CEnumNTDirectory>(path, IID_PPV_ARG(IEnumIDList, ppil)); 740 } 741