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>
11 static struct RootKeyEntry
{
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" }
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
);
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",
35 const LPCWSTR RegistryTypeNames
[] = {
41 L
"REG_DWORD_BIG_ENDIAN",
45 L
"REG_FULL_RESOURCE_DESCRIPTOR",
46 L
"REG_RESOURCE_REQUIREMENTS_LIST",
50 static DWORD
NtOpenObject(OBJECT_TYPE type
, PHANDLE phandle
, DWORD access
, LPCWSTR path
)
54 RtlInitUnicodeString(&ustr
, path
);
56 OBJECT_ATTRIBUTES open_struct
= { sizeof(OBJECT_ATTRIBUTES
), 0x00, &ustr
, 0x40 };
58 if (type
!= FILE_OBJECT
)
59 access
|= STANDARD_RIGHTS_READ
;
61 IO_STATUS_BLOCK ioStatusBlock
;
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);
77 return ERROR_INVALID_FUNCTION
;
81 OBJECT_TYPE
MapTypeNameToType(LPCWSTR TypeName
, DWORD cbTypeName
)
84 return UNKNOWN_OBJECT_TYPE
;
86 for (UINT i
= 0; i
< _countof(ObjectTypeNames
); i
++)
88 LPCWSTR typeName
= ObjectTypeNames
[i
];
89 if (!StrCmpNW(typeName
, TypeName
, cbTypeName
/ sizeof(WCHAR
)))
91 return (OBJECT_TYPE
) i
;
95 return UNKNOWN_OBJECT_TYPE
;
98 HRESULT
ReadRegistryValue(HKEY root
, PCWSTR path
, PCWSTR valueName
, PVOID
* valueData
, PDWORD valueLength
)
105 res
= RegOpenKeyExW(root
, *path
== '\\' ? path
+ 1 : path
, 0, STANDARD_RIGHTS_READ
| KEY_QUERY_VALUE
, &hkey
);
109 res
= NtOpenObject(KEY_OBJECT
, (PHANDLE
) &hkey
, STANDARD_RIGHTS_READ
| KEY_QUERY_VALUE
, path
);
111 if (!NT_SUCCESS(res
))
113 ERR("RegOpenKeyExW failed for path %S with status=%x\n", path
, res
);
114 return HRESULT_FROM_NT(res
);
117 res
= RegQueryValueExW(hkey
, valueName
, NULL
, NULL
, NULL
, valueLength
);
118 if (!NT_SUCCESS(res
))
120 ERR("RegQueryValueExW failed for path %S with status=%x\n", path
, res
);
121 return HRESULT_FROM_NT(res
);
124 if (*valueLength
> 0)
126 PBYTE data
= (PBYTE
) CoTaskMemAlloc(*valueLength
);
129 res
= RegQueryValueExW(hkey
, valueName
, NULL
, NULL
, data
, valueLength
);
130 if (!NT_SUCCESS(res
))
137 ERR("RegOpenKeyExW failed for path %S with status=%x\n", path
, res
);
138 return HRESULT_FROM_NT(res
);
151 HRESULT
GetNTObjectSymbolicLinkTarget(LPCWSTR path
, LPCWSTR entryName
, PUNICODE_STRING LinkTarget
)
154 WCHAR buffer
[MAX_PATH
];
155 LPWSTR pend
= buffer
;
157 StringCbCopyExW(buffer
, sizeof(buffer
), path
, &pend
, NULL
, 0);
159 if (pend
[-1] != '\\')
165 StringCbCatW(buffer
, sizeof(buffer
), entryName
);
167 DbgPrint("GetNTObjectSymbolicLinkTarget %d\n", buffer
);
169 LinkTarget
->Length
= 0;
171 DWORD err
= NtOpenObject(SYMBOLICLINK_OBJECT
, &handle
, SYMBOLIC_LINK_QUERY
, buffer
);
172 if (!NT_SUCCESS(err
))
173 return HRESULT_FROM_NT(err
);
175 err
= NtQuerySymbolicLinkObject(handle
, LinkTarget
, NULL
);
176 if (!NT_SUCCESS(err
))
177 return HRESULT_FROM_NT(err
);
185 public CComObjectRootEx
<CComMultiThreadModelNoCS
>,
200 HRESULT
EnumerateNext(LPITEMIDLIST
* ppidl
)
202 if (m_idx
>= _countof(RootKeys
))
205 RootKeyEntry
& key
= RootKeys
[m_idx
++];
207 PCWSTR name
= key
.keyName
;
208 DWORD cchName
= wcslen(name
);
210 REG_ENTRY_TYPE otype
= REG_ENTRY_ROOT
;
212 DWORD entryBufferLength
= FIELD_OFFSET(RegPidlEntry
, entryName
) + sizeof(WCHAR
) + cchName
* sizeof(WCHAR
);
214 // allocate space for the terminator
215 entryBufferLength
+= FIELD_OFFSET(SHITEMID
, abID
);
217 RegPidlEntry
* entry
= (RegPidlEntry
*) CoTaskMemAlloc(entryBufferLength
);
219 return E_OUTOFMEMORY
;
221 memset(entry
, 0, entryBufferLength
);
223 entry
->cb
= FIELD_OFFSET(RegPidlEntry
, entryName
);
224 entry
->magic
= REGISTRY_PIDL_MAGIC
;
225 entry
->entryType
= otype
;
226 entry
->rootKey
= key
.key
;
230 entry
->entryNameLength
= cchName
* sizeof(WCHAR
);
231 StringCbCopyNW(entry
->entryName
, entryBufferLength
, name
, entry
->entryNameLength
);
232 entry
->cb
+= entry
->entryNameLength
+ sizeof(WCHAR
);
236 entry
->entryNameLength
= 0;
237 entry
->entryName
[0] = 0;
238 entry
->cb
+= sizeof(WCHAR
);
242 *ppidl
= (LPITEMIDLIST
) entry
;
246 STDMETHODIMP
Next(ULONG celt
, LPITEMIDLIST
*rgelt
, ULONG
*pceltFetched
) override
253 HRESULT hr
= EnumerateNext(rgelt
);
266 STDMETHODIMP
Skip(ULONG celt
) override
270 HRESULT hr
= EnumerateNext(NULL
);
280 STDMETHODIMP
Reset() override
285 STDMETHODIMP
Clone(IEnumIDList
**ppenum
) override
290 DECLARE_NOT_AGGREGATABLE(CEnumRegRoot
)
291 DECLARE_PROTECT_FINAL_CONSTRUCT()
293 BEGIN_COM_MAP(CEnumRegRoot
)
294 COM_INTERFACE_ENTRY_IID(IID_IEnumIDList
, IEnumIDList
)
300 public CComObjectRootEx
<CComMultiThreadModelNoCS
>,
310 : m_path(NULL
), m_hkey(NULL
), m_values(FALSE
), m_idx(0)
319 HRESULT
Initialize(PCWSTR path
, HKEY root
)
326 res
= RegOpenKeyExW(root
, *path
== '\\' ? path
+ 1 : path
, 0, STANDARD_RIGHTS_READ
| KEY_QUERY_VALUE
| KEY_ENUMERATE_SUB_KEYS
, &m_hkey
);
330 res
= NtOpenObject(KEY_OBJECT
, (PHANDLE
) &m_hkey
, STANDARD_RIGHTS_READ
| KEY_QUERY_VALUE
| KEY_ENUMERATE_SUB_KEYS
, path
);
332 if (!NT_SUCCESS(res
))
334 ERR("RegOpenKeyExW failed for path %S with status=%x\n", path
, res
);
335 return HRESULT_FROM_NT(res
);
341 HRESULT
NextKey(LPITEMIDLIST
* ppidl
)
343 WCHAR name
[MAX_PATH
];
344 DWORD cchName
= _countof(name
);
346 WCHAR className
[MAX_PATH
];
347 DWORD cchClass
= _countof(className
);
349 if (RegEnumKeyExW(m_hkey
, m_idx
++, name
, &cchName
, 0, className
, &cchClass
, NULL
))
353 className
[cchClass
] = 0;
355 REG_ENTRY_TYPE otype
= REG_ENTRY_KEY
;
357 DWORD entryBufferLength
= FIELD_OFFSET(RegPidlEntry
, entryName
) + sizeof(WCHAR
) + cchName
* sizeof(WCHAR
);
361 entryBufferLength
+= sizeof(WCHAR
) + cchClass
* sizeof(WCHAR
);
364 // allocate space for the terminator
365 entryBufferLength
+= FIELD_OFFSET(SHITEMID
, abID
);
367 RegPidlEntry
* entry
= (RegPidlEntry
*) CoTaskMemAlloc(entryBufferLength
);
369 return E_OUTOFMEMORY
;
371 memset(entry
, 0, entryBufferLength
);
373 entry
->cb
= FIELD_OFFSET(RegPidlEntry
, entryName
);
374 entry
->magic
= REGISTRY_PIDL_MAGIC
;
375 entry
->entryType
= otype
;
379 entry
->entryNameLength
= cchName
* sizeof(WCHAR
);
380 StringCbCopyNW(entry
->entryName
, entryBufferLength
, name
, entry
->entryNameLength
);
381 entry
->cb
+= entry
->entryNameLength
+ sizeof(WCHAR
);
385 entry
->entryNameLength
= 0;
386 entry
->entryName
[0] = 0;
387 entry
->cb
+= sizeof(WCHAR
);
392 PWSTR contentData
= (PWSTR
) ((PBYTE
) entry
+ entry
->cb
);
393 DWORD remainingSpace
= entryBufferLength
- entry
->cb
;
395 entry
->contentsLength
= cchClass
* sizeof(WCHAR
);
396 StringCbCopyNW(contentData
, remainingSpace
, className
, entry
->contentsLength
);
398 entry
->cb
+= entry
->contentsLength
+ sizeof(WCHAR
);
402 *ppidl
= (LPITEMIDLIST
) entry
;
406 HRESULT
NextValue(LPITEMIDLIST
* ppidl
)
408 WCHAR name
[MAX_PATH
];
409 DWORD cchName
= _countof(name
);
413 if (RegEnumValueW(m_hkey
, m_idx
++, name
, &cchName
, 0, &type
, NULL
, &dataSize
))
416 REG_ENTRY_TYPE otype
= REG_ENTRY_VALUE
;
418 DWORD entryBufferLength
= FIELD_OFFSET(RegPidlEntry
, entryName
) + sizeof(WCHAR
) + cchName
* sizeof(WCHAR
);
420 #define MAX_EMBEDDED_DATA 32
421 BOOL copyData
= dataSize
<= MAX_EMBEDDED_DATA
;
424 entryBufferLength
+= dataSize
+ sizeof(WCHAR
);
426 otype
= REG_ENTRY_VALUE_WITH_CONTENT
;
429 // allocate space for the terminator
430 entryBufferLength
+= FIELD_OFFSET(SHITEMID
, abID
);
432 RegPidlEntry
* entry
= (RegPidlEntry
*) CoTaskMemAlloc(entryBufferLength
);
434 return E_OUTOFMEMORY
;
436 memset(entry
, 0, entryBufferLength
);
438 entry
->cb
= FIELD_OFFSET(RegPidlEntry
, entryName
);
439 entry
->magic
= REGISTRY_PIDL_MAGIC
;
440 entry
->entryType
= otype
;
441 entry
->contentType
= type
;
445 entry
->entryNameLength
= cchName
* sizeof(WCHAR
);
446 StringCbCopyNW(entry
->entryName
, entryBufferLength
, name
, entry
->entryNameLength
);
447 entry
->cb
+= entry
->entryNameLength
+ sizeof(WCHAR
);
451 entry
->entryNameLength
= 0;
452 entry
->entryName
[0] = 0;
453 entry
->cb
+= sizeof(WCHAR
);
458 PBYTE contentData
= (PBYTE
) ((PBYTE
) entry
+ entry
->cb
);
460 entry
->contentsLength
= dataSize
;
462 // In case it's an unterminated string, RegGetValue will add the NULL termination
463 dataSize
+= sizeof(WCHAR
);
465 if (!RegQueryValueExW(m_hkey
, name
, NULL
, NULL
, contentData
, &dataSize
))
467 entry
->cb
+= entry
->contentsLength
+ sizeof(WCHAR
);
471 entry
->contentsLength
= 0;
472 entry
->cb
+= sizeof(WCHAR
);
478 *ppidl
= (LPITEMIDLIST
) entry
;
482 HRESULT
EnumerateNext(LPITEMIDLIST
* ppidl
)
486 HRESULT hr
= NextKey(ppidl
);
495 return NextValue(ppidl
);
498 STDMETHODIMP
Next(ULONG celt
, LPITEMIDLIST
*rgelt
, ULONG
*pceltFetched
) override
505 HRESULT hr
= EnumerateNext(rgelt
);
518 STDMETHODIMP
Skip(ULONG celt
) override
522 HRESULT hr
= EnumerateNext(NULL
);
532 STDMETHODIMP
Reset() override
537 STDMETHODIMP
Clone(IEnumIDList
**ppenum
) override
542 DECLARE_NOT_AGGREGATABLE(CEnumRegKey
)
543 DECLARE_PROTECT_FINAL_CONSTRUCT()
545 BEGIN_COM_MAP(CEnumRegKey
)
546 COM_INTERFACE_ENTRY_IID(IID_IEnumIDList
, IEnumIDList
)
550 class CEnumNTDirectory
:
551 public CComObjectRootEx
<CComMultiThreadModelNoCS
>,
554 WCHAR buffer
[MAX_PATH
];
562 : m_directory(NULL
), m_first(TRUE
), m_enumContext(0), m_pend(NULL
)
568 NtClose(m_directory
);
571 HRESULT
Initialize(PCWSTR path
)
573 StringCbCopyExW(buffer
, sizeof(buffer
), path
, &m_pend
, NULL
, 0);
575 DWORD err
= NtOpenObject(DIRECTORY_OBJECT
, &m_directory
, FILE_LIST_DIRECTORY
, buffer
);
576 if (!NT_SUCCESS(err
))
578 ERR("NtOpenDirectoryObject failed for path %S with status=%x\n", buffer
, err
);
579 return HRESULT_FROM_NT(err
);
582 if (m_pend
[-1] != '\\')
588 HRESULT
EnumerateNext(LPITEMIDLIST
* ppidl
)
590 BYTE dirbuffer
[2048];
591 if (!NT_SUCCESS(NtQueryDirectoryObject(m_directory
, dirbuffer
, 2048, TRUE
, m_first
, &m_enumContext
, NULL
)))
596 // if ppidl is NULL, assume the caller was Skip(),
597 // so we don't care about the info
601 POBJECT_DIRECTORY_INFORMATION info
= (POBJECT_DIRECTORY_INFORMATION
) dirbuffer
;
603 if (info
->Name
.Buffer
)
605 StringCbCopyNW(m_pend
, sizeof(buffer
), info
->Name
.Buffer
, info
->Name
.Length
);
608 OBJECT_TYPE otype
= MapTypeNameToType(info
->TypeName
.Buffer
, info
->TypeName
.Length
);
610 DWORD entryBufferLength
= FIELD_OFFSET(NtPidlEntry
, entryName
) + sizeof(WCHAR
);
611 if (info
->Name
.Buffer
)
612 entryBufferLength
+= info
->Name
.Length
;
616 entryBufferLength
+= FIELD_OFFSET(NtPidlTypeData
, typeName
) + sizeof(WCHAR
);
618 if (info
->TypeName
.Buffer
)
620 entryBufferLength
+= info
->TypeName
.Length
;
624 // allocate space for the terminator
625 entryBufferLength
+= FIELD_OFFSET(SHITEMID
, abID
);
627 NtPidlEntry
* entry
= (NtPidlEntry
*) CoTaskMemAlloc(entryBufferLength
);
629 return E_OUTOFMEMORY
;
631 memset(entry
, 0, entryBufferLength
);
633 entry
->cb
= FIELD_OFFSET(NtPidlEntry
, entryName
);
634 entry
->magic
= NT_OBJECT_PIDL_MAGIC
;
635 entry
->objectType
= otype
;
637 if (info
->Name
.Buffer
)
639 entry
->entryNameLength
= info
->Name
.Length
;
640 StringCbCopyNW(entry
->entryName
, entryBufferLength
, info
->Name
.Buffer
, info
->Name
.Length
);
641 entry
->cb
+= entry
->entryNameLength
+ sizeof(WCHAR
);
645 entry
->entryNameLength
= 0;
646 entry
->entryName
[0] = 0;
647 entry
->cb
+= sizeof(WCHAR
);
652 NtPidlTypeData
* typedata
= (NtPidlTypeData
*) ((PBYTE
) entry
+ entry
->cb
);
653 DWORD remainingSpace
= entryBufferLength
- ((PBYTE
) (typedata
->typeName
) - (PBYTE
) entry
);
655 if (info
->TypeName
.Buffer
)
657 typedata
->typeNameLength
= info
->TypeName
.Length
;
658 StringCbCopyNW(typedata
->typeName
, remainingSpace
, info
->TypeName
.Buffer
, info
->TypeName
.Length
);
660 entry
->cb
+= typedata
->typeNameLength
+ sizeof(WCHAR
);
664 typedata
->typeNameLength
= 0;
665 typedata
->typeName
[0] = 0;
666 entry
->cb
+= typedata
->typeNameLength
+ sizeof(WCHAR
);
670 *ppidl
= (LPITEMIDLIST
) entry
;
675 STDMETHODIMP
Next(ULONG celt
, LPITEMIDLIST
*rgelt
, ULONG
*pceltFetched
) override
682 HRESULT hr
= EnumerateNext(rgelt
);
695 STDMETHODIMP
Skip(ULONG celt
) override
699 HRESULT hr
= EnumerateNext(NULL
);
709 STDMETHODIMP
Reset() override
714 STDMETHODIMP
Clone(IEnumIDList
**ppenum
) override
719 DECLARE_NOT_AGGREGATABLE(CEnumNTDirectory
)
720 DECLARE_PROTECT_FINAL_CONSTRUCT()
722 BEGIN_COM_MAP(CEnumNTDirectory
)
723 COM_INTERFACE_ENTRY_IID(IID_IEnumIDList
, IEnumIDList
)
727 HRESULT
GetEnumRegistryRoot(IEnumIDList
** ppil
)
729 return ShellObjectCreator
<CEnumRegRoot
>(IID_PPV_ARG(IEnumIDList
, ppil
));
732 HRESULT
GetEnumRegistryKey(LPCWSTR path
, HKEY root
, IEnumIDList
** ppil
)
734 return ShellObjectCreatorInit
<CEnumRegKey
>(path
, root
, IID_PPV_ARG(IEnumIDList
, ppil
));
737 HRESULT
GetEnumNTDirectory(LPCWSTR path
, IEnumIDList
** ppil
)
739 return ShellObjectCreatorInit
<CEnumNTDirectory
>(path
, IID_PPV_ARG(IEnumIDList
, ppil
));