mirror of
https://github.com/reactos/reactos.git
synced 2024-08-20 02:13:29 +00:00
349 lines
9.8 KiB
C++
349 lines
9.8 KiB
C++
/*
|
|
* ReactOS Explorer
|
|
*
|
|
* Copyright 2016 Sylvain Deverre <deverre dot sylv at gmail dot com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
/*
|
|
* Wraps the component categories manager enum
|
|
*/
|
|
|
|
#include "shellbars.h"
|
|
|
|
#define REGPATH L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Discardable\\PostSetup\\Component Categories"
|
|
#define IMPLEMENTING L"Implementing"
|
|
#define REQUIRING L"Requiring"
|
|
|
|
typedef struct categoryCacheHeader
|
|
{
|
|
DWORD dwSize; // size of header only
|
|
DWORD version; // currently 1
|
|
SYSTEMTIME writeTime; // time we were written to registry
|
|
DWORD classCount; // number of classes following
|
|
} CATCACHEHDR, *PCATCACHEHDR;
|
|
|
|
/*
|
|
* This class manages a cached explorer component categories items, writing cache if it
|
|
* doesn't exist yet.
|
|
* It is used by CSHEnumClassesOfCategories internally.
|
|
*/
|
|
class CComCatCachedCategory
|
|
{
|
|
public:
|
|
CComCatCachedCategory();
|
|
virtual ~CComCatCachedCategory();
|
|
HRESULT WriteCacheToDSA(HDSA pDest);
|
|
HRESULT STDMETHODCALLTYPE Initialize(CATID &catID, BOOL reloadCache);
|
|
private:
|
|
BOOL LoadFromRegistry();
|
|
HRESULT LoadFromComCatMgr();
|
|
HRESULT CacheDSA();
|
|
CATID fCategory;
|
|
HDSA fLocalDsa;
|
|
};
|
|
|
|
CComCatCachedCategory::CComCatCachedCategory()
|
|
{
|
|
fLocalDsa = DSA_Create(sizeof(GUID), 5);
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE CComCatCachedCategory::Initialize(CATID &catID, BOOL reloadCache)
|
|
{
|
|
HRESULT hr;
|
|
|
|
fCategory = catID;
|
|
if (reloadCache || !LoadFromRegistry())
|
|
{
|
|
hr = LoadFromComCatMgr();
|
|
if (FAILED_UNEXPECTEDLY(hr))
|
|
return hr;
|
|
|
|
hr = CacheDSA();
|
|
if (FAILED_UNEXPECTEDLY(hr))
|
|
return hr;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
CComCatCachedCategory::~CComCatCachedCategory()
|
|
{
|
|
DSA_Destroy(fLocalDsa);
|
|
}
|
|
|
|
BOOL CComCatCachedCategory::LoadFromRegistry()
|
|
{
|
|
WCHAR bufKey[MAX_PATH];
|
|
WCHAR guidStr[MAX_PATH];
|
|
DWORD dataSize, i;
|
|
CComHeapPtr<CATCACHEHDR> buffer;
|
|
GUID *guidArray;
|
|
|
|
if (!fLocalDsa)
|
|
return FALSE;
|
|
|
|
dataSize = 0;
|
|
if (!StringFromGUID2(fCategory, guidStr, MAX_PATH))
|
|
return FALSE;
|
|
|
|
wsprintf(bufKey, L"%s\\%s\\%s", REGPATH , guidStr, L"Enum");
|
|
|
|
// Try to read key and get proper value size
|
|
if (SHGetValue(HKEY_CURRENT_USER, bufKey, IMPLEMENTING, NULL, NULL, &dataSize))
|
|
return FALSE;
|
|
|
|
buffer.Attach((PCATCACHEHDR)CoTaskMemAlloc(dataSize));
|
|
|
|
SHGetValue(HKEY_CURRENT_USER, bufKey, IMPLEMENTING, NULL, buffer, &dataSize);
|
|
guidArray = (GUID*)(buffer + 1);
|
|
for (i = 0; i < buffer->classCount; i++)
|
|
{
|
|
// Add class to cache
|
|
DSA_InsertItem(fLocalDsa, DSA_APPEND, guidArray + i);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
HRESULT CComCatCachedCategory::CacheDSA()
|
|
{
|
|
WCHAR bufKey[MAX_PATH];
|
|
WCHAR guidStr[MAX_PATH];
|
|
UINT elemCount;
|
|
UINT i;
|
|
UINT bufferSize;
|
|
CComHeapPtr<CATCACHEHDR> buffer;
|
|
GUID *guidArray;
|
|
GUID *tmp;
|
|
|
|
elemCount = DSA_GetItemCount(fLocalDsa);
|
|
bufferSize = sizeof(CATCACHEHDR) + elemCount * sizeof(GUID);
|
|
if (!StringFromGUID2(fCategory, guidStr, MAX_PATH))
|
|
return E_FAIL;
|
|
|
|
buffer.Attach((PCATCACHEHDR)CoTaskMemAlloc(bufferSize));
|
|
if (!buffer)
|
|
return E_OUTOFMEMORY;
|
|
|
|
// Correctly fill cache header
|
|
buffer->dwSize = sizeof(CATCACHEHDR);
|
|
buffer->version = 1;
|
|
GetSystemTime(&buffer->writeTime);
|
|
buffer->classCount = (DWORD)elemCount;
|
|
|
|
guidArray = (GUID*)(buffer + 1);
|
|
wsprintf(bufKey, L"%s\\%s\\%s", REGPATH , guidStr, L"Enum");
|
|
|
|
// Write DSA contents inside the memory buffer allocated
|
|
for(i = 0; i < elemCount; i++)
|
|
{
|
|
tmp = (GUID*)DSA_GetItemPtr(fLocalDsa, i);
|
|
if (tmp)
|
|
{
|
|
guidArray[i] = *tmp;
|
|
}
|
|
}
|
|
|
|
// Save items to registry
|
|
SHSetValue(HKEY_CURRENT_USER, bufKey, IMPLEMENTING, REG_BINARY, buffer, bufferSize);
|
|
|
|
guidArray = NULL;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CComCatCachedCategory::LoadFromComCatMgr()
|
|
{
|
|
HRESULT hr;
|
|
CComPtr<ICatInformation> pCatInformation;
|
|
CComPtr<IEnumGUID> pEnumGUID;
|
|
ULONG pFetched;
|
|
CLSID tmp;
|
|
|
|
// Get component categories manager instance
|
|
hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr, NULL, CLSCTX_INPROC_SERVER,
|
|
IID_PPV_ARG(ICatInformation, &pCatInformation));
|
|
if (FAILED_UNEXPECTEDLY(hr))
|
|
return hr;
|
|
|
|
// Get the proper enumerator
|
|
hr = pCatInformation->EnumClassesOfCategories(1, &fCategory, NULL, NULL, &pEnumGUID);
|
|
if (FAILED_UNEXPECTEDLY(hr))
|
|
return hr;
|
|
|
|
// Enumerate elements
|
|
do
|
|
{
|
|
pFetched = 0;
|
|
pEnumGUID->Next(1, &tmp, &pFetched);
|
|
if (pFetched)
|
|
{
|
|
if (DSA_InsertItem(fLocalDsa, DSA_APPEND, &tmp) == E_OUTOFMEMORY)
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
while (pFetched > 0);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CComCatCachedCategory::WriteCacheToDSA(HDSA pDest)
|
|
{
|
|
INT i;
|
|
for(i = 0; i < DSA_GetItemCount(fLocalDsa); i++)
|
|
{
|
|
if (DSA_InsertItem(pDest, DSA_APPEND, DSA_GetItemPtr(fLocalDsa, i)) == DSA_ERR)
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
class CSHEnumClassesOfCategories :
|
|
public CComCoClass<CSHEnumClassesOfCategories>,
|
|
public CComObjectRootEx<CComMultiThreadModelNoCS>,
|
|
public IEnumGUID
|
|
{
|
|
private:
|
|
CComPtr<ICatInformation> fCatInformation;
|
|
HDSA fDsa;
|
|
ULONG fCursor;
|
|
|
|
public:
|
|
CSHEnumClassesOfCategories();
|
|
virtual ~CSHEnumClassesOfCategories();
|
|
virtual HRESULT STDMETHODCALLTYPE Initialize(ULONG cImplemented, CATID *pImplemented, ULONG cRequired, CATID *pRequired);
|
|
// *** IEnumGUID methods ***
|
|
virtual HRESULT STDMETHODCALLTYPE Clone(IEnumCLSID **ppvOut);
|
|
virtual HRESULT STDMETHODCALLTYPE Next(ULONG cElt, CLSID *pElts, ULONG *pFetched);
|
|
virtual HRESULT STDMETHODCALLTYPE Reset();
|
|
virtual HRESULT STDMETHODCALLTYPE Skip(ULONG nbElts);
|
|
|
|
BEGIN_COM_MAP(CSHEnumClassesOfCategories)
|
|
COM_INTERFACE_ENTRY_IID(IID_IEnumGUID, IEnumGUID)
|
|
END_COM_MAP()
|
|
};
|
|
|
|
CSHEnumClassesOfCategories::CSHEnumClassesOfCategories()
|
|
{
|
|
fCursor = 0;
|
|
fDsa = DSA_Create(sizeof(GUID), 5);
|
|
}
|
|
|
|
CSHEnumClassesOfCategories::~CSHEnumClassesOfCategories()
|
|
{
|
|
if (fDsa)
|
|
DSA_Destroy(fDsa);
|
|
}
|
|
|
|
HRESULT CSHEnumClassesOfCategories::Initialize(ULONG cImplemented, CATID *pImplemented, ULONG cRequired, CATID *pRequired)
|
|
{
|
|
UINT i;
|
|
HRESULT hr;
|
|
|
|
if (!fDsa)
|
|
return E_FAIL;
|
|
|
|
// Parameter validation:
|
|
// - We must have at least one category to manage.
|
|
// - The array pointers must not be NULL if there is a non-zero
|
|
// element count specified for them.
|
|
if (cImplemented == 0 && cRequired == 0)
|
|
return E_INVALIDARG;
|
|
if ((cImplemented && !pImplemented) || (cRequired && !pRequired))
|
|
return E_INVALIDARG;
|
|
|
|
// For each implemented category, create a cache and add it to our local DSA.
|
|
for (i = 0; i < cImplemented; i++)
|
|
{
|
|
CComCatCachedCategory cachedCat;
|
|
hr = cachedCat.Initialize(pImplemented[i], FALSE);
|
|
if (FAILED_UNEXPECTEDLY(hr))
|
|
return hr;
|
|
cachedCat.WriteCacheToDSA(fDsa);
|
|
}
|
|
|
|
// TODO: Implement caching of the required categories.
|
|
if (cRequired > 0)
|
|
{
|
|
FIXME("Implement required categories class enumeration\n");
|
|
|
|
// Only fail in case we didn't look at the implemented categories.
|
|
if (cImplemented == 0)
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
// *** IEnumGUID methods ***
|
|
|
|
HRESULT STDMETHODCALLTYPE CSHEnumClassesOfCategories::Clone(IEnumCLSID **ppvOut)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE CSHEnumClassesOfCategories::Next(ULONG cElt, CLSID *pElts, ULONG *pFetched)
|
|
{
|
|
ULONG i;
|
|
ULONG read;
|
|
GUID *tmp;
|
|
|
|
if (!pElts)
|
|
return E_INVALIDARG;
|
|
read = 0;
|
|
for (i = 0; i < cElt && (fCursor < (ULONG)DSA_GetItemCount(fDsa)); i++)
|
|
{
|
|
tmp = (GUID*)DSA_GetItemPtr(fDsa, fCursor + i);
|
|
if (!tmp)
|
|
break;
|
|
pElts[i] = *tmp;
|
|
read++;
|
|
}
|
|
fCursor += read;
|
|
if (pFetched)
|
|
*pFetched = read;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE CSHEnumClassesOfCategories::Reset()
|
|
{
|
|
fCursor = 0;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE CSHEnumClassesOfCategories::Skip(ULONG nbElts)
|
|
{
|
|
if (fCursor + nbElts >= (ULONG)DSA_GetItemCount(fDsa))
|
|
return E_INVALIDARG;
|
|
fCursor += nbElts;
|
|
return S_OK;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* SHEnumClassesOfCategories [BROWSEUI.136]
|
|
*/
|
|
extern "C" HRESULT WINAPI SHEnumClassesOfCategories(ULONG cImplemented, CATID *pImplemented, ULONG cRequired, CATID *pRequired, IEnumGUID **out)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (!out)
|
|
return E_INVALIDARG;
|
|
|
|
hr = ShellObjectCreatorInit<CSHEnumClassesOfCategories>(
|
|
cImplemented, pImplemented, cRequired, pRequired, IID_PPV_ARG(IEnumGUID, out));
|
|
if (FAILED_UNEXPECTEDLY(hr))
|
|
return hr;
|
|
return S_OK;
|
|
}
|