reactos/dll/shellext/ntobjshex/ntobjenum.cpp
Katayama Hirofumi MZ de8e1553ec
[NTOBJSHEX] Use STDMETHOD macro and keyword override (#6566)
For simplicity and short typing.
JIRA issue: CORE-19469
- Replace "virtual HRESULT 
  STDMETHODCALLTYPE m" with
  "STDMETHOD(m)" (m is a
  method name).
- Replace "virtual t
  STDMETHODCALLTYPE m"
  with "STDMETHOD_(t, m)"
  (t is a type. m is a method name).
- Use "override" keyword as possible.
2024-03-05 00:34:21 +09:00

740 lines
20 KiB
C++

/*
* PROJECT: NT Object Namespace shell extension
* LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
* PURPOSE: NT Object Namespace enumeration functions
* COPYRIGHT: Copyright 2004-2005 Martin Fuchs <martin-fuchs@gmx.net>
*/
#include "precomp.h"
#include <strsafe.h>
static struct RootKeyEntry {
HKEY key;
PCWSTR keyName;
} RootKeys [] = {
{ HKEY_CLASSES_ROOT, L"HKEY_CLASSES_ROOT" },
{ HKEY_CURRENT_USER, L"HKEY_CURRENT_USER" },
{ HKEY_LOCAL_MACHINE, L"HKEY_LOCAL_MACHINE" },
{ HKEY_USERS, L"HKEY_USERS" },
{ HKEY_CURRENT_CONFIG, L"HKEY_CURRENT_CONFIG" }
};
typedef NTSTATUS(__stdcall* pfnNtGenericOpen)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES);
typedef NTSTATUS(__stdcall* pfnNtOpenFile)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PIO_STATUS_BLOCK, ULONG, ULONG);
const LPCWSTR ObjectTypeNames [] = {
L"Directory", L"SymbolicLink",
L"Mutant", L"Section", L"Event", L"Semaphore",
L"Timer", L"Key", L"EventPair", L"IoCompletion",
L"Device", L"File", L"Controller", L"Profile",
L"Type", L"Desktop", L"WindowStation", L"Driver",
L"Token", L"Process", L"Thread", L"Adapter", L"Port",
0
};
const LPCWSTR RegistryTypeNames [] = {
L"REG_NONE",
L"REG_SZ",
L"REG_EXPAND_SZ",
L"REG_BINARY",
L"REG_DWORD",
L"REG_DWORD_BIG_ENDIAN",
L"REG_LINK",
L"REG_MULTI_SZ",
L"REG_RESOURCE_LIST",
L"REG_FULL_RESOURCE_DESCRIPTOR",
L"REG_RESOURCE_REQUIREMENTS_LIST",
L"REG_QWORD"
};
static DWORD NtOpenObject(OBJECT_TYPE type, PHANDLE phandle, DWORD access, LPCWSTR path)
{
UNICODE_STRING ustr;
RtlInitUnicodeString(&ustr, path);
OBJECT_ATTRIBUTES open_struct = { sizeof(OBJECT_ATTRIBUTES), 0x00, &ustr, 0x40 };
if (type != FILE_OBJECT)
access |= STANDARD_RIGHTS_READ;
IO_STATUS_BLOCK ioStatusBlock;
switch (type)
{
case DIRECTORY_OBJECT: return NtOpenDirectoryObject(phandle, access, &open_struct);
case SYMBOLICLINK_OBJECT: return NtOpenSymbolicLinkObject(phandle, access, &open_struct);
case MUTANT_OBJECT: return NtOpenMutant(phandle, access, &open_struct);
case SECTION_OBJECT: return NtOpenSection(phandle, access, &open_struct);
case EVENT_OBJECT: return NtOpenEvent(phandle, access, &open_struct);
case SEMAPHORE_OBJECT: return NtOpenSemaphore(phandle, access, &open_struct);
case TIMER_OBJECT: return NtOpenTimer(phandle, access, &open_struct);
case KEY_OBJECT: return NtOpenKey(phandle, access, &open_struct);
case EVENTPAIR_OBJECT: return NtOpenEventPair(phandle, access, &open_struct);
case IOCOMPLETION_OBJECT: return NtOpenIoCompletion(phandle, access, &open_struct);
case FILE_OBJECT: return NtOpenFile(phandle, access, &open_struct, &ioStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0);
default:
return ERROR_INVALID_FUNCTION;
}
}
OBJECT_TYPE MapTypeNameToType(LPCWSTR TypeName, DWORD cbTypeName)
{
if (!TypeName)
return UNKNOWN_OBJECT_TYPE;
for (UINT i = 0; i < _countof(ObjectTypeNames); i++)
{
LPCWSTR typeName = ObjectTypeNames[i];
if (!StrCmpNW(typeName, TypeName, cbTypeName / sizeof(WCHAR)))
{
return (OBJECT_TYPE) i;
}
}
return UNKNOWN_OBJECT_TYPE;
}
HRESULT ReadRegistryValue(HKEY root, PCWSTR path, PCWSTR valueName, PVOID * valueData, PDWORD valueLength)
{
HKEY hkey;
DWORD res;
if (root)
{
res = RegOpenKeyExW(root, *path == '\\' ? path + 1 : path, 0, STANDARD_RIGHTS_READ | KEY_QUERY_VALUE, &hkey);
}
else
{
res = NtOpenObject(KEY_OBJECT, (PHANDLE) &hkey, STANDARD_RIGHTS_READ | KEY_QUERY_VALUE, path);
}
if (!NT_SUCCESS(res))
{
ERR("RegOpenKeyExW failed for path %S with status=%x\n", path, res);
return HRESULT_FROM_NT(res);
}
res = RegQueryValueExW(hkey, valueName, NULL, NULL, NULL, valueLength);
if (!NT_SUCCESS(res))
{
ERR("RegQueryValueExW failed for path %S with status=%x\n", path, res);
return HRESULT_FROM_NT(res);
}
if (*valueLength > 0)
{
PBYTE data = (PBYTE) CoTaskMemAlloc(*valueLength);
*valueData = data;
res = RegQueryValueExW(hkey, valueName, NULL, NULL, data, valueLength);
if (!NT_SUCCESS(res))
{
CoTaskMemFree(data);
*valueData = NULL;
RegCloseKey(hkey);
ERR("RegOpenKeyExW failed for path %S with status=%x\n", path, res);
return HRESULT_FROM_NT(res);
}
}
else
{
*valueData = NULL;
}
RegCloseKey(hkey);
return S_OK;
}
HRESULT GetNTObjectSymbolicLinkTarget(LPCWSTR path, LPCWSTR entryName, PUNICODE_STRING LinkTarget)
{
HANDLE handle;
WCHAR buffer[MAX_PATH];
LPWSTR pend = buffer;
StringCbCopyExW(buffer, sizeof(buffer), path, &pend, NULL, 0);
if (pend[-1] != '\\')
{
*pend++ = '\\';
*pend = 0;
}
StringCbCatW(buffer, sizeof(buffer), entryName);
DbgPrint("GetNTObjectSymbolicLinkTarget %d\n", buffer);
LinkTarget->Length = 0;
DWORD err = NtOpenObject(SYMBOLICLINK_OBJECT, &handle, SYMBOLIC_LINK_QUERY, buffer);
if (!NT_SUCCESS(err))
return HRESULT_FROM_NT(err);
err = NtQuerySymbolicLinkObject(handle, LinkTarget, NULL);
if (!NT_SUCCESS(err))
return HRESULT_FROM_NT(err);
NtClose(handle);
return S_OK;
}
class CEnumRegRoot :
public CComObjectRootEx<CComMultiThreadModelNoCS>,
public IEnumIDList
{
UINT m_idx;
public:
CEnumRegRoot()
: m_idx(0)
{
}
~CEnumRegRoot()
{
}
HRESULT EnumerateNext(LPITEMIDLIST* ppidl)
{
if (m_idx >= _countof(RootKeys))
return S_FALSE;
RootKeyEntry& key = RootKeys[m_idx++];
PCWSTR name = key.keyName;
DWORD cchName = wcslen(name);
REG_ENTRY_TYPE otype = REG_ENTRY_ROOT;
DWORD entryBufferLength = FIELD_OFFSET(RegPidlEntry, entryName) + sizeof(WCHAR) + cchName * sizeof(WCHAR);
// allocate space for the terminator
entryBufferLength += FIELD_OFFSET(SHITEMID, abID);
RegPidlEntry* entry = (RegPidlEntry*) CoTaskMemAlloc(entryBufferLength);
if (!entry)
return E_OUTOFMEMORY;
memset(entry, 0, entryBufferLength);
entry->cb = FIELD_OFFSET(RegPidlEntry, entryName);
entry->magic = REGISTRY_PIDL_MAGIC;
entry->entryType = otype;
entry->rootKey = key.key;
if (cchName > 0)
{
entry->entryNameLength = cchName * sizeof(WCHAR);
StringCbCopyNW(entry->entryName, entryBufferLength, name, entry->entryNameLength);
entry->cb += entry->entryNameLength + sizeof(WCHAR);
}
else
{
entry->entryNameLength = 0;
entry->entryName[0] = 0;
entry->cb += sizeof(WCHAR);
}
if (ppidl)
*ppidl = (LPITEMIDLIST) entry;
return S_OK;
}
STDMETHODIMP Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched) override
{
if (pceltFetched)
*pceltFetched = 0;
while (celt-- > 0)
{
HRESULT hr = EnumerateNext(rgelt);
if (hr != S_OK)
return hr;
if (pceltFetched)
(*pceltFetched)++;
if (rgelt)
rgelt++;
}
return S_OK;
}
STDMETHODIMP Skip(ULONG celt) override
{
while (celt > 0)
{
HRESULT hr = EnumerateNext(NULL);
if (FAILED(hr))
return hr;
if (hr != S_OK)
break;
}
return S_OK;
}
STDMETHODIMP Reset() override
{
return E_NOTIMPL;
}
STDMETHODIMP Clone(IEnumIDList **ppenum) override
{
return E_NOTIMPL;
}
DECLARE_NOT_AGGREGATABLE(CEnumRegRoot)
DECLARE_PROTECT_FINAL_CONSTRUCT()
BEGIN_COM_MAP(CEnumRegRoot)
COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList)
END_COM_MAP()
};
class CEnumRegKey :
public CComObjectRootEx<CComMultiThreadModelNoCS>,
public IEnumIDList
{
PCWSTR m_path;
HKEY m_hkey;
BOOL m_values;
int m_idx;
public:
CEnumRegKey()
: m_path(NULL), m_hkey(NULL), m_values(FALSE), m_idx(0)
{
}
~CEnumRegKey()
{
RegCloseKey(m_hkey);
}
HRESULT Initialize(PCWSTR path, HKEY root)
{
m_path = path;
DWORD res;
if (root)
{
res = RegOpenKeyExW(root, *path == '\\' ? path + 1 : path, 0, STANDARD_RIGHTS_READ | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &m_hkey);
}
else
{
res = NtOpenObject(KEY_OBJECT, (PHANDLE) &m_hkey, STANDARD_RIGHTS_READ | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, path);
}
if (!NT_SUCCESS(res))
{
ERR("RegOpenKeyExW failed for path %S with status=%x\n", path, res);
return HRESULT_FROM_NT(res);
}
return S_OK;
}
HRESULT NextKey(LPITEMIDLIST* ppidl)
{
WCHAR name[MAX_PATH];
DWORD cchName = _countof(name);
WCHAR className[MAX_PATH];
DWORD cchClass = _countof(className);
if (RegEnumKeyExW(m_hkey, m_idx++, name, &cchName, 0, className, &cchClass, NULL))
return S_FALSE;
name[cchName] = 0;
className[cchClass] = 0;
REG_ENTRY_TYPE otype = REG_ENTRY_KEY;
DWORD entryBufferLength = FIELD_OFFSET(RegPidlEntry, entryName) + sizeof(WCHAR) + cchName * sizeof(WCHAR);
if (cchClass > 0)
{
entryBufferLength += sizeof(WCHAR) + cchClass * sizeof(WCHAR);
}
// allocate space for the terminator
entryBufferLength += FIELD_OFFSET(SHITEMID, abID);
RegPidlEntry* entry = (RegPidlEntry*) CoTaskMemAlloc(entryBufferLength);
if (!entry)
return E_OUTOFMEMORY;
memset(entry, 0, entryBufferLength);
entry->cb = FIELD_OFFSET(RegPidlEntry, entryName);
entry->magic = REGISTRY_PIDL_MAGIC;
entry->entryType = otype;
if (cchName > 0)
{
entry->entryNameLength = cchName * sizeof(WCHAR);
StringCbCopyNW(entry->entryName, entryBufferLength, name, entry->entryNameLength);
entry->cb += entry->entryNameLength + sizeof(WCHAR);
}
else
{
entry->entryNameLength = 0;
entry->entryName[0] = 0;
entry->cb += sizeof(WCHAR);
}
if (cchClass)
{
PWSTR contentData = (PWSTR) ((PBYTE) entry + entry->cb);
DWORD remainingSpace = entryBufferLength - entry->cb;
entry->contentsLength = cchClass * sizeof(WCHAR);
StringCbCopyNW(contentData, remainingSpace, className, entry->contentsLength);
entry->cb += entry->contentsLength + sizeof(WCHAR);
}
if (ppidl)
*ppidl = (LPITEMIDLIST) entry;
return S_OK;
}
HRESULT NextValue(LPITEMIDLIST* ppidl)
{
WCHAR name[MAX_PATH];
DWORD cchName = _countof(name);
DWORD type = 0;
DWORD dataSize = 0;
if (RegEnumValueW(m_hkey, m_idx++, name, &cchName, 0, &type, NULL, &dataSize))
return S_FALSE;
REG_ENTRY_TYPE otype = REG_ENTRY_VALUE;
DWORD entryBufferLength = FIELD_OFFSET(RegPidlEntry, entryName) + sizeof(WCHAR) + cchName * sizeof(WCHAR);
#define MAX_EMBEDDED_DATA 32
BOOL copyData = dataSize <= MAX_EMBEDDED_DATA;
if (copyData)
{
entryBufferLength += dataSize + sizeof(WCHAR);
otype = REG_ENTRY_VALUE_WITH_CONTENT;
}
// allocate space for the terminator
entryBufferLength += FIELD_OFFSET(SHITEMID, abID);
RegPidlEntry* entry = (RegPidlEntry*) CoTaskMemAlloc(entryBufferLength);
if (!entry)
return E_OUTOFMEMORY;
memset(entry, 0, entryBufferLength);
entry->cb = FIELD_OFFSET(RegPidlEntry, entryName);
entry->magic = REGISTRY_PIDL_MAGIC;
entry->entryType = otype;
entry->contentType = type;
if (cchName > 0)
{
entry->entryNameLength = cchName * sizeof(WCHAR);
StringCbCopyNW(entry->entryName, entryBufferLength, name, entry->entryNameLength);
entry->cb += entry->entryNameLength + sizeof(WCHAR);
}
else
{
entry->entryNameLength = 0;
entry->entryName[0] = 0;
entry->cb += sizeof(WCHAR);
}
if (copyData)
{
PBYTE contentData = (PBYTE) ((PBYTE) entry + entry->cb);
entry->contentsLength = dataSize;
// In case it's an unterminated string, RegGetValue will add the NULL termination
dataSize += sizeof(WCHAR);
if (!RegQueryValueExW(m_hkey, name, NULL, NULL, contentData, &dataSize))
{
entry->cb += entry->contentsLength + sizeof(WCHAR);
}
else
{
entry->contentsLength = 0;
entry->cb += sizeof(WCHAR);
}
}
if (ppidl)
*ppidl = (LPITEMIDLIST) entry;
return S_OK;
}
HRESULT EnumerateNext(LPITEMIDLIST* ppidl)
{
if (!m_values)
{
HRESULT hr = NextKey(ppidl);
if (hr != S_FALSE)
return hr;
// switch to values.
m_values = TRUE;
m_idx = 0;
}
return NextValue(ppidl);
}
STDMETHODIMP Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched) override
{
if (pceltFetched)
*pceltFetched = 0;
while (celt-- > 0)
{
HRESULT hr = EnumerateNext(rgelt);
if (hr != S_OK)
return hr;
if (pceltFetched)
(*pceltFetched)++;
if (rgelt)
rgelt++;
}
return S_OK;
}
STDMETHODIMP Skip(ULONG celt) override
{
while (celt > 0)
{
HRESULT hr = EnumerateNext(NULL);
if (FAILED(hr))
return hr;
if (hr != S_OK)
break;
}
return S_OK;
}
STDMETHODIMP Reset() override
{
return E_NOTIMPL;
}
STDMETHODIMP Clone(IEnumIDList **ppenum) override
{
return E_NOTIMPL;
}
DECLARE_NOT_AGGREGATABLE(CEnumRegKey)
DECLARE_PROTECT_FINAL_CONSTRUCT()
BEGIN_COM_MAP(CEnumRegKey)
COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList)
END_COM_MAP()
};
class CEnumNTDirectory :
public CComObjectRootEx<CComMultiThreadModelNoCS>,
public IEnumIDList
{
WCHAR buffer[MAX_PATH];
HANDLE m_directory;
BOOL m_first;
ULONG m_enumContext;
PWSTR m_pend;
public:
CEnumNTDirectory()
: m_directory(NULL), m_first(TRUE), m_enumContext(0), m_pend(NULL)
{
}
~CEnumNTDirectory()
{
NtClose(m_directory);
}
HRESULT Initialize(PCWSTR path)
{
StringCbCopyExW(buffer, sizeof(buffer), path, &m_pend, NULL, 0);
DWORD err = NtOpenObject(DIRECTORY_OBJECT, &m_directory, FILE_LIST_DIRECTORY, buffer);
if (!NT_SUCCESS(err))
{
ERR("NtOpenDirectoryObject failed for path %S with status=%x\n", buffer, err);
return HRESULT_FROM_NT(err);
}
if (m_pend[-1] != '\\')
*m_pend++ = '\\';
return S_OK;
}
HRESULT EnumerateNext(LPITEMIDLIST* ppidl)
{
BYTE dirbuffer[2048];
if (!NT_SUCCESS(NtQueryDirectoryObject(m_directory, dirbuffer, 2048, TRUE, m_first, &m_enumContext, NULL)))
return S_FALSE;
m_first = FALSE;
// if ppidl is NULL, assume the caller was Skip(),
// so we don't care about the info
if (!ppidl)
return S_OK;
POBJECT_DIRECTORY_INFORMATION info = (POBJECT_DIRECTORY_INFORMATION) dirbuffer;
if (info->Name.Buffer)
{
StringCbCopyNW(m_pend, sizeof(buffer), info->Name.Buffer, info->Name.Length);
}
OBJECT_TYPE otype = MapTypeNameToType(info->TypeName.Buffer, info->TypeName.Length);
DWORD entryBufferLength = FIELD_OFFSET(NtPidlEntry, entryName) + sizeof(WCHAR);
if (info->Name.Buffer)
entryBufferLength += info->Name.Length;
if (otype < 0)
{
entryBufferLength += FIELD_OFFSET(NtPidlTypeData, typeName) + sizeof(WCHAR);
if (info->TypeName.Buffer)
{
entryBufferLength += info->TypeName.Length;
}
}
// allocate space for the terminator
entryBufferLength += FIELD_OFFSET(SHITEMID, abID);
NtPidlEntry* entry = (NtPidlEntry*) CoTaskMemAlloc(entryBufferLength);
if (!entry)
return E_OUTOFMEMORY;
memset(entry, 0, entryBufferLength);
entry->cb = FIELD_OFFSET(NtPidlEntry, entryName);
entry->magic = NT_OBJECT_PIDL_MAGIC;
entry->objectType = otype;
if (info->Name.Buffer)
{
entry->entryNameLength = info->Name.Length;
StringCbCopyNW(entry->entryName, entryBufferLength, info->Name.Buffer, info->Name.Length);
entry->cb += entry->entryNameLength + sizeof(WCHAR);
}
else
{
entry->entryNameLength = 0;
entry->entryName[0] = 0;
entry->cb += sizeof(WCHAR);
}
if (otype < 0)
{
NtPidlTypeData * typedata = (NtPidlTypeData*) ((PBYTE) entry + entry->cb);
DWORD remainingSpace = entryBufferLength - ((PBYTE) (typedata->typeName) - (PBYTE) entry);
if (info->TypeName.Buffer)
{
typedata->typeNameLength = info->TypeName.Length;
StringCbCopyNW(typedata->typeName, remainingSpace, info->TypeName.Buffer, info->TypeName.Length);
entry->cb += typedata->typeNameLength + sizeof(WCHAR);
}
else
{
typedata->typeNameLength = 0;
typedata->typeName[0] = 0;
entry->cb += typedata->typeNameLength + sizeof(WCHAR);
}
}
*ppidl = (LPITEMIDLIST) entry;
return S_OK;
}
STDMETHODIMP Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched) override
{
if (pceltFetched)
*pceltFetched = 0;
while (celt-- > 0)
{
HRESULT hr = EnumerateNext(rgelt);
if (hr != S_OK)
return hr;
if (pceltFetched)
(*pceltFetched)++;
if (rgelt)
rgelt++;
}
return S_OK;
}
STDMETHODIMP Skip(ULONG celt) override
{
while (celt > 0)
{
HRESULT hr = EnumerateNext(NULL);
if (FAILED(hr))
return hr;
if (hr != S_OK)
break;
}
return S_OK;
}
STDMETHODIMP Reset() override
{
return E_NOTIMPL;
}
STDMETHODIMP Clone(IEnumIDList **ppenum) override
{
return E_NOTIMPL;
}
DECLARE_NOT_AGGREGATABLE(CEnumNTDirectory)
DECLARE_PROTECT_FINAL_CONSTRUCT()
BEGIN_COM_MAP(CEnumNTDirectory)
COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList)
END_COM_MAP()
};
HRESULT GetEnumRegistryRoot(IEnumIDList ** ppil)
{
return ShellObjectCreator<CEnumRegRoot>(IID_PPV_ARG(IEnumIDList, ppil));
}
HRESULT GetEnumRegistryKey(LPCWSTR path, HKEY root, IEnumIDList ** ppil)
{
return ShellObjectCreatorInit<CEnumRegKey>(path, root, IID_PPV_ARG(IEnumIDList, ppil));
}
HRESULT GetEnumNTDirectory(LPCWSTR path, IEnumIDList ** ppil)
{
return ShellObjectCreatorInit<CEnumNTDirectory>(path, IID_PPV_ARG(IEnumIDList, ppil));
}