mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 18:15:11 +00:00
7fff96e7fe
This fixes a crash when attempting to drag-n-drop items from NTObject Namespace and System Registry. CORE-18480, CORE-18481
540 lines
15 KiB
C++
540 lines
15 KiB
C++
/*
|
|
* PROJECT: NT Object Namespace shell extension
|
|
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
|
|
* PURPOSE: NT Object Namespace folder class implementation
|
|
* COPYRIGHT: Copyright 2015-2017 David Quintana <gigaherz@gmail.com>
|
|
*/
|
|
|
|
#include "precomp.h"
|
|
|
|
#include <wine/unicode.h>
|
|
|
|
// {845B0FB2-66E0-416B-8F91-314E23F7C12D}
|
|
const GUID CLSID_NtObjectFolder = { 0x845b0fb2, 0x66e0, 0x416b, { 0x8f, 0x91, 0x31, 0x4e, 0x23, 0xf7, 0xc1, 0x2d } };
|
|
|
|
// {F4C430C3-3A8D-4B56-A018-E598DA60C2E0}
|
|
static const GUID GUID_NtObjectColumns = { 0xf4c430c3, 0x3a8d, 0x4b56, { 0xa0, 0x18, 0xe5, 0x98, 0xda, 0x60, 0xc2, 0xe0 } };
|
|
|
|
enum NtObjectColumns
|
|
{
|
|
NTOBJECT_COLUMN_NAME = 0,
|
|
NTOBJECT_COLUMN_TYPE,
|
|
NTOBJECT_COLUMN_LINKTARGET,
|
|
NTOBJECT_COLUMN_END
|
|
};
|
|
|
|
CNtObjectFolderExtractIcon::CNtObjectFolderExtractIcon() :
|
|
m_NtPath(NULL),
|
|
m_pcidlChild(NULL)
|
|
{
|
|
|
|
}
|
|
|
|
CNtObjectFolderExtractIcon::~CNtObjectFolderExtractIcon()
|
|
{
|
|
if (m_pcidlChild)
|
|
ILFree((LPITEMIDLIST) m_pcidlChild);
|
|
}
|
|
|
|
HRESULT CNtObjectFolderExtractIcon::Initialize(LPCWSTR ntPath, PCIDLIST_ABSOLUTE parent, UINT cidl, PCUITEMID_CHILD_ARRAY apidl)
|
|
{
|
|
m_NtPath = ntPath;
|
|
if (cidl != 1)
|
|
return E_INVALIDARG;
|
|
m_pcidlChild = ILClone(apidl[0]);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE CNtObjectFolderExtractIcon::GetIconLocation(
|
|
UINT uFlags,
|
|
LPWSTR szIconFile,
|
|
UINT cchMax,
|
|
INT *piIndex,
|
|
UINT *pwFlags)
|
|
{
|
|
const NtPidlEntry * entry = (NtPidlEntry *) m_pcidlChild;
|
|
|
|
if ((entry->cb < sizeof(NtPidlEntry)) || (entry->magic != NT_OBJECT_PIDL_MAGIC))
|
|
return E_INVALIDARG;
|
|
|
|
UINT flags = 0;
|
|
|
|
switch (entry->objectType)
|
|
{
|
|
case DIRECTORY_OBJECT:
|
|
case SYMBOLICLINK_OBJECT:
|
|
GetModuleFileNameW(g_hInstance, szIconFile, cchMax);
|
|
*piIndex = -((uFlags & GIL_OPENICON) ? IDI_NTOBJECTDIROPEN : IDI_NTOBJECTDIR);
|
|
*pwFlags = flags;
|
|
return S_OK;
|
|
|
|
case DEVICE_OBJECT:
|
|
GetModuleFileNameW(g_hInstance, szIconFile, cchMax);
|
|
*piIndex = -IDI_NTOBJECTDEVICE;
|
|
*pwFlags = flags;
|
|
return S_OK;
|
|
|
|
case PORT_OBJECT:
|
|
GetModuleFileNameW(g_hInstance, szIconFile, cchMax);
|
|
*piIndex = -IDI_NTOBJECTPORT;
|
|
*pwFlags = flags;
|
|
return S_OK;
|
|
|
|
case KEY_OBJECT:
|
|
GetModuleFileNameW(g_hInstance, szIconFile, cchMax);
|
|
*piIndex = -IDI_REGISTRYKEY;
|
|
*pwFlags = flags;
|
|
return S_OK;
|
|
|
|
default:
|
|
GetModuleFileNameW(g_hInstance, szIconFile, cchMax);
|
|
*piIndex = -IDI_NTOBJECTITEM;
|
|
*pwFlags = flags;
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE CNtObjectFolderExtractIcon::Extract(
|
|
LPCWSTR pszFile,
|
|
UINT nIconIndex,
|
|
HICON *phiconLarge,
|
|
HICON *phiconSmall,
|
|
UINT nIconSize)
|
|
{
|
|
return SHDefExtractIconW(pszFile, nIconIndex, 0, phiconLarge, phiconSmall, nIconSize);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CNtObjectFolder
|
|
|
|
CNtObjectFolder::CNtObjectFolder()
|
|
{
|
|
}
|
|
|
|
CNtObjectFolder::~CNtObjectFolder()
|
|
{
|
|
}
|
|
|
|
// IShellFolder
|
|
|
|
HRESULT STDMETHODCALLTYPE CNtObjectFolder::EnumObjects(
|
|
HWND hwndOwner,
|
|
SHCONTF grfFlags,
|
|
IEnumIDList **ppenumIDList)
|
|
{
|
|
return GetEnumNTDirectory(m_NtPath, ppenumIDList);
|
|
}
|
|
|
|
BOOL STDMETHODCALLTYPE CNtObjectFolder::IsSymLink(const NtPidlEntry * info)
|
|
{
|
|
return info->objectType == SYMBOLICLINK_OBJECT;
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE CNtObjectFolder::ResolveSymLink(
|
|
const NtPidlEntry * info,
|
|
LPITEMIDLIST * fullPidl)
|
|
{
|
|
WCHAR wbLink[MAX_PATH] = { 0 };
|
|
UNICODE_STRING link;
|
|
RtlInitEmptyUnicodeString(&link, wbLink, sizeof(wbLink));
|
|
|
|
*fullPidl = NULL;
|
|
|
|
HRESULT hr = GetNTObjectSymbolicLinkTarget(m_NtPath, info->entryName, &link);
|
|
if (FAILED_UNEXPECTEDLY(hr))
|
|
return hr;
|
|
|
|
WCHAR path[MAX_PATH];
|
|
|
|
if (link.Length == 0)
|
|
return E_UNEXPECTED;
|
|
|
|
if (link.Buffer[1] == L':' && isalphaW(link.Buffer[0]))
|
|
{
|
|
StringCbCopyNW(path, sizeof(path), link.Buffer, link.Length);
|
|
|
|
CComPtr<IShellFolder> psfDesktop;
|
|
hr = SHGetDesktopFolder(&psfDesktop);
|
|
if (FAILED_UNEXPECTEDLY(hr))
|
|
return hr;
|
|
|
|
return psfDesktop->ParseDisplayName(NULL, NULL, path, NULL, fullPidl, NULL);
|
|
}
|
|
|
|
StringCbCopyW(path, sizeof(path), L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{845B0FB2-66E0-416B-8F91-314E23F7C12D}");
|
|
PathAppend(path, link.Buffer);
|
|
|
|
CComPtr<IShellFolder> psfDesktop;
|
|
hr = SHGetDesktopFolder(&psfDesktop);
|
|
if (FAILED_UNEXPECTEDLY(hr))
|
|
return hr;
|
|
|
|
LPITEMIDLIST pidl;
|
|
|
|
hr = psfDesktop->ParseDisplayName(NULL, NULL, path, NULL, &pidl, NULL);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
*fullPidl = pidl;
|
|
|
|
DumpIdList(pidl);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE CNtObjectFolder::InternalBindToObject(
|
|
PWSTR path,
|
|
const NtPidlEntry * info,
|
|
LPITEMIDLIST first,
|
|
LPCITEMIDLIST rest,
|
|
LPITEMIDLIST fullPidl,
|
|
LPBC pbcReserved,
|
|
IShellFolder** ppsfChild)
|
|
{
|
|
|
|
if (info->objectType == KEY_OBJECT)
|
|
{
|
|
return ShellObjectCreatorInit<CRegistryFolder>(fullPidl, path, (HKEY) NULL, IID_PPV_ARG(IShellFolder, ppsfChild));
|
|
}
|
|
|
|
return ShellObjectCreatorInit<CNtObjectFolder>(fullPidl, path, IID_PPV_ARG(IShellFolder, ppsfChild));
|
|
}
|
|
|
|
// IPersistFolder
|
|
HRESULT STDMETHODCALLTYPE CNtObjectFolder::Initialize(PCIDLIST_ABSOLUTE pidl)
|
|
{
|
|
m_shellPidl = ILClone(pidl);
|
|
|
|
StringCbCopyW(m_NtPath, sizeof(m_NtPath), L"\\");
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
// Internal
|
|
HRESULT STDMETHODCALLTYPE CNtObjectFolder::Initialize(PCIDLIST_ABSOLUTE pidl, PCWSTR ntPath)
|
|
{
|
|
m_shellPidl = ILClone(pidl);
|
|
|
|
StringCbCopyW(m_NtPath, sizeof(m_NtPath), ntPath);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE CNtObjectFolder::GetDefaultColumnState(
|
|
UINT iColumn,
|
|
SHCOLSTATEF *pcsFlags)
|
|
{
|
|
switch (iColumn)
|
|
{
|
|
case NTOBJECT_COLUMN_NAME:
|
|
*pcsFlags = SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT;
|
|
return S_OK;
|
|
|
|
case NTOBJECT_COLUMN_TYPE:
|
|
*pcsFlags = SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT;
|
|
return S_OK;
|
|
|
|
case NTOBJECT_COLUMN_LINKTARGET:
|
|
*pcsFlags = SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT | SHCOLSTATE_SLOW;
|
|
return S_OK;
|
|
}
|
|
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE CNtObjectFolder::GetDetailsEx(
|
|
LPCITEMIDLIST pidl,
|
|
const SHCOLUMNID *pscid,
|
|
VARIANT *pv)
|
|
{
|
|
const NtPidlEntry * info;
|
|
|
|
TRACE("GetDetailsEx\n");
|
|
|
|
if (pidl)
|
|
{
|
|
HRESULT hr = GetInfoFromPidl(pidl, &info);
|
|
if (FAILED_UNEXPECTEDLY(hr))
|
|
return hr;
|
|
|
|
static const GUID storage = PSGUID_STORAGE;
|
|
if (IsEqualGUID(pscid->fmtid, storage))
|
|
{
|
|
if (pscid->pid == PID_STG_NAME)
|
|
{
|
|
return MakeVariantString(pv, info->entryName);
|
|
}
|
|
else if (pscid->pid == PID_STG_STORAGETYPE)
|
|
{
|
|
if (info->objectType < 0)
|
|
{
|
|
NtPidlTypeData * td = (NtPidlTypeData*) (((PBYTE) info) + FIELD_OFFSET(NtPidlEntry, entryName) + info->entryNameLength + sizeof(WCHAR));
|
|
|
|
if (td->typeNameLength > 0)
|
|
{
|
|
return MakeVariantString(pv, td->typeName);
|
|
}
|
|
else
|
|
{
|
|
return MakeVariantString(pv, L"Unknown");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return MakeVariantString(pv, ObjectTypeNames[info->objectType]);
|
|
}
|
|
}
|
|
}
|
|
else if (IsEqualGUID(pscid->fmtid, GUID_NtObjectColumns))
|
|
{
|
|
if (pscid->pid == NTOBJECT_COLUMN_LINKTARGET && info->objectType == SYMBOLICLINK_OBJECT)
|
|
{
|
|
WCHAR wbLink[MAX_PATH] = { 0 };
|
|
UNICODE_STRING link;
|
|
RtlInitEmptyUnicodeString(&link, wbLink, sizeof(wbLink));
|
|
|
|
HRESULT hr = GetNTObjectSymbolicLinkTarget(m_NtPath, info->entryName, &link);
|
|
|
|
if (!FAILED_UNEXPECTEDLY(hr) && link.Length > 0)
|
|
{
|
|
return MakeVariantString(pv, link.Buffer);
|
|
}
|
|
}
|
|
|
|
V_VT(pv) = VT_EMPTY;
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE CNtObjectFolder::GetDetailsOf(
|
|
LPCITEMIDLIST pidl,
|
|
UINT iColumn,
|
|
SHELLDETAILS *psd)
|
|
{
|
|
const NtPidlEntry * info;
|
|
|
|
TRACE("GetDetailsOf\n");
|
|
|
|
if (pidl)
|
|
{
|
|
HRESULT hr = GetInfoFromPidl(pidl, &info);
|
|
if (FAILED_UNEXPECTEDLY(hr))
|
|
return hr;
|
|
|
|
switch (iColumn)
|
|
{
|
|
case NTOBJECT_COLUMN_NAME:
|
|
{
|
|
psd->fmt = LVCFMT_LEFT;
|
|
|
|
MakeStrRetFromString(info->entryName, info->entryNameLength, &(psd->str));
|
|
return S_OK;
|
|
}
|
|
|
|
case NTOBJECT_COLUMN_TYPE:
|
|
{
|
|
psd->fmt = LVCFMT_LEFT;
|
|
|
|
if (info->objectType < 0)
|
|
{
|
|
NtPidlTypeData * td = (NtPidlTypeData*) (((PBYTE) info) + FIELD_OFFSET(NtPidlEntry, entryName) + info->entryNameLength + sizeof(WCHAR));
|
|
|
|
if (td->typeNameLength > 0)
|
|
MakeStrRetFromString(td->typeName, td->typeNameLength, &(psd->str));
|
|
else
|
|
MakeStrRetFromString(L"Unknown", &(psd->str));
|
|
}
|
|
else
|
|
MakeStrRetFromString(ObjectTypeNames[info->objectType], &(psd->str));
|
|
return S_OK;
|
|
}
|
|
|
|
case NTOBJECT_COLUMN_LINKTARGET:
|
|
{
|
|
psd->fmt = LVCFMT_LEFT;
|
|
|
|
if (info->objectType == SYMBOLICLINK_OBJECT)
|
|
{
|
|
WCHAR wbLink[MAX_PATH] = { 0 };
|
|
UNICODE_STRING link;
|
|
RtlInitEmptyUnicodeString(&link, wbLink, sizeof(wbLink));
|
|
|
|
HRESULT hr = GetNTObjectSymbolicLinkTarget(m_NtPath, info->entryName, &link);
|
|
|
|
if (!FAILED_UNEXPECTEDLY(hr) && link.Length > 0)
|
|
{
|
|
MakeStrRetFromString(link.Buffer, link.Length, &(psd->str));
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
MakeStrRetFromString(L"", &(psd->str));
|
|
return S_OK;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (iColumn)
|
|
{
|
|
case NTOBJECT_COLUMN_NAME:
|
|
psd->fmt = LVCFMT_LEFT;
|
|
psd->cxChar = 30;
|
|
|
|
// TODO: Make localizable
|
|
MakeStrRetFromString(L"Object Name", &(psd->str));
|
|
return S_OK;
|
|
|
|
case NTOBJECT_COLUMN_TYPE:
|
|
psd->fmt = LVCFMT_LEFT;
|
|
psd->cxChar = 20;
|
|
|
|
// TODO: Make localizable
|
|
MakeStrRetFromString(L"Object Type", &(psd->str));
|
|
return S_OK;
|
|
|
|
case NTOBJECT_COLUMN_LINKTARGET:
|
|
psd->fmt = LVCFMT_LEFT;
|
|
psd->cxChar = 30;
|
|
|
|
// TODO: Make localizable
|
|
MakeStrRetFromString(L"Symlink Target", &(psd->str));
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE CNtObjectFolder::MapColumnToSCID(
|
|
UINT iColumn,
|
|
SHCOLUMNID *pscid)
|
|
{
|
|
static const GUID storage = PSGUID_STORAGE;
|
|
switch (iColumn)
|
|
{
|
|
case NTOBJECT_COLUMN_NAME:
|
|
pscid->fmtid = storage;
|
|
pscid->pid = PID_STG_NAME;
|
|
return S_OK;
|
|
|
|
case NTOBJECT_COLUMN_TYPE:
|
|
pscid->fmtid = storage;
|
|
pscid->pid = PID_STG_STORAGETYPE;
|
|
return S_OK;
|
|
|
|
case NTOBJECT_COLUMN_LINKTARGET:
|
|
pscid->fmtid = GUID_NtObjectColumns;
|
|
pscid->pid = NTOBJECT_COLUMN_LINKTARGET;
|
|
return S_OK;
|
|
}
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
HRESULT CNtObjectFolder::CompareIDs(LPARAM lParam, const NtPidlEntry * first, const NtPidlEntry * second)
|
|
{
|
|
HRESULT hr;
|
|
|
|
DWORD sortMode = lParam & 0xFFFF0000;
|
|
DWORD column = lParam & 0x0000FFFF;
|
|
|
|
if (sortMode == SHCIDS_ALLFIELDS)
|
|
{
|
|
if (column != 0)
|
|
return E_INVALIDARG;
|
|
|
|
int minsize = min(first->cb, second->cb);
|
|
hr = MAKE_COMPARE_HRESULT(memcmp(second, first, minsize));
|
|
if (hr != S_EQUAL)
|
|
return hr;
|
|
|
|
return MAKE_COMPARE_HRESULT(second->cb - first->cb);
|
|
}
|
|
|
|
switch (column)
|
|
{
|
|
case NTOBJECT_COLUMN_NAME:
|
|
return CompareName(lParam, first, second);
|
|
|
|
case NTOBJECT_COLUMN_TYPE:
|
|
return MAKE_COMPARE_HRESULT(second->objectType - first->objectType);
|
|
|
|
case NTOBJECT_COLUMN_LINKTARGET:
|
|
{
|
|
if (first->objectType != SYMBOLICLINK_OBJECT && second->objectType != SYMBOLICLINK_OBJECT)
|
|
return CompareName(lParam, first, second);
|
|
|
|
if (first->objectType != SYMBOLICLINK_OBJECT || second->objectType != SYMBOLICLINK_OBJECT)
|
|
return first->objectType != SYMBOLICLINK_OBJECT ? S_GREATERTHAN : S_LESSTHAN;
|
|
|
|
WCHAR wbLink1[MAX_PATH] = { 0 }, wbLink2[MAX_PATH] = { 0 };
|
|
UNICODE_STRING firstLink, secondLink;
|
|
RtlInitEmptyUnicodeString(&firstLink, wbLink1, sizeof(wbLink1));
|
|
|
|
if (FAILED_UNEXPECTEDLY(GetNTObjectSymbolicLinkTarget(m_NtPath, first->entryName, &firstLink)))
|
|
return E_INVALIDARG;
|
|
|
|
RtlInitEmptyUnicodeString(&secondLink, wbLink2, sizeof(wbLink2));
|
|
|
|
if (FAILED_UNEXPECTEDLY(GetNTObjectSymbolicLinkTarget(m_NtPath, second->entryName, &secondLink)))
|
|
return E_INVALIDARG;
|
|
|
|
return MAKE_COMPARE_HRESULT(RtlCompareUnicodeString(&firstLink, &secondLink, TRUE));
|
|
}
|
|
}
|
|
|
|
DbgPrint("Unsupported sorting mode.\n");
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
ULONG CNtObjectFolder::ConvertAttributes(const NtPidlEntry * entry, PULONG inMask)
|
|
{
|
|
ULONG mask = inMask ? *inMask : 0xFFFFFFFF;
|
|
ULONG flags = SFGAO_HASPROPSHEET | SFGAO_CANLINK;
|
|
|
|
if (entry->objectType == DIRECTORY_OBJECT)
|
|
flags |= SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_BROWSABLE;
|
|
|
|
if (entry->objectType == SYMBOLICLINK_OBJECT)
|
|
flags |= SFGAO_LINK | SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_BROWSABLE;
|
|
|
|
if (entry->objectType == KEY_OBJECT)
|
|
flags |= SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_BROWSABLE;
|
|
|
|
return flags & mask;
|
|
}
|
|
|
|
BOOL CNtObjectFolder::IsFolder(const NtPidlEntry * info)
|
|
{
|
|
return (info->objectType == DIRECTORY_OBJECT) ||
|
|
(info->objectType == SYMBOLICLINK_OBJECT) ||
|
|
(info->objectType == KEY_OBJECT);
|
|
}
|
|
|
|
HRESULT CNtObjectFolder::GetInfoFromPidl(LPCITEMIDLIST pcidl, const NtPidlEntry ** pentry)
|
|
{
|
|
if (!pcidl)
|
|
{
|
|
DbgPrint("PCIDL is NULL\n");
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
NtPidlEntry * entry = (NtPidlEntry*) &(pcidl->mkid);
|
|
if (entry->cb < sizeof(NtPidlEntry))
|
|
{
|
|
DbgPrint("PCIDL too small %l (required %l)\n", entry->cb, sizeof(NtPidlEntry));
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (entry->magic != NT_OBJECT_PIDL_MAGIC)
|
|
{
|
|
DbgPrint("PCIDL magic mismatch %04x (expected %04x)\n", entry->magic, NT_OBJECT_PIDL_MAGIC);
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
*pentry = entry;
|
|
return S_OK;
|
|
}
|