1624 lines
42 KiB
C++
1624 lines
42 KiB
C++
|
#include "shellprv.h"
|
||
|
#include "category.h"
|
||
|
#include "prop.h"
|
||
|
#include "ids.h"
|
||
|
#include "clsobj.h"
|
||
|
#include "comcat.h" // for IEnumGUID
|
||
|
#include "ole2dup.h"
|
||
|
|
||
|
#define GROUPID_UNSPECIFIED (-10)
|
||
|
#define GROUPID_FOLDER (-11)
|
||
|
#define GROUPID_OTHER (-12)
|
||
|
|
||
|
#define STRINGID_FROM_GROUPID(id) ((id) == GROUPID_UNSPECIFIED)? IDS_UNSPECIFIED : (((id) == GROUPID_FOLDER)?IDS_GROUPFOLDERS: IDS_GROUPOTHERCHAR)
|
||
|
|
||
|
typedef struct tagCATCACHE
|
||
|
{
|
||
|
GUID guid;
|
||
|
SHCOLUMNID scid;
|
||
|
IUnknown* punk;
|
||
|
} CATCACHE;
|
||
|
|
||
|
// {3E373E22-DA99-4cb7-A886-754EAE984CB4}
|
||
|
static const GUID CLSID_DetailCategorizer =
|
||
|
{ 0x3e373e22, 0xda99, 0x4cb7, { 0xa8, 0x86, 0x75, 0x4e, 0xae, 0x98, 0x4c, 0xb4 } };
|
||
|
|
||
|
|
||
|
|
||
|
class CTimeCategorizer : public ICategorizer,
|
||
|
public IShellExtInit
|
||
|
{
|
||
|
public:
|
||
|
// IUnknown
|
||
|
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
|
||
|
STDMETHODIMP_(ULONG) AddRef(void);
|
||
|
STDMETHODIMP_(ULONG) Release(void);
|
||
|
|
||
|
// ICategorizer
|
||
|
STDMETHODIMP GetDescription(LPWSTR pszDesc, UINT cch);
|
||
|
STDMETHODIMP GetCategory(UINT cidl, LPCITEMIDLIST * apidl, DWORD* rgCategoryIds);
|
||
|
STDMETHODIMP GetCategoryInfo(DWORD dwCategoryId, CATEGORY_INFO* pci);
|
||
|
STDMETHODIMP CompareCategory(CATSORT_FLAGS csfFlags, DWORD dwCategoryId1, DWORD dwCategoryId2);
|
||
|
|
||
|
// IShellExtInit
|
||
|
STDMETHODIMP Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdobj, HKEY hkeyProgID);
|
||
|
|
||
|
CTimeCategorizer(const SHCOLUMNID* pscid, IShellFolder2* psf);
|
||
|
CTimeCategorizer();
|
||
|
private:
|
||
|
~CTimeCategorizer();
|
||
|
long _cRef;
|
||
|
IShellFolder2* _psf;
|
||
|
SHCOLUMNID _scid;
|
||
|
};
|
||
|
|
||
|
class CSizeCategorizer : public ICategorizer,
|
||
|
public IShellExtInit
|
||
|
{
|
||
|
public:
|
||
|
// IUnknown
|
||
|
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
|
||
|
STDMETHODIMP_(ULONG) AddRef(void);
|
||
|
STDMETHODIMP_(ULONG) Release(void);
|
||
|
|
||
|
// ICategorizer
|
||
|
STDMETHODIMP GetDescription(LPWSTR pszDesc, UINT cch);
|
||
|
STDMETHODIMP GetCategory(UINT cidl, LPCITEMIDLIST * apidl, DWORD* rgCategoryIds);
|
||
|
STDMETHODIMP GetCategoryInfo(DWORD dwCategoryId, CATEGORY_INFO* pci);
|
||
|
STDMETHODIMP CompareCategory(CATSORT_FLAGS csfFlags, DWORD dwCategoryId1, DWORD dwCategoryId2);
|
||
|
|
||
|
// IShellExtInit
|
||
|
STDMETHODIMP Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdobj, HKEY hkeyProgID);
|
||
|
|
||
|
CSizeCategorizer(IShellFolder2* psf);
|
||
|
CSizeCategorizer(BOOL fLarge);
|
||
|
private:
|
||
|
~CSizeCategorizer();
|
||
|
long _cRef;
|
||
|
IShellFolder2* _psf;
|
||
|
BOOL _fLarge;
|
||
|
};
|
||
|
|
||
|
class CDriveTypeCategorizer : public ICategorizer,
|
||
|
public IShellExtInit
|
||
|
{
|
||
|
public:
|
||
|
// IUnknown
|
||
|
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
|
||
|
STDMETHODIMP_(ULONG) AddRef(void);
|
||
|
STDMETHODIMP_(ULONG) Release(void);
|
||
|
|
||
|
// ICategorizer
|
||
|
STDMETHODIMP GetDescription(LPWSTR pszDesc, UINT cch);
|
||
|
STDMETHODIMP GetCategory(UINT cidl, LPCITEMIDLIST * apidl, DWORD* rgCategoryIds);
|
||
|
STDMETHODIMP GetCategoryInfo(DWORD dwCategoryId, CATEGORY_INFO* pci);
|
||
|
STDMETHODIMP CompareCategory(CATSORT_FLAGS csfFlags, DWORD dwCategoryId1, DWORD dwCategoryId2);
|
||
|
|
||
|
// IShellExtInit
|
||
|
STDMETHODIMP Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdobj, HKEY hkeyProgID);
|
||
|
|
||
|
CDriveTypeCategorizer(IShellFolder2* psf);
|
||
|
CDriveTypeCategorizer();
|
||
|
private:
|
||
|
~CDriveTypeCategorizer();
|
||
|
long _cRef;
|
||
|
IShellFolder2* _psf;
|
||
|
};
|
||
|
|
||
|
class CAlphaCategorizer : public ICategorizer
|
||
|
{
|
||
|
public:
|
||
|
// IUnknown
|
||
|
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
|
||
|
STDMETHODIMP_(ULONG) AddRef(void);
|
||
|
STDMETHODIMP_(ULONG) Release(void);
|
||
|
|
||
|
// ICategorizer
|
||
|
STDMETHODIMP GetDescription(LPWSTR pszDesc, UINT cch);
|
||
|
STDMETHODIMP GetCategory(UINT cidl, LPCITEMIDLIST * apidl, DWORD* rgCategoryIds);
|
||
|
STDMETHODIMP GetCategoryInfo(DWORD dwCategoryId, CATEGORY_INFO* pci);
|
||
|
STDMETHODIMP CompareCategory(CATSORT_FLAGS csfFlags, DWORD dwCategoryId1, DWORD dwCategoryId2);
|
||
|
|
||
|
CAlphaCategorizer(IShellFolder2* psf);
|
||
|
private:
|
||
|
~CAlphaCategorizer();
|
||
|
long _cRef;
|
||
|
IShellFolder2* _psf;
|
||
|
};
|
||
|
|
||
|
class CFreeSpaceCategorizer : public ICategorizer,
|
||
|
public IShellExtInit
|
||
|
{
|
||
|
public:
|
||
|
// IUnknown
|
||
|
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
|
||
|
STDMETHODIMP_(ULONG) AddRef(void);
|
||
|
STDMETHODIMP_(ULONG) Release(void);
|
||
|
|
||
|
// ICategorizer
|
||
|
STDMETHODIMP GetDescription(LPWSTR pszDesc, UINT cch);
|
||
|
STDMETHODIMP GetCategory(UINT cidl, LPCITEMIDLIST * apidl, DWORD* rgCategoryIds);
|
||
|
STDMETHODIMP GetCategoryInfo(DWORD dwCategoryId, CATEGORY_INFO* pci);
|
||
|
STDMETHODIMP CompareCategory(CATSORT_FLAGS csfFlags, DWORD dwCategoryId1, DWORD dwCategoryId2);
|
||
|
|
||
|
// IShellExtInit
|
||
|
STDMETHODIMP Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdobj, HKEY hkeyProgID);
|
||
|
|
||
|
CFreeSpaceCategorizer();
|
||
|
private:
|
||
|
~CFreeSpaceCategorizer();
|
||
|
long _cRef;
|
||
|
IShellFolder2* _psf;
|
||
|
};
|
||
|
|
||
|
|
||
|
class CDetailCategorizer : public ICategorizer
|
||
|
{
|
||
|
public:
|
||
|
// IUnknown
|
||
|
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
|
||
|
STDMETHODIMP_(ULONG) AddRef(void);
|
||
|
STDMETHODIMP_(ULONG) Release(void);
|
||
|
|
||
|
// ICategorizer
|
||
|
STDMETHODIMP GetDescription(LPWSTR pszDesc, UINT cch);
|
||
|
STDMETHODIMP GetCategory(UINT cidl, LPCITEMIDLIST * apidl, DWORD* rgCategoryIds);
|
||
|
STDMETHODIMP GetCategoryInfo(DWORD dwCategoryId, CATEGORY_INFO* pci);
|
||
|
STDMETHODIMP CompareCategory(CATSORT_FLAGS csfFlags, DWORD dwCategoryId1, DWORD dwCategoryId2);
|
||
|
|
||
|
CDetailCategorizer(IShellFolder2* psf, const SHCOLUMNID& scid);
|
||
|
private:
|
||
|
~CDetailCategorizer();
|
||
|
long _cRef;
|
||
|
IShellFolder2* _psf;
|
||
|
SHCOLUMNID _scid;
|
||
|
HHASHTABLE _hash;
|
||
|
HDPA _hdpaKeys;
|
||
|
};
|
||
|
|
||
|
|
||
|
class CEnumCategoryGUID : public IEnumGUID
|
||
|
{
|
||
|
public:
|
||
|
// IUnknown
|
||
|
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
|
||
|
STDMETHODIMP_(ULONG) AddRef(void);
|
||
|
STDMETHODIMP_(ULONG) Release(void);
|
||
|
|
||
|
// IEnumIDList
|
||
|
STDMETHODIMP Next(ULONG celt, GUID *rgelt, ULONG *pceltFetched);
|
||
|
STDMETHODIMP Skip(ULONG celt) { return E_NOTIMPL; }
|
||
|
STDMETHODIMP Reset() { _iIndex = 0; return S_OK;}
|
||
|
STDMETHODIMP Clone(IEnumGUID **ppenum) { return E_NOTIMPL; };
|
||
|
|
||
|
CEnumCategoryGUID(HDSA hdsa);
|
||
|
private:
|
||
|
|
||
|
long _cRef;
|
||
|
HDSA _hda;
|
||
|
int _iIndex;
|
||
|
};
|
||
|
|
||
|
CEnumCategoryGUID::CEnumCategoryGUID(HDSA hda): _cRef(1)
|
||
|
{
|
||
|
_hda = hda;
|
||
|
}
|
||
|
|
||
|
HRESULT CEnumCategoryGUID::QueryInterface(REFIID riid, void **ppv)
|
||
|
{
|
||
|
static const QITAB qit[] =
|
||
|
{
|
||
|
QITABENT(CEnumCategoryGUID, IEnumGUID),
|
||
|
{ 0 },
|
||
|
};
|
||
|
return QISearch(this, qit, riid, ppv);
|
||
|
}
|
||
|
|
||
|
ULONG CEnumCategoryGUID::AddRef()
|
||
|
{
|
||
|
return InterlockedIncrement(&_cRef);
|
||
|
}
|
||
|
|
||
|
ULONG CEnumCategoryGUID::Release()
|
||
|
{
|
||
|
if (InterlockedDecrement(&_cRef))
|
||
|
return _cRef;
|
||
|
|
||
|
delete this;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CEnumCategoryGUID::Next(ULONG celt, GUID *rgelt, ULONG *pceltFetched)
|
||
|
{
|
||
|
HRESULT hr = S_FALSE;
|
||
|
if (celt > 1)
|
||
|
return E_INVALIDARG;
|
||
|
|
||
|
if (_hda == NULL)
|
||
|
return hr;
|
||
|
|
||
|
while (hr != S_OK &&
|
||
|
_iIndex < DSA_GetItemCount(_hda))
|
||
|
{
|
||
|
CATCACHE* pcat = (CATCACHE*)DSA_GetItemPtr(_hda, _iIndex);
|
||
|
|
||
|
// Is this a scid map entry instead of an external categorizer?
|
||
|
if (pcat->scid.fmtid == GUID_NULL)
|
||
|
{
|
||
|
// Nope. then we can enum it.
|
||
|
if (pceltFetched)
|
||
|
*pceltFetched = 1;
|
||
|
|
||
|
*rgelt = pcat->guid;
|
||
|
|
||
|
hr = S_OK;
|
||
|
}
|
||
|
_iIndex++;
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
|
||
|
}
|
||
|
|
||
|
class CCategoryProvider : public ICategoryProvider, public IDefCategoryProvider
|
||
|
{
|
||
|
public:
|
||
|
// IUnknown
|
||
|
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
|
||
|
STDMETHODIMP_(ULONG) AddRef(void);
|
||
|
STDMETHODIMP_(ULONG) Release(void);
|
||
|
|
||
|
// ICategoryProvider
|
||
|
STDMETHODIMP CanCategorizeOnSCID(SHCOLUMNID* pscid);
|
||
|
STDMETHODIMP GetDefaultCategory(GUID* pguid, SHCOLUMNID* pscid);
|
||
|
STDMETHODIMP GetCategoryForSCID(SHCOLUMNID* pscid, GUID* pguid);
|
||
|
STDMETHODIMP EnumCategories(IEnumGUID** penum);
|
||
|
STDMETHODIMP GetCategoryName(GUID* pguid, LPWSTR pszName, UINT cch);
|
||
|
STDMETHODIMP CreateCategory(GUID* pguid, REFIID riid, void** ppv);
|
||
|
|
||
|
// IDefCategoryProvider
|
||
|
STDMETHODIMP Initialize(const GUID* pguid, const SHCOLUMNID* pscid, const SHCOLUMNID* pscidExlude, HKEY hkey, const CATLIST* pcl, IShellFolder* psf);
|
||
|
|
||
|
CCategoryProvider();
|
||
|
private:
|
||
|
~CCategoryProvider();
|
||
|
BOOL _BuildCategoryList(HKEY hkey, const CATLIST* pcl);
|
||
|
friend int DestroyCache(void *pv, void *unused);
|
||
|
HRESULT CreateInstance(GUID* pguid, REFIID riid, void** ppv);
|
||
|
|
||
|
|
||
|
long _cRef;
|
||
|
LPITEMIDLIST _pidlFolder;
|
||
|
IShellFolder2* _psf;
|
||
|
HDSA _hdaCat;
|
||
|
|
||
|
GUID _guidDefault;
|
||
|
SHCOLUMNID _scidDefault;
|
||
|
HDSA _hdaExcludeSCIDs;
|
||
|
|
||
|
};
|
||
|
|
||
|
STDAPI CCategoryProvider_CreateInstance(IUnknown* punkOuter, REFIID riid, void **ppv)
|
||
|
{
|
||
|
HRESULT hr = E_OUTOFMEMORY;
|
||
|
CCategoryProvider* p = new CCategoryProvider();
|
||
|
if (p)
|
||
|
{
|
||
|
hr = p->QueryInterface(riid, ppv);
|
||
|
p->Release();
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL CCategoryProvider::_BuildCategoryList(HKEY hkey, const CATLIST* pcl)
|
||
|
{
|
||
|
int i = 0;
|
||
|
_hdaCat = DSA_Create(sizeof(CATCACHE), 3);
|
||
|
|
||
|
if (!_hdaCat)
|
||
|
return FALSE;
|
||
|
|
||
|
// Enumerate static
|
||
|
while(!IsEqualGUID(*pcl[i].pguid, GUID_NULL))
|
||
|
{
|
||
|
CATCACHE cc = {0};
|
||
|
cc.guid = *pcl[i].pguid;
|
||
|
if (pcl[i].pscid)
|
||
|
{
|
||
|
cc.scid = *pcl[i].pscid;
|
||
|
}
|
||
|
|
||
|
DSA_AppendItem(_hdaCat, (void*)&cc);
|
||
|
i++;
|
||
|
}
|
||
|
|
||
|
// Enumerate hkey
|
||
|
TCHAR szHandlerCLSID[GUIDSTR_MAX];
|
||
|
int iHandler = 0;
|
||
|
|
||
|
while (ERROR_SUCCESS == RegEnumKey(hkey, iHandler++, szHandlerCLSID, ARRAYSIZE(szHandlerCLSID)))
|
||
|
{
|
||
|
CLSID clsid;
|
||
|
if (SUCCEEDED(SHCLSIDFromString(szHandlerCLSID, &clsid)))
|
||
|
{
|
||
|
CATCACHE cc = {0};
|
||
|
cc.guid = clsid;
|
||
|
|
||
|
DSA_AppendItem(_hdaCat, (void*)&cc);
|
||
|
i++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
CCategoryProvider::CCategoryProvider() : _cRef(1)
|
||
|
{
|
||
|
DllAddRef();
|
||
|
}
|
||
|
|
||
|
int DestroyCache(void *pv, void *unused)
|
||
|
{
|
||
|
CATCACHE* pcat = (CATCACHE*)pv;
|
||
|
ATOMICRELEASE(pcat->punk);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
CCategoryProvider::~CCategoryProvider()
|
||
|
{
|
||
|
ATOMICRELEASE(_psf);
|
||
|
ILFree(_pidlFolder);
|
||
|
if (_hdaExcludeSCIDs)
|
||
|
{
|
||
|
DSA_Destroy(_hdaExcludeSCIDs);
|
||
|
}
|
||
|
|
||
|
if (_hdaCat)
|
||
|
{
|
||
|
DSA_DestroyCallback(_hdaCat, DestroyCache, NULL);
|
||
|
}
|
||
|
DllRelease();
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CCategoryProvider::QueryInterface(REFIID riid, void **ppv)
|
||
|
{
|
||
|
static const QITAB qit[] =
|
||
|
{
|
||
|
QITABENT(CCategoryProvider, IDefCategoryProvider),
|
||
|
QITABENT(CCategoryProvider, ICategoryProvider),
|
||
|
{ 0 },
|
||
|
};
|
||
|
return QISearch(this, qit, riid, ppv);
|
||
|
}
|
||
|
|
||
|
ULONG CCategoryProvider::AddRef()
|
||
|
{
|
||
|
return InterlockedIncrement(&_cRef);
|
||
|
}
|
||
|
|
||
|
ULONG CCategoryProvider::Release()
|
||
|
{
|
||
|
if (InterlockedDecrement(&_cRef))
|
||
|
return _cRef;
|
||
|
|
||
|
delete this;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
HRESULT CCategoryProvider::Initialize(const GUID* pguid, const SHCOLUMNID* pscid, const SHCOLUMNID* pscidExclude, HKEY hkey, const CATLIST* pcl, IShellFolder* psf)
|
||
|
{
|
||
|
if (!psf)
|
||
|
return E_INVALIDARG;
|
||
|
|
||
|
HRESULT hr = SHGetIDListFromUnk(psf, &_pidlFolder);
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
if (pcl && !_BuildCategoryList(hkey, pcl))
|
||
|
return E_OUTOFMEMORY;
|
||
|
|
||
|
if (pguid)
|
||
|
_guidDefault = *pguid;
|
||
|
|
||
|
if (pscid)
|
||
|
_scidDefault = *pscid;
|
||
|
|
||
|
if (pscidExclude)
|
||
|
{
|
||
|
_hdaExcludeSCIDs = DSA_Create(sizeof(SHCOLUMNID), 3);
|
||
|
if (_hdaExcludeSCIDs)
|
||
|
{
|
||
|
int i = 0;
|
||
|
while(pscidExclude[i].fmtid != GUID_NULL)
|
||
|
{
|
||
|
DSA_AppendItem(_hdaExcludeSCIDs, (void*)&pscidExclude[i]);
|
||
|
i++;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
hr = psf->QueryInterface(IID_PPV_ARG(IShellFolder2, &_psf));
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CCategoryProvider::CanCategorizeOnSCID(SHCOLUMNID* pscid)
|
||
|
{
|
||
|
if (_hdaExcludeSCIDs)
|
||
|
{
|
||
|
for (int i=0; i < DSA_GetItemCount(_hdaExcludeSCIDs); i++)
|
||
|
{
|
||
|
SHCOLUMNID* pscidExclude = (SHCOLUMNID*)DSA_GetItemPtr(_hdaExcludeSCIDs, i);
|
||
|
if (IsEqualSCID(*pscidExclude, *pscid))
|
||
|
return S_FALSE;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CCategoryProvider::GetDefaultCategory(GUID* pguid, SHCOLUMNID* pscid)
|
||
|
{
|
||
|
*pguid = _guidDefault;
|
||
|
*pscid = _scidDefault;
|
||
|
|
||
|
if (_guidDefault == GUID_NULL && _scidDefault.fmtid == GUID_NULL)
|
||
|
return S_FALSE;
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CCategoryProvider::GetCategoryForSCID(SHCOLUMNID* pscid, GUID* pguid)
|
||
|
{
|
||
|
HRESULT hr = S_FALSE;
|
||
|
if (_hdaCat == NULL || pscid == NULL)
|
||
|
return hr;
|
||
|
|
||
|
int c = DSA_GetItemCount(_hdaCat);
|
||
|
for (int i = 0; i < c; i++)
|
||
|
{
|
||
|
CATCACHE* pcc = (CATCACHE*)DSA_GetItemPtr(_hdaCat, i);
|
||
|
ASSERT(pcc != NULL);
|
||
|
|
||
|
if (IsEqualSCID(pcc->scid, *pscid))
|
||
|
{
|
||
|
*pguid = pcc->guid;
|
||
|
hr = S_OK;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
STDMETHODIMP CCategoryProvider::EnumCategories(IEnumGUID** penum)
|
||
|
{
|
||
|
HRESULT hr = E_NOINTERFACE;
|
||
|
if (_hdaCat)
|
||
|
{
|
||
|
*penum = (IEnumGUID*)new CEnumCategoryGUID(_hdaCat);
|
||
|
|
||
|
if (!*penum)
|
||
|
hr = E_OUTOFMEMORY;
|
||
|
|
||
|
hr = S_OK;
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CCategoryProvider::GetCategoryName(GUID* pguid, LPWSTR pszName, UINT cch)
|
||
|
{
|
||
|
ICategorizer* pcat;
|
||
|
HRESULT hr = CreateCategory(pguid, IID_PPV_ARG(ICategorizer, &pcat));
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = pcat->GetDescription(pszName, cch);
|
||
|
pcat->Release();
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
|
||
|
}
|
||
|
|
||
|
HRESULT CCategoryProvider::CreateInstance(GUID* pguid, REFIID riid, void** ppv)
|
||
|
{
|
||
|
IShellExtInit* psei;
|
||
|
// These come from HKCR hence must go through approval
|
||
|
HRESULT hr = SHExtCoCreateInstance(NULL, pguid, NULL, IID_PPV_ARG(IShellExtInit, &psei));
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
psei->Initialize(_pidlFolder, NULL, NULL);
|
||
|
hr = psei->QueryInterface(riid, ppv);
|
||
|
psei->Release();
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
STDMETHODIMP CCategoryProvider::CreateCategory(GUID* pguid, REFIID riid, void** ppv)
|
||
|
{
|
||
|
HRESULT hr = E_NOINTERFACE;
|
||
|
if (_hdaCat != NULL)
|
||
|
{
|
||
|
int c = DSA_GetItemCount(_hdaCat);
|
||
|
for (int i = 0; i < c; i++)
|
||
|
{
|
||
|
CATCACHE* pcc = (CATCACHE*)DSA_GetItemPtr(_hdaCat, i);
|
||
|
ASSERT(pcc != NULL);
|
||
|
|
||
|
if (IsEqualGUID(pcc->guid, *pguid))
|
||
|
{
|
||
|
if (!pcc->punk)
|
||
|
{
|
||
|
hr = CreateInstance(pguid, IID_PPV_ARG(IUnknown, &pcc->punk));
|
||
|
}
|
||
|
|
||
|
if (pcc->punk)
|
||
|
{
|
||
|
hr = pcc->punk->QueryInterface(riid, ppv);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
// Not in the cache? Just try a create
|
||
|
hr = CreateInstance(pguid, riid, ppv);
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
STDAPI CCategoryProvider_Create(const GUID* pguid, const SHCOLUMNID* pscid, HKEY hkey, const CATLIST* pcl, IShellFolder* psf, REFIID riid, void **ppv)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
CCategoryProvider *pdext = new CCategoryProvider();
|
||
|
if (pdext)
|
||
|
{
|
||
|
hr = pdext->Initialize(pguid, pscid, NULL, hkey, pcl, psf);
|
||
|
if (SUCCEEDED(hr))
|
||
|
hr = pdext->QueryInterface(riid, ppv);
|
||
|
|
||
|
pdext->Release();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*ppv = NULL;
|
||
|
hr = E_OUTOFMEMORY;
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
/////////////////////////////////////////////////////////
|
||
|
// Time Categorizer
|
||
|
|
||
|
STDAPI CTimeCategorizer_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
|
||
|
{
|
||
|
HRESULT hr = E_OUTOFMEMORY;
|
||
|
CTimeCategorizer* p = new CTimeCategorizer();
|
||
|
if (p)
|
||
|
{
|
||
|
hr = p->QueryInterface(riid, ppv);
|
||
|
p->Release();
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
STDAPI CTimeCategorizer_Create(IShellFolder2* psf2, const SHCOLUMNID* pscid, REFIID riid, void **ppv)
|
||
|
{
|
||
|
HRESULT hr = E_OUTOFMEMORY;
|
||
|
CTimeCategorizer* p = new CTimeCategorizer(pscid, psf2);
|
||
|
if (p)
|
||
|
{
|
||
|
hr = p->QueryInterface(riid, ppv);
|
||
|
p->Release();
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
CTimeCategorizer::CTimeCategorizer(const SHCOLUMNID* pscid, IShellFolder2* psf) : _cRef(1)
|
||
|
{
|
||
|
_psf = psf;
|
||
|
ASSERT(psf);
|
||
|
psf->AddRef();
|
||
|
_scid = *pscid;
|
||
|
}
|
||
|
|
||
|
CTimeCategorizer::CTimeCategorizer() : _cRef(1)
|
||
|
{
|
||
|
_scid = SCID_WRITETIME;
|
||
|
}
|
||
|
|
||
|
CTimeCategorizer::~CTimeCategorizer()
|
||
|
{
|
||
|
ATOMICRELEASE(_psf);
|
||
|
|
||
|
}
|
||
|
|
||
|
HRESULT CTimeCategorizer::QueryInterface(REFIID riid, void **ppv)
|
||
|
{
|
||
|
static const QITAB qit[] =
|
||
|
{
|
||
|
QITABENT(CTimeCategorizer, ICategorizer),
|
||
|
QITABENT(CTimeCategorizer, IShellExtInit),
|
||
|
{ 0 },
|
||
|
};
|
||
|
return QISearch(this, qit, riid, ppv);
|
||
|
}
|
||
|
|
||
|
ULONG CTimeCategorizer::AddRef()
|
||
|
{
|
||
|
return InterlockedIncrement(&_cRef);
|
||
|
}
|
||
|
|
||
|
ULONG CTimeCategorizer::Release()
|
||
|
{
|
||
|
if (InterlockedDecrement(&_cRef))
|
||
|
return _cRef;
|
||
|
|
||
|
delete this;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
HRESULT CTimeCategorizer::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdobj, HKEY hkeyProgID)
|
||
|
{
|
||
|
ATOMICRELEASE(_psf);
|
||
|
return SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder2, pidlFolder, &_psf));
|
||
|
}
|
||
|
|
||
|
HRESULT CTimeCategorizer::GetDescription(LPWSTR pszDesc, UINT cch)
|
||
|
{
|
||
|
LoadString(HINST_THISDLL, IDS_GROUPBYTIME, pszDesc, cch);
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
static const int mpcdymoAccum[13] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
|
||
|
|
||
|
int GetDaysForMonth(int yr, int mo)
|
||
|
{
|
||
|
int cdy;
|
||
|
|
||
|
if (yr == 1752 && mo == 9)
|
||
|
return 19;
|
||
|
|
||
|
cdy = mpcdymoAccum[mo] - mpcdymoAccum[mo - 1];
|
||
|
if (mo == 2 && (yr & 03) == 0 && (yr <= 1750 || yr % 100 != 0 || yr % 400 == 0))
|
||
|
cdy++;
|
||
|
|
||
|
return cdy;
|
||
|
}
|
||
|
|
||
|
int GetDaysForLastMonth(int year, int month)
|
||
|
{
|
||
|
if (month == 1)
|
||
|
{
|
||
|
year--;
|
||
|
month = 12;
|
||
|
}
|
||
|
else
|
||
|
month--;
|
||
|
|
||
|
return GetDaysForMonth(year, month);
|
||
|
|
||
|
}
|
||
|
|
||
|
HRESULT CTimeCategorizer::GetCategory(UINT cidl, LPCITEMIDLIST * apidl, DWORD* rgCategoryIds)
|
||
|
{
|
||
|
SYSTEMTIME stCur;
|
||
|
GetLocalTime(&stCur);
|
||
|
|
||
|
for (UINT i = 0; i < cidl; i++)
|
||
|
{
|
||
|
FILETIME ft;
|
||
|
|
||
|
// Get the time data
|
||
|
if (SUCCEEDED(GetDateProperty(_psf, apidl[i], &_scid, &ft)))
|
||
|
{
|
||
|
// Convert it to a usable format
|
||
|
SYSTEMTIME stFile;
|
||
|
FileTimeToLocalFileTime(&ft, &ft);
|
||
|
FileTimeToSystemTime(&ft, &stFile);
|
||
|
|
||
|
if (stFile.wYear == stCur.wYear)
|
||
|
{
|
||
|
if (stFile.wMonth > stCur.wMonth)
|
||
|
{
|
||
|
if (stFile.wMonth == stCur.wMonth + 1)
|
||
|
{
|
||
|
rgCategoryIds[i] = IDS_NEXTMONTH;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
rgCategoryIds[i] = IDS_LATERTHISYEAR;
|
||
|
}
|
||
|
}
|
||
|
else if (stFile.wMonth == stCur.wMonth)
|
||
|
{
|
||
|
if (stFile.wDay == stCur.wDay + 1)
|
||
|
{
|
||
|
rgCategoryIds[i] = IDS_TOMORROW;
|
||
|
}
|
||
|
else if (stFile.wDay == stCur.wDay + 2)
|
||
|
{
|
||
|
rgCategoryIds[i] = IDS_TWODAYSFROMNOW;
|
||
|
}
|
||
|
else if (stFile.wDay == stCur.wDay)
|
||
|
{
|
||
|
rgCategoryIds[i] = IDS_TODAY;
|
||
|
}
|
||
|
else if (stFile.wDay == stCur.wDay - 1)
|
||
|
{
|
||
|
rgCategoryIds[i] = IDS_YESTERDAY;
|
||
|
}
|
||
|
else if (stFile.wDayOfWeek < stCur.wDayOfWeek &&
|
||
|
stFile.wDay < stCur.wDay &&
|
||
|
stCur.wDay - stCur.wDayOfWeek > 0 &&
|
||
|
stFile.wDay >= stCur.wDay - stCur.wDayOfWeek)
|
||
|
{
|
||
|
rgCategoryIds[i] = IDS_EARLIERTHISWEEK;
|
||
|
}
|
||
|
else if (stFile.wDayOfWeek > stCur.wDayOfWeek &&
|
||
|
stFile.wDay > stCur.wDay &&
|
||
|
stFile.wDay <= stCur.wDay + (7 - stCur.wDayOfWeek))
|
||
|
{
|
||
|
rgCategoryIds[i] = IDS_LATERTHISWEEK;
|
||
|
}
|
||
|
else if (stFile.wDay > stCur.wDay)
|
||
|
{
|
||
|
rgCategoryIds[i] = IDS_LATERTHISMONTH;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
int fileDays = GetDaysForLastMonth(stFile.wYear, stFile.wMonth - 1) + stFile.wDay;
|
||
|
int curDays = GetDaysForLastMonth(stCur.wYear, stCur.wMonth - 1) + stCur.wDay;
|
||
|
|
||
|
if (fileDays < (curDays - stCur.wDayOfWeek) &&
|
||
|
fileDays > (curDays - stCur.wDayOfWeek - 7))
|
||
|
{
|
||
|
rgCategoryIds[i] = IDS_LASTWEEK;
|
||
|
}
|
||
|
else if (fileDays < (curDays - stCur.wDayOfWeek - 7) &&
|
||
|
fileDays > (curDays - stCur.wDayOfWeek - 14))
|
||
|
{
|
||
|
rgCategoryIds[i] = IDS_TWOWEEKSAGO;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
rgCategoryIds[i] = IDS_EARLIERTHISMONTH;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if (stFile.wMonth == stCur.wMonth - 1 ||
|
||
|
(stFile.wMonth == 12 &&
|
||
|
stCur.wMonth == 1))
|
||
|
{
|
||
|
rgCategoryIds[i] = IDS_LASTMONTH;
|
||
|
}
|
||
|
else if (stFile.wMonth == stCur.wMonth - 2 ||
|
||
|
(stFile.wMonth == 12 && stCur.wMonth == 2) ||
|
||
|
(stFile.wMonth == 11 && stCur.wMonth == 1))
|
||
|
{
|
||
|
rgCategoryIds[i] = IDS_TWOMONTHSAGO;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
rgCategoryIds[i] = IDS_EARLIERTHISYEAR;
|
||
|
}
|
||
|
}
|
||
|
else if (stFile.wYear == stCur.wYear - 1)
|
||
|
{
|
||
|
rgCategoryIds[i] = IDS_LASTYEAR;
|
||
|
}
|
||
|
else if (stFile.wYear == stCur.wYear - 2)
|
||
|
{
|
||
|
rgCategoryIds[i] = IDS_TWOYEARSAGO;
|
||
|
}
|
||
|
else if (stFile.wYear < stCur.wYear - 2)
|
||
|
{
|
||
|
rgCategoryIds[i] = IDS_LONGTIMEAGO;
|
||
|
}
|
||
|
else if (stFile.wYear == stCur.wYear + 1)
|
||
|
{
|
||
|
rgCategoryIds[i] = IDS_NEXTYEAR;
|
||
|
}
|
||
|
else if (stFile.wYear > stCur.wYear + 2)
|
||
|
{
|
||
|
rgCategoryIds[i] = IDS_SOMETIMETHISMILLENNIA;
|
||
|
}
|
||
|
else if (stFile.wYear > (stCur.wYear / 1000) * 1000 + 1000) // 2050 / 1000 = 2. 2 * 1000 = 2000. 2000 + 1000 = 3000 i.e. next millennium
|
||
|
{
|
||
|
rgCategoryIds[i] = IDS_SOMEFUTUREDATE;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
rgCategoryIds[i] = IDS_UNSPECIFIED;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT CTimeCategorizer::GetCategoryInfo(DWORD dwCategoryId, CATEGORY_INFO* pci)
|
||
|
{
|
||
|
LoadString(HINST_THISDLL, dwCategoryId, pci->wszName, ARRAYSIZE(pci->wszName));
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT CTimeCategorizer::CompareCategory(CATSORT_FLAGS csfFlags, DWORD dwCategoryId1, DWORD dwCategoryId2)
|
||
|
{
|
||
|
if (dwCategoryId1 == dwCategoryId2)
|
||
|
return ResultFromShort(0);
|
||
|
else if (dwCategoryId1 == IDS_GROUPFOLDERS)
|
||
|
return ResultFromShort(-1);
|
||
|
else if (dwCategoryId2 == IDS_GROUPFOLDERS)
|
||
|
return ResultFromShort(1);
|
||
|
else if (dwCategoryId1 < dwCategoryId2)
|
||
|
return ResultFromShort(-1);
|
||
|
else
|
||
|
return ResultFromShort(1);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/////////////////////////////////////////////////////////
|
||
|
// Size Categorizer
|
||
|
|
||
|
STDAPI CDriveSizeCategorizer_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
|
||
|
{
|
||
|
HRESULT hr = E_OUTOFMEMORY;
|
||
|
CSizeCategorizer* p = new CSizeCategorizer(TRUE);
|
||
|
if (p)
|
||
|
{
|
||
|
hr = p->QueryInterface(riid, ppv);
|
||
|
p->Release();
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
STDAPI CSizeCategorizer_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
|
||
|
{
|
||
|
HRESULT hr = E_OUTOFMEMORY;
|
||
|
CSizeCategorizer* p = new CSizeCategorizer(FALSE);
|
||
|
if (p)
|
||
|
{
|
||
|
hr = p->QueryInterface(riid, ppv);
|
||
|
p->Release();
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
STDAPI CSizeCategorizer_Create(IShellFolder2* psf2, REFIID riid, void **ppv)
|
||
|
{
|
||
|
HRESULT hr = E_OUTOFMEMORY;
|
||
|
CSizeCategorizer* p = new CSizeCategorizer(psf2);
|
||
|
if (p)
|
||
|
{
|
||
|
hr = p->QueryInterface(riid, ppv);
|
||
|
p->Release();
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
CSizeCategorizer::CSizeCategorizer(IShellFolder2* psf) : _cRef(1)
|
||
|
{
|
||
|
_psf = psf;
|
||
|
ASSERT(psf);
|
||
|
psf->AddRef();
|
||
|
}
|
||
|
|
||
|
CSizeCategorizer::CSizeCategorizer(BOOL fLarge) : _cRef(1), _fLarge(fLarge)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
CSizeCategorizer::~CSizeCategorizer()
|
||
|
{
|
||
|
ATOMICRELEASE(_psf);
|
||
|
|
||
|
}
|
||
|
|
||
|
HRESULT CSizeCategorizer::QueryInterface(REFIID riid, void **ppv)
|
||
|
{
|
||
|
static const QITAB qit[] =
|
||
|
{
|
||
|
QITABENT(CSizeCategorizer, ICategorizer),
|
||
|
QITABENT(CSizeCategorizer, IShellExtInit),
|
||
|
{ 0 },
|
||
|
};
|
||
|
return QISearch(this, qit, riid, ppv);
|
||
|
}
|
||
|
|
||
|
ULONG CSizeCategorizer::AddRef()
|
||
|
{
|
||
|
return InterlockedIncrement(&_cRef);
|
||
|
}
|
||
|
|
||
|
ULONG CSizeCategorizer::Release()
|
||
|
{
|
||
|
if (InterlockedDecrement(&_cRef))
|
||
|
return _cRef;
|
||
|
|
||
|
delete this;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
HRESULT CSizeCategorizer::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdobj, HKEY hkeyProgID)
|
||
|
{
|
||
|
ATOMICRELEASE(_psf);
|
||
|
return SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder2, pidlFolder, &_psf));
|
||
|
}
|
||
|
|
||
|
HRESULT CSizeCategorizer::GetDescription(LPWSTR pszDesc, UINT cch)
|
||
|
{
|
||
|
LoadString(HINST_THISDLL, IDS_GROUPBYSIZE, pszDesc, cch);
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
const static ULONGLONG s_rgSizesSmall[] =
|
||
|
{
|
||
|
// 130mb 16mb 1mb 100k 10l
|
||
|
134217728, 16777216, 1048576, 131072, 32768, 0
|
||
|
};
|
||
|
|
||
|
const static ULONGLONG s_rgSizesLarge[] =
|
||
|
{
|
||
|
// 80gig 25gig 10gig 2gig 500mb
|
||
|
80000000000, 25000000000, 10000000000, 2000000000, 500000000, 0
|
||
|
};
|
||
|
|
||
|
|
||
|
HRESULT CSizeCategorizer::GetCategory(UINT cidl, LPCITEMIDLIST * apidl, DWORD* rgCategoryIds)
|
||
|
{
|
||
|
if (_psf == NULL)
|
||
|
return E_ACCESSDENIED; // Not initialized yet.
|
||
|
|
||
|
for (UINT i = 0; i < cidl; i++)
|
||
|
{
|
||
|
const ULONGLONG* pll = _fLarge? s_rgSizesLarge : s_rgSizesSmall;
|
||
|
|
||
|
// Get the size data
|
||
|
ULONGLONG ullSize;
|
||
|
if (SUCCEEDED(GetLongProperty(_psf, apidl[i], _fLarge?&SCID_CAPACITY:&SCID_SIZE, &ullSize)))
|
||
|
{
|
||
|
if (ullSize >= pll[0])
|
||
|
rgCategoryIds[i] = IDS_GIGANTIC;
|
||
|
if (ullSize < pll[0])
|
||
|
rgCategoryIds[i] = IDS_HUGE;
|
||
|
if (ullSize < pll[1]) // Under 16mb
|
||
|
rgCategoryIds[i] = IDS_LARGE;
|
||
|
if (ullSize < pll[2]) // Under 1mb
|
||
|
rgCategoryIds[i] = IDS_MEDIUM;
|
||
|
if (ullSize < pll[3]) // Under 100k
|
||
|
rgCategoryIds[i] = IDS_SMALL;
|
||
|
if (ullSize < pll[4]) // Under 10k
|
||
|
rgCategoryIds[i] = IDS_TINY;
|
||
|
if (ullSize == pll[5]) // Zero sized files
|
||
|
{
|
||
|
if (SHGetAttributes(_psf, apidl[i], SFGAO_FOLDER))
|
||
|
{
|
||
|
rgCategoryIds[i] = IDS_FOLDERS;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
rgCategoryIds[i] = IDS_ZERO;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
rgCategoryIds[i] = IDS_UNSPECIFIED;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT CSizeCategorizer::GetCategoryInfo(DWORD dwCategoryId, CATEGORY_INFO* pci)
|
||
|
{
|
||
|
LoadString(HINST_THISDLL, dwCategoryId, pci->wszName, ARRAYSIZE(pci->wszName));
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT CSizeCategorizer::CompareCategory(CATSORT_FLAGS csfFlags, DWORD dwCategoryId1, DWORD dwCategoryId2)
|
||
|
{
|
||
|
if (dwCategoryId1 == dwCategoryId2)
|
||
|
return ResultFromShort(0);
|
||
|
else if (dwCategoryId1 == IDS_GROUPFOLDERS)
|
||
|
return ResultFromShort(-1);
|
||
|
else if (dwCategoryId2 == IDS_GROUPFOLDERS)
|
||
|
return ResultFromShort(1);
|
||
|
else if (dwCategoryId1 < dwCategoryId2)
|
||
|
return ResultFromShort(-1);
|
||
|
else
|
||
|
return ResultFromShort(1);
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////////////////////////////
|
||
|
// Type Categorizer
|
||
|
|
||
|
STDAPI CDriveTypeCategorizer_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
|
||
|
{
|
||
|
CDriveTypeCategorizer *p = new CDriveTypeCategorizer();
|
||
|
if (!p)
|
||
|
return E_OUTOFMEMORY;
|
||
|
|
||
|
HRESULT hr = p->QueryInterface(riid, ppv);
|
||
|
p->Release();
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
CDriveTypeCategorizer::CDriveTypeCategorizer(IShellFolder2* psf) :
|
||
|
_cRef(1)
|
||
|
{
|
||
|
_psf = psf;
|
||
|
ASSERT(psf);
|
||
|
psf->AddRef();
|
||
|
}
|
||
|
|
||
|
CDriveTypeCategorizer::CDriveTypeCategorizer() :
|
||
|
_cRef(1)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
CDriveTypeCategorizer::~CDriveTypeCategorizer()
|
||
|
{
|
||
|
ATOMICRELEASE(_psf);
|
||
|
}
|
||
|
|
||
|
HRESULT CDriveTypeCategorizer::QueryInterface(REFIID riid, void **ppv)
|
||
|
{
|
||
|
static const QITAB qit[] =
|
||
|
{
|
||
|
QITABENT(CDriveTypeCategorizer, ICategorizer),
|
||
|
QITABENT(CDriveTypeCategorizer, IShellExtInit),
|
||
|
{ 0 },
|
||
|
};
|
||
|
return QISearch(this, qit, riid, ppv);
|
||
|
}
|
||
|
|
||
|
ULONG CDriveTypeCategorizer::AddRef()
|
||
|
{
|
||
|
return InterlockedIncrement(&_cRef);
|
||
|
}
|
||
|
|
||
|
ULONG CDriveTypeCategorizer::Release()
|
||
|
{
|
||
|
if (InterlockedDecrement(&_cRef))
|
||
|
return _cRef;
|
||
|
|
||
|
delete this;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
HRESULT CDriveTypeCategorizer::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdobj, HKEY hkeyProgID)
|
||
|
{
|
||
|
ATOMICRELEASE(_psf);
|
||
|
return SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder2, pidlFolder, &_psf));
|
||
|
}
|
||
|
|
||
|
HRESULT CDriveTypeCategorizer::GetDescription(LPWSTR pszDesc, UINT cch)
|
||
|
{
|
||
|
LoadString(HINST_THISDLL, IDS_GROUPBYDRIVETYPE, pszDesc, cch);
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
const struct { DWORD dwDescriptionId; UINT uIDGroup; } c_drives_mapping[] =
|
||
|
{
|
||
|
{ SHDID_COMPUTER_FIXED , IDS_DRIVES_FIXED_GROUP },
|
||
|
{ SHDID_COMPUTER_DRIVE35 , IDS_DRIVES_REMOVABLE_GROUP },
|
||
|
{ SHDID_COMPUTER_REMOVABLE , IDS_DRIVES_REMOVABLE_GROUP },
|
||
|
{ SHDID_COMPUTER_CDROM , IDS_DRIVES_REMOVABLE_GROUP },
|
||
|
{ SHDID_COMPUTER_NETDRIVE , IDS_DRIVES_NETDRIVE_GROUP },
|
||
|
{ SHDID_COMPUTER_OTHER , IDS_DRIVES_OTHER_GROUP },
|
||
|
{ SHDID_COMPUTER_DRIVE525 , IDS_DRIVES_REMOVABLE_GROUP },
|
||
|
{ SHDID_COMPUTER_RAMDISK , IDS_DRIVES_OTHER_GROUP },
|
||
|
{ SHDID_COMPUTER_IMAGING , IDS_DRIVES_IMAGING_GROUP },
|
||
|
{ SHDID_COMPUTER_AUDIO , IDS_DRIVES_AUDIO_GROUP },
|
||
|
{ SHDID_COMPUTER_SHAREDDOCS, IDS_DRIVES_SHAREDDOCS_GROUP},
|
||
|
};
|
||
|
|
||
|
HRESULT CDriveTypeCategorizer::GetCategory(UINT cidl, LPCITEMIDLIST * apidl, DWORD* rgCategoryIds)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
|
||
|
if (_psf == NULL)
|
||
|
{
|
||
|
hr = E_ACCESSDENIED; // Not initialized yet.
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
for (UINT i = 0; i < cidl; i++)
|
||
|
{
|
||
|
rgCategoryIds[i] = IDS_DRIVES_OTHER_GROUP;
|
||
|
|
||
|
VARIANT v;
|
||
|
// Get the type data
|
||
|
hr = _psf->GetDetailsEx(apidl[i], &SCID_DESCRIPTIONID, &v);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
SHDESCRIPTIONID did;
|
||
|
if (VariantToBuffer(&v, &did, sizeof(did)))
|
||
|
{
|
||
|
for (int j = 0; j < ARRAYSIZE(c_drives_mapping); j++)
|
||
|
{
|
||
|
if (did.dwDescriptionId == c_drives_mapping[j].dwDescriptionId)
|
||
|
{
|
||
|
rgCategoryIds[i] = c_drives_mapping[j].uIDGroup;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
VariantClear(&v);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
hr = S_OK;
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT CDriveTypeCategorizer::GetCategoryInfo(DWORD dwCategoryId, CATEGORY_INFO* pci)
|
||
|
{
|
||
|
LoadString(HINST_THISDLL, dwCategoryId, pci->wszName, ARRAYSIZE(pci->wszName));
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT CDriveTypeCategorizer::CompareCategory(CATSORT_FLAGS csfFlags, DWORD dwCategoryId1, DWORD dwCategoryId2)
|
||
|
{
|
||
|
if (dwCategoryId1 == dwCategoryId2)
|
||
|
return ResultFromShort(0);
|
||
|
else if (dwCategoryId1 < dwCategoryId2)
|
||
|
return ResultFromShort(-1);
|
||
|
else
|
||
|
return ResultFromShort(1);
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////////////////////////////
|
||
|
// FreeSpace Categorizer
|
||
|
|
||
|
CFreeSpaceCategorizer::CFreeSpaceCategorizer() : _cRef(1)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
CFreeSpaceCategorizer::~CFreeSpaceCategorizer()
|
||
|
{
|
||
|
ATOMICRELEASE(_psf);
|
||
|
|
||
|
}
|
||
|
|
||
|
HRESULT CFreeSpaceCategorizer::QueryInterface(REFIID riid, void **ppv)
|
||
|
{
|
||
|
static const QITAB qit[] =
|
||
|
{
|
||
|
QITABENT(CFreeSpaceCategorizer, ICategorizer),
|
||
|
QITABENT(CFreeSpaceCategorizer, IShellExtInit),
|
||
|
{ 0 },
|
||
|
};
|
||
|
return QISearch(this, qit, riid, ppv);
|
||
|
}
|
||
|
|
||
|
ULONG CFreeSpaceCategorizer::AddRef()
|
||
|
{
|
||
|
return InterlockedIncrement(&_cRef);
|
||
|
}
|
||
|
|
||
|
ULONG CFreeSpaceCategorizer::Release()
|
||
|
{
|
||
|
if (InterlockedDecrement(&_cRef))
|
||
|
return _cRef;
|
||
|
|
||
|
delete this;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
HRESULT CFreeSpaceCategorizer::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdobj, HKEY hkeyProgID)
|
||
|
{
|
||
|
ATOMICRELEASE(_psf);
|
||
|
return SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder2, pidlFolder, &_psf));
|
||
|
}
|
||
|
|
||
|
HRESULT CFreeSpaceCategorizer::GetDescription(LPWSTR pszDesc, UINT cch)
|
||
|
{
|
||
|
LoadString(HINST_THISDLL, IDS_GROUPBYFREESPACE, pszDesc, cch);
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT CFreeSpaceCategorizer::GetCategory(UINT cidl, LPCITEMIDLIST* apidl, DWORD* rgCategoryIds)
|
||
|
{
|
||
|
if (_psf == NULL)
|
||
|
return E_ACCESSDENIED; // Not initialized yet.
|
||
|
|
||
|
for (UINT i = 0; i < cidl; i++)
|
||
|
{
|
||
|
rgCategoryIds[i] = IDS_UNSPECIFIED;
|
||
|
|
||
|
// Get the total size and free space
|
||
|
ULONGLONG ullSize;
|
||
|
if (SUCCEEDED(GetLongProperty(_psf, apidl[i], &SCID_CAPACITY, &ullSize)))
|
||
|
{
|
||
|
ULONGLONG ullFree;
|
||
|
if (SUCCEEDED(GetLongProperty(_psf, apidl[i], &SCID_FREESPACE, &ullFree)))
|
||
|
{
|
||
|
// Prevent divide by zero
|
||
|
if (ullSize == 0)
|
||
|
{
|
||
|
rgCategoryIds[i] = IDS_UNSPECIFIED;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Turning this into a percent. DWORD cast is ok.
|
||
|
rgCategoryIds[i] = (static_cast<DWORD>((ullFree * 100) / ullSize) / 10) * 10;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT CFreeSpaceCategorizer::GetCategoryInfo(DWORD dwCategoryId, CATEGORY_INFO* pci)
|
||
|
{
|
||
|
if (dwCategoryId == IDS_UNSPECIFIED)
|
||
|
{
|
||
|
LoadString(HINST_THISDLL, IDS_UNSPECIFIED, pci->wszName, ARRAYSIZE(pci->wszName));
|
||
|
return S_OK;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TCHAR szName[MAX_PATH];
|
||
|
LoadString(HINST_THISDLL, IDS_FREESPACEPERCENT, szName, ARRAYSIZE(szName));
|
||
|
wnsprintf(pci->wszName, ARRAYSIZE(pci->wszName), szName, dwCategoryId);
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
return E_FAIL;
|
||
|
}
|
||
|
|
||
|
HRESULT CFreeSpaceCategorizer::CompareCategory(CATSORT_FLAGS csfFlags, DWORD dwCategoryId1, DWORD dwCategoryId2)
|
||
|
{
|
||
|
if (dwCategoryId1 == IDS_UNSPECIFIED)
|
||
|
return ResultFromShort(1);
|
||
|
else if (dwCategoryId2 == IDS_UNSPECIFIED)
|
||
|
return ResultFromShort(-1);
|
||
|
else if (dwCategoryId1 == dwCategoryId2)
|
||
|
return ResultFromShort(0);
|
||
|
else if (dwCategoryId1 < dwCategoryId2)
|
||
|
return ResultFromShort(-1);
|
||
|
else
|
||
|
return ResultFromShort(1);
|
||
|
}
|
||
|
|
||
|
STDAPI CFreeSpaceCategorizer_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
|
||
|
{
|
||
|
HRESULT hr = E_OUTOFMEMORY;
|
||
|
CFreeSpaceCategorizer* p = new CFreeSpaceCategorizer();
|
||
|
if (p)
|
||
|
{
|
||
|
hr = p->QueryInterface(riid, ppv);
|
||
|
p->Release();
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
/////////////////////////////////////////////////////////
|
||
|
// Detail Categorizer
|
||
|
STDAPI CDetailCategorizer_Create(const SHCOLUMNID& pscid, IShellFolder2* psf2, REFIID riid, void **ppv)
|
||
|
{
|
||
|
HRESULT hr = E_OUTOFMEMORY;
|
||
|
CDetailCategorizer* p = new CDetailCategorizer(psf2, pscid);
|
||
|
if (p)
|
||
|
{
|
||
|
hr = p->QueryInterface(riid, ppv);
|
||
|
p->Release();
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
CDetailCategorizer::CDetailCategorizer(IShellFolder2* psf, const SHCOLUMNID& scid) : _cRef(1)
|
||
|
{
|
||
|
_psf = psf;
|
||
|
psf->AddRef();
|
||
|
_scid = scid;
|
||
|
|
||
|
_hash = CreateHashItemTable(10, sizeof(DWORD));
|
||
|
_hdpaKeys = DPA_Create(10);
|
||
|
|
||
|
}
|
||
|
|
||
|
CDetailCategorizer::~CDetailCategorizer()
|
||
|
{
|
||
|
_psf->Release();
|
||
|
DestroyHashItemTable(_hash);
|
||
|
DPA_Destroy(_hdpaKeys);
|
||
|
}
|
||
|
|
||
|
HRESULT CDetailCategorizer::QueryInterface(REFIID riid, void **ppv)
|
||
|
{
|
||
|
static const QITAB qit[] =
|
||
|
{
|
||
|
QITABENT(CDetailCategorizer, ICategorizer),
|
||
|
{ 0 },
|
||
|
};
|
||
|
return QISearch(this, qit, riid, ppv);
|
||
|
}
|
||
|
|
||
|
ULONG CDetailCategorizer::AddRef()
|
||
|
{
|
||
|
return InterlockedIncrement(&_cRef);
|
||
|
}
|
||
|
|
||
|
ULONG CDetailCategorizer::Release()
|
||
|
{
|
||
|
if (InterlockedDecrement(&_cRef))
|
||
|
return _cRef;
|
||
|
|
||
|
delete this;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
HRESULT CDetailCategorizer::GetDescription(LPWSTR pszDesc, UINT cch)
|
||
|
{
|
||
|
return E_FAIL;
|
||
|
}
|
||
|
|
||
|
HRESULT CDetailCategorizer::GetCategory(UINT cidl, LPCITEMIDLIST * apidl, DWORD* rgCategoryIds)
|
||
|
{
|
||
|
if (!_hash || !_hdpaKeys)
|
||
|
return E_OUTOFMEMORY;
|
||
|
|
||
|
for (UINT i = 0; i < cidl; i++)
|
||
|
{
|
||
|
VARIANT v;
|
||
|
|
||
|
rgCategoryIds[i] = GROUPID_UNSPECIFIED;
|
||
|
|
||
|
HRESULT hr = _psf->GetDetailsEx(apidl[i], &_scid, &v);
|
||
|
if (hr == S_OK) // GetDetails returns S_FALSE for failure.
|
||
|
{
|
||
|
WCHAR szValue[MAX_PATH];
|
||
|
if (SUCCEEDED(SHFormatForDisplay(_scid.fmtid, _scid.pid, (PROPVARIANT*)&v, PUIFFDF_DEFAULT, szValue, ARRAYSIZE(szValue))))
|
||
|
{
|
||
|
LPCTSTR pszKey = FindHashItem(_hash, szValue);
|
||
|
if (pszKey)
|
||
|
{
|
||
|
rgCategoryIds[i] = (DWORD)GetHashItemData(_hash, pszKey, 0);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pszKey = AddHashItem(_hash, szValue);
|
||
|
if (pszKey)
|
||
|
{
|
||
|
rgCategoryIds[i] = DPA_AppendPtr(_hdpaKeys, (void*)pszKey);
|
||
|
SetHashItemData(_hash, pszKey, 0, rgCategoryIds[i]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
VariantClear(&v);
|
||
|
}
|
||
|
}
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT CDetailCategorizer::GetCategoryInfo(DWORD dwCategoryId, CATEGORY_INFO* pci)
|
||
|
{
|
||
|
HRESULT hr = E_OUTOFMEMORY;
|
||
|
if (_hash|| _hdpaKeys)
|
||
|
{
|
||
|
if (dwCategoryId == GROUPID_UNSPECIFIED || dwCategoryId == GROUPID_FOLDER)
|
||
|
{
|
||
|
LoadString(HINST_THISDLL, STRINGID_FROM_GROUPID(dwCategoryId), pci->wszName, ARRAYSIZE(pci->wszName));
|
||
|
hr = S_OK;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
LPCTSTR pszKey = (LPCTSTR)DPA_FastGetPtr(_hdpaKeys, dwCategoryId);
|
||
|
if (pszKey)
|
||
|
{
|
||
|
LPCTSTR psz = FindHashItem(_hash, pszKey);
|
||
|
if (psz)
|
||
|
{
|
||
|
StrCpyN(pci->wszName, psz, ARRAYSIZE(pci->wszName));
|
||
|
hr = S_OK;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hr = E_INVALIDARG;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT CDetailCategorizer::CompareCategory(CATSORT_FLAGS csfFlags, DWORD dwCategoryId1, DWORD dwCategoryId2)
|
||
|
{
|
||
|
if (dwCategoryId1 == dwCategoryId2)
|
||
|
{
|
||
|
return ResultFromShort(0);
|
||
|
}
|
||
|
else if (dwCategoryId1 == GROUPID_UNSPECIFIED ||
|
||
|
dwCategoryId2 == GROUPID_UNSPECIFIED)
|
||
|
{
|
||
|
return ResultFromShort((dwCategoryId1 == GROUPID_UNSPECIFIED)? 1 : -1);
|
||
|
}
|
||
|
else if (dwCategoryId1 == GROUPID_FOLDER)
|
||
|
{
|
||
|
return ResultFromShort(-1);
|
||
|
}
|
||
|
else if (dwCategoryId2 == GROUPID_FOLDER)
|
||
|
{
|
||
|
return ResultFromShort(1);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
LPCTSTR pszKey1 = (LPCTSTR)DPA_FastGetPtr(_hdpaKeys, dwCategoryId1);
|
||
|
LPCTSTR pszKey2 = (LPCTSTR)DPA_FastGetPtr(_hdpaKeys, dwCategoryId2);
|
||
|
LPCTSTR psz1 = FindHashItem(_hash, pszKey1);
|
||
|
LPCTSTR psz2 = FindHashItem(_hash, pszKey2);
|
||
|
|
||
|
return ResultFromShort(lstrcmpi(psz1, psz2));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/////////////////////////////////////////////////////////
|
||
|
// Alphanumeric Categorizer
|
||
|
|
||
|
STDAPI CAlphaCategorizer_Create(IShellFolder2* psf2, REFIID riid, void **ppv)
|
||
|
{
|
||
|
HRESULT hr = E_OUTOFMEMORY;
|
||
|
CAlphaCategorizer* p = new CAlphaCategorizer(psf2);
|
||
|
if (p)
|
||
|
{
|
||
|
hr = p->QueryInterface(riid, ppv);
|
||
|
p->Release();
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
CAlphaCategorizer::CAlphaCategorizer(IShellFolder2* psf) : _cRef(1)
|
||
|
{
|
||
|
_psf = psf;
|
||
|
ASSERT(psf);
|
||
|
psf->AddRef();
|
||
|
}
|
||
|
|
||
|
CAlphaCategorizer::~CAlphaCategorizer()
|
||
|
{
|
||
|
_psf->Release();
|
||
|
|
||
|
}
|
||
|
|
||
|
HRESULT CAlphaCategorizer::QueryInterface(REFIID riid, void **ppv)
|
||
|
{
|
||
|
static const QITAB qit[] =
|
||
|
{
|
||
|
QITABENT(CAlphaCategorizer, ICategorizer),
|
||
|
{ 0 },
|
||
|
};
|
||
|
return QISearch(this, qit, riid, ppv);
|
||
|
}
|
||
|
|
||
|
ULONG CAlphaCategorizer::AddRef()
|
||
|
{
|
||
|
return InterlockedIncrement(&_cRef);
|
||
|
}
|
||
|
|
||
|
ULONG CAlphaCategorizer::Release()
|
||
|
{
|
||
|
if (InterlockedDecrement(&_cRef))
|
||
|
return _cRef;
|
||
|
|
||
|
delete this;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
HRESULT CAlphaCategorizer::GetDescription(LPWSTR pszDesc, UINT cch)
|
||
|
{
|
||
|
LoadString(HINST_THISDLL, IDS_GROUPALPHABETICALLY, pszDesc, cch);
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT CAlphaCategorizer::GetCategory(UINT cidl, LPCITEMIDLIST * apidl, DWORD* rgCategoryIds)
|
||
|
{
|
||
|
if (_psf == NULL)
|
||
|
return E_ACCESSDENIED; // Not initialized yet.
|
||
|
|
||
|
for (UINT i = 0; i < cidl; i++)
|
||
|
{
|
||
|
TCHAR szName[MAX_PATH];
|
||
|
HRESULT hr = DisplayNameOf(_psf, apidl[i], SHGDN_INFOLDER, szName, ARRAYSIZE(szName));
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
if (StrChr(TEXT("~`!@#$%^&*()_-+=1234567890<,>.;:'[]{}|"), szName[0]) != NULL)
|
||
|
{
|
||
|
rgCategoryIds[i] = GROUPID_OTHER;
|
||
|
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CharUpperBuff(szName, 1);
|
||
|
rgCategoryIds[i] = (DWORD)szName[0];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (FAILED(hr))
|
||
|
rgCategoryIds[i] = GROUPID_UNSPECIFIED;
|
||
|
}
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT CAlphaCategorizer::GetCategoryInfo(DWORD dwCategoryId, CATEGORY_INFO* pci)
|
||
|
{
|
||
|
if (GROUPID_UNSPECIFIED == dwCategoryId ||
|
||
|
GROUPID_OTHER == dwCategoryId ||
|
||
|
GROUPID_FOLDER == dwCategoryId)
|
||
|
{
|
||
|
LoadString(HINST_THISDLL, STRINGID_FROM_GROUPID(dwCategoryId), pci->wszName, ARRAYSIZE(pci->wszName));
|
||
|
return S_OK;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pci->wszName[0] = (WCHAR)dwCategoryId;
|
||
|
pci->wszName[1] = 0;
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
HRESULT CAlphaCategorizer::CompareCategory(CATSORT_FLAGS csfFlags, DWORD dwCategoryId1, DWORD dwCategoryId2)
|
||
|
{
|
||
|
if (dwCategoryId1 == dwCategoryId2)
|
||
|
{
|
||
|
return ResultFromShort(0);
|
||
|
}
|
||
|
else if (IDS_UNSPECIFIED == dwCategoryId1 || IDS_GROUPOTHERCHAR == dwCategoryId1)
|
||
|
{
|
||
|
return ResultFromShort(1);
|
||
|
}
|
||
|
else if (IDS_UNSPECIFIED == dwCategoryId2 || IDS_GROUPOTHERCHAR == dwCategoryId2)
|
||
|
{
|
||
|
return ResultFromShort(-1);
|
||
|
}
|
||
|
else if (dwCategoryId1 == IDS_GROUPFOLDERS)
|
||
|
return ResultFromShort(-1);
|
||
|
else if (dwCategoryId2 == IDS_GROUPFOLDERS)
|
||
|
return ResultFromShort(1);
|
||
|
else
|
||
|
{
|
||
|
TCHAR szName1[2] = {(WCHAR)dwCategoryId1, 0};
|
||
|
TCHAR szName2[2] = {(WCHAR)dwCategoryId2, 0};
|
||
|
|
||
|
return ResultFromShort(lstrcmpi(szName1, szName2));
|
||
|
}
|
||
|
}
|
||
|
|