[CRT] Massively improve performance of rand_s
[reactos.git] / dll / shellext / ntobjshex / ntobjenum.cpp
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 }