2020-09-30 17:12:32 +02:00

1474 lines
43 KiB
C++

#include "shellprv.h"
#include "caggunk.h"
#include "views.h"
#include "ids.h"
#include "shitemid.h"
#include "datautil.h"
#include "shobjprv.h"
#include "control.h"
#include "sfviewp.h"
#include "drives.h"
#include "infotip.h"
#include "prop.h" // COL_DATA
extern "C"
{
#include "fstreex.h"
}
extern "C" BOOL IsNameListedUnderKey(LPCTSTR pszFileName, LPCTSTR pszKey);
// our pidc type:
typedef struct _IDCONTROL
{
USHORT cb;
int idIcon;
USHORT oName; // cBuf[oName] is start of NAME
USHORT oInfo; // cBuf[oInfo] is start of DESCRIPTION
CHAR cBuf[MAX_PATH+MAX_CCH_CPLNAME+MAX_CCH_CPLINFO]; // cBuf[0] is the start of FILENAME
USHORT uTerm;
} IDCONTROL;
typedef UNALIGNED struct _IDCONTROL *LPIDCONTROL;
typedef struct _IDCONTROLW
{
USHORT cb;
int idIcon;
USHORT oName; // if Unicode .cpl, this will be 0
USHORT oInfo; // if Unicode .cpl, this will be 0
CHAR cBuf[2]; // if Unicode .cpl, cBuf[0] = '\0', cBuf[1] = magic byte
DWORD dwFlags; // Unused; for future expansion
USHORT oNameW; // cBufW[oNameW] is start of NAME
USHORT oInfoW; // cBufW[oInfoW] is start of DESCRIPTION
WCHAR cBufW[MAX_PATH+MAX_CCH_CPLNAME+MAX_CCH_CPLINFO]; // cBufW[0] is the start of FILENAME
} IDCONTROLW;
typedef UNALIGNED struct _IDCONTROLW *LPIDCONTROLW;
// Unicode IDCONTROLs will be flagged by having oName = 0, oInfo = 0,
// cBuf[0] = '\0', and cBuf[1] = UNICODE_CPL_SIGNATURE_BYTE
STDAPI ControlExtractIcon_CreateInstance(LPCTSTR pszSubObject, REFIID riid, void **ppv);
class CControlPanelFolder : public CAggregatedUnknown, IShellFolder2, IPersistFolder2
{
public:
// *** IUnknown ***
STDMETHODIMP QueryInterface(REFIID riid, void **ppv)
{ return CAggregatedUnknown::QueryInterface(riid, ppv); };
STDMETHODIMP_(ULONG) AddRef(void)
{ return CAggregatedUnknown::AddRef(); };
STDMETHODIMP_(ULONG) Release(void)
{ return CAggregatedUnknown::Release(); };
// *** IShellFolder methods ***
STDMETHODIMP ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR lpszDisplayName,
ULONG* pchEaten, LPITEMIDLIST* ppidl, ULONG* pdwAttributes);
STDMETHODIMP EnumObjects(HWND hwnd, DWORD grfFlags, LPENUMIDLIST* ppenumIDList);
STDMETHODIMP BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void** ppv);
STDMETHODIMP BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void** ppv);
STDMETHODIMP CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
STDMETHODIMP CreateViewObject (HWND hwndOwner, REFIID riid, void** ppv);
STDMETHODIMP GetAttributesOf(UINT cidl, LPCITEMIDLIST* apidl, ULONG* rgfInOut);
STDMETHODIMP GetUIObjectOf(HWND hwndOwner, UINT cidl, LPCITEMIDLIST* apidl,
REFIID riid, UINT* prgfInOut, void** ppv);
STDMETHODIMP GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, LPSTRRET lpName);
STDMETHODIMP SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR lpszName, DWORD uFlags,
LPITEMIDLIST* ppidlOut);
// *** IShellFolder2 methods ***
STDMETHODIMP GetDefaultSearchGUID(LPGUID lpGuid);
STDMETHODIMP EnumSearches(LPENUMEXTRASEARCH* ppenum);
STDMETHODIMP GetDefaultColumn(DWORD dwRes, ULONG* pSort, ULONG* pDisplay);
STDMETHODIMP GetDefaultColumnState(UINT iColumn, DWORD* pbState);
STDMETHODIMP GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID* pscid, VARIANT* pv);
STDMETHODIMP GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS* pDetails);
STDMETHODIMP MapColumnToSCID(UINT iColumn, SHCOLUMNID* pscid);
// *** IPersist methods ***
STDMETHODIMP GetClassID(CLSID* pClassID);
// *** IPersistFolder methods ***
STDMETHODIMP Initialize(LPCITEMIDLIST pidl);
// *** IPersistFolder2 methods ***
STDMETHODIMP GetCurFolder(LPITEMIDLIST* ppidl);
protected:
CControlPanelFolder(IUnknown* punkOuter);
~CControlPanelFolder();
// used by the CAggregatedUnknown stuff
HRESULT v_InternalQueryInterface(REFIID riid, void** ppv);
static void GetExecName(LPIDCONTROL pidc, LPTSTR pszParseName, UINT cchParseName);
static HRESULT GetModuleMapped(LPIDCONTROL pidc, LPTSTR pszModule, UINT cchModule,
UINT* pidNewIcon, LPTSTR pszApplet, UINT cchApplet);
static void GetDisplayName(LPIDCONTROL pidc, LPTSTR pszName, UINT cchName);
static void GetModule(LPIDCONTROL pidc, LPTSTR pszModule, UINT cchModule);
static void _GetDescription(LPIDCONTROL pidc, LPTSTR pszDesc, UINT cchDesc);
static LPIDCONTROL _IsValid(LPCITEMIDLIST pidl);
static LPIDCONTROLW _IsUnicodeCPL(LPIDCONTROL pidc);
private:
friend HRESULT CControlPanel_CreateInstance(IUnknown* punkOuter, REFIID riid, void** ppv);
static HRESULT CALLBACK DFMCallBack(IShellFolder *psf, HWND hwndView,
IDataObject *pdtobj, UINT uMsg,
WPARAM wParam, LPARAM lParam);
LPITEMIDLIST _pidl;
IUnknown* _punkReg;
};
class CControlPanelEnum : public IEnumIDList
{
public:
// *** IUnknown methods ***
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
// *** IEnumIDList methods ***
STDMETHODIMP Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched);
STDMETHODIMP Skip(ULONG celt) { return E_NOTIMPL; };
STDMETHODIMP Reset();
STDMETHODIMP Clone(IEnumIDList **ppenum) { return E_NOTIMPL; };
CControlPanelEnum(UINT uFlags);
~CControlPanelEnum();
HRESULT Init();
protected:
LONG _cRef;
ULONG _uFlags;
int _iModuleCur;
int _cControlsOfCurrentModule;
int _iControlCur;
int _cControlsTotal;
int _iRegControls;
MINST _minstCur;
ControlData _cplData;
};
class CControlPanelViewCallback : public CBaseShellFolderViewCB
{
public:
CControlPanelViewCallback(IUnknown *punkFolder, LPCITEMIDLIST pidl)
: CBaseShellFolderViewCB(punkFolder, pidl, SHCNE_UPDATEITEM)
{ }
STDMETHODIMP RealMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
private:
HRESULT OnMERGEMENU(DWORD pv, QCMINFO*lP)
{
CDefFolderMenu_MergeMenu(HINST_THISDLL, 0, POPUP_CONTROLS_POPUPMERGE, lP);
return S_OK;
}
HRESULT OnINVOKECOMMAND(DWORD pv, UINT wP)
{
return CControls_DFMCallBackBG(m_pshf, m_hwndMain, NULL, DFM_INVOKECOMMAND, wP, 0);
}
HRESULT OnGETHELPTEXT(DWORD pv, UINT id, UINT cch, LPTSTR psz)
{
LoadString(HINST_THISDLL, id + IDS_MH_FSIDM_FIRST, psz, cch);;
return S_OK;
}
HRESULT OnDEFITEMCOUNT(DWORD pv, UINT*lP)
{
// if we time out enuming items make a guess at how many items there
// will be to make sure we get large icon mode and a reasonable window size
*lP = 20;
return S_OK;
}
HRESULT OnSize(DWORD pv, UINT cx, UINT cy)
{
ResizeStatus(_punkSite, cx);
return S_OK;
}
HRESULT OnGetPane(DWORD pv, LPARAM dwPaneID, DWORD *pdwPane)
{
if (PANE_ZONE == dwPaneID)
*pdwPane = 2;
return S_OK;
}
HRESULT OnGetHelpTopic( DWORD pv, SFVM_HELPTOPIC_DATA * phtd )
{
StrCpyW( phtd->wszHelpFile, L"cpanel.chm" );
return S_OK;
}
};
STDMETHODIMP CControlPanelViewCallback::RealMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
HANDLE_MSG(0, SFVM_MERGEMENU, OnMERGEMENU);
HANDLE_MSG(0, SFVM_INVOKECOMMAND, OnINVOKECOMMAND);
HANDLE_MSG(0, SFVM_GETHELPTEXT, OnGETHELPTEXT);
HANDLE_MSG(0, SFVM_DEFITEMCOUNT, OnDEFITEMCOUNT);
HANDLE_MSG(0, SFVM_ADDPROPERTYPAGES, SFVCB_OnAddPropertyPages);
HANDLE_MSG(0, SFVM_SIZE, OnSize);
HANDLE_MSG(0, SFVM_GETPANE, OnGetPane);
HANDLE_MSG(0, SFVM_GETHELPTOPIC, OnGetHelpTopic);
default:
return E_FAIL;
}
return NOERROR;
}
IShellFolderViewCB* Controls_CreateSFVCB(IUnknown *punkFolder, LPCITEMIDLIST pidl)
{
return new CControlPanelViewCallback(punkFolder, pidl);
}
// column IDs
enum
{
ICOL_NAME = 0,
ICOL_COMMENT,
};
typedef enum
{
CPL_ICOL_NAME = 0,
CPL_ICOL_COMMENT,
} EnumCplCols;
const COL_DATA c_cpl_cols[] = {
{ CPL_ICOL_NAME, IDS_NAME_COL, 20, LVCFMT_LEFT, &SCID_NAME},
{ CPL_ICOL_COMMENT, IDS_COMMENT_COL, 20, LVCFMT_LEFT, &SCID_Comment},
};
#define PRINTERS_SORT_INDEX 45
const REQREGITEM c_asControlPanelReqItems[] =
{
{ &CLSID_Printers, IDS_PRINTERS, c_szShell32Dll, -IDI_PRNFLD, PRINTERS_SORT_INDEX, SFGAO_DROPTARGET | SFGAO_FOLDER, NULL},
};
CControlPanelFolder::CControlPanelFolder(IUnknown* punkOuter) :
CAggregatedUnknown (punkOuter),
_pidl (NULL),
_punkReg (NULL)
{
}
CControlPanelFolder::~CControlPanelFolder()
{
if (NULL != _pidl)
{
ILFree(_pidl);
}
SHReleaseInnerInterface(SAFECAST(this, IShellFolder *), &_punkReg);
}
#define REGSTR_POLICIES_RESTRICTCPL REGSTR_PATH_POLICIES TEXT("\\Explorer\\RestrictCpl")
#define REGSTR_POLICIES_DISALLOWCPL REGSTR_PATH_POLICIES TEXT("\\Explorer\\DisallowCpl")
HRESULT CControlPanel_CreateInstance(IUnknown* punkOuter, REFIID riid, void** ppvOut)
{
CControlPanelFolder* pcpf = new CControlPanelFolder(punkOuter);
if (NULL != pcpf)
{
static REGITEMSPOLICY ripControlPanel =
{
REGSTR_POLICIES_RESTRICTCPL,
REST_RESTRICTCPL,
REGSTR_POLICIES_DISALLOWCPL,
REST_DISALLOWCPL
};
REGITEMSINFO riiControlPanel =
{
REGSTR_PATH_EXPLORER TEXT("\\ControlPanel\\NameSpace"),
&ripControlPanel,
TEXT(':'),
SHID_CONTROLPANEL_REGITEM_EX, // note, we don't really have a sig
1,
SFGAO_CANLINK,
ARRAYSIZE(c_asControlPanelReqItems),
c_asControlPanelReqItems,
RIISA_ALPHABETICAL,
NULL,
// we want everything from after IDREGITEM.bOrder to the first 2 cBuf bytes to be filled with 0's
(FIELD_OFFSET(IDCONTROL, cBuf) + 2) - (FIELD_OFFSET(IDREGITEM, bOrder) + 1),
SHID_CONTROLPANEL_REGITEM,
};
// we dont want to return a naked
// control panel folder. this should
// only fail with memory probs.
HRESULT hr = CRegFolder_CreateInstance(&riiControlPanel, (IUnknown*) (IShellFolder2*) pcpf,
IID_IUnknown, (PVOID*) &(pcpf->_punkReg));
if (SUCCEEDED(hr))
{
hr = pcpf->QueryInterface(riid, ppvOut);
}
pcpf->Release();
return hr;
}
*ppvOut = NULL;
return E_OUTOFMEMORY;
}
HRESULT CControlPanelFolder::v_InternalQueryInterface(REFIID riid, void **ppv)
{
static const QITAB qit[] = {
QITABENT(CControlPanelFolder, IShellFolder2), // IID_IShellFolder2
QITABENTMULTI(CControlPanelFolder, IShellFolder, IShellFolder2), // IID_IShellFolder
QITABENT(CControlPanelFolder, IPersistFolder2), // IID_IPersistFolder2
QITABENTMULTI(CControlPanelFolder, IPersistFolder, IPersistFolder2), // IID_IPersistFolder
QITABENTMULTI(CControlPanelFolder, IPersist, IPersistFolder2), // IID_IPersist
{ 0 },
};
if (_punkReg && (IsEqualIID(riid, IID_IShellFolder) || IsEqualIID(riid, IID_IShellFolder2)))
{
return _punkReg->QueryInterface(riid, ppv);
}
else
{
return QISearch(this, qit, riid, ppv);
}
}
LPIDCONTROL CControlPanelFolder::_IsValid(LPCITEMIDLIST pidl)
{
// BUGBUG - this sucks. the original design had no signature
// so we are left just trying to filter out the regitems that might
// somehow get to us. we used to SIL_GetType(pidl) != SHID_CONTROLPANEL_REGITEM)
// but if somehow we had an icon index that had the low byte equal
// to SHID_CONTROLPANEL_REGITEM (0x70) we would invalidate it. DUMB!
// so we will complicate the heuristics a little bit. lets assume that
// all icon indeces will range between 0xFF000000 and 0x00FFFFFF
// (or -16777214 and 16777215, 16 million each way should be plenty).
// of course this could easily get false positives, but there really
// isnt anything else that we can check against.
// we will also check a minimum size.
if (pidl && pidl->mkid.cb > FIELD_OFFSET(IDCONTROL, cBuf))
{
LPIDCONTROL pidc = (LPIDCONTROL)pidl;
int i = pidc->idIcon & 0xFF000000;
if (i == 0 || i == 0xFF000000)
return pidc;
}
return NULL;
}
STDMETHODIMP CControlPanelFolder::ParseDisplayName(HWND hwnd, LPBC pbc, WCHAR* pszName,
ULONG* pchEaten, LPITEMIDLIST* ppidl, ULONG* pdwAttrib)
{
*ppidl = NULL;
return E_NOTIMPL;
}
STDMETHODIMP CControlPanelFolder::GetAttributesOf(UINT cidl, LPCITEMIDLIST* apidl, ULONG* prgfInOut)
{
if ((*prgfInOut & SFGAO_VALIDATE) && cidl)
{
HRESULT hr = E_INVALIDARG;
// BUGBUG - we dont validate more than one in the array
LPIDCONTROL pidc = _IsValid(*apidl);
if (pidc)
{
TCHAR szModule[MAX_PATH];
GetModuleMapped((LPIDCONTROL)*apidl, szModule, ARRAYSIZE(szModule),
NULL, NULL, 0);
if (PathFileExists(szModule))
hr = S_OK;
else
hr = E_FAIL;
}
return hr;
}
*prgfInOut &= SFGAO_CANLINK;
return NOERROR;
}
STDMETHODIMP CControlPanelFolder::GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST* apidl,
REFIID riid, UINT *pres, void **ppv)
{
HRESULT hr = E_INVALIDARG;
LPIDCONTROL pidc = cidl && apidl ? _IsValid(apidl[0]) : NULL;
*ppv = NULL;
if (pidc && (IsEqualIID(riid, IID_IExtractIconA) || IsEqualIID(riid, IID_IExtractIconW)))
{
TCHAR achParams[MAX_PATH+1+32+1+MAX_CCH_CPLNAME]; // See wsprintf below
TCHAR szModule[MAX_PATH], szName[MAX_CCH_CPLNAME];
UINT idIcon;
// Map the icon ID for upgraded win95 shortcuts to CPLs
GetModuleMapped(pidc, szModule, ARRAYSIZE(szModule), &idIcon, szName, ARRAYSIZE(szName));
// Use the applet name in the pid if we didn't override the name in GetModuleMapped
if (*szName == 0)
GetDisplayName(pidc, szName, ARRAYSIZE(szName));
wsprintf(achParams, TEXT("%s,%d,%s"), szModule, idIcon, szName);
hr = ControlExtractIcon_CreateInstance(achParams, riid, ppv);
}
else if (pidc && IsEqualIID(riid, IID_IContextMenu))
{
hr = CDefFolderMenu_Create(_pidl, hwnd, cidl, apidl,
(IShellFolder*) this, DFMCallBack, NULL, NULL, (IContextMenu**) ppv);
}
else if (pidc && IsEqualIID(riid, IID_IDataObject))
{
hr = CIDLData_CreateFromIDArray(_pidl, cidl, apidl, (IDataObject**) ppv);
}
else if (pidc && IsEqualIID(riid, IID_IQueryInfo))
{
TCHAR szTemp[MAX_CCH_CPLINFO];
_GetDescription(pidc, szTemp, ARRAYSIZE(szTemp));
hr = CreateInfoTipFromText(szTemp, riid, ppv);
}
return hr;
}
STDMETHODIMP CControlPanelFolder::GetDefaultSearchGUID(GUID *pGuid)
{
*pGuid = SRCID_SFileSearch;
return S_OK;
}
STDMETHODIMP CControlPanelFolder::EnumSearches(LPENUMEXTRASEARCH *ppenum)
{
*ppenum = NULL;
return E_NOTIMPL;
}
STDMETHODIMP CControlPanelFolder::EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList** ppenumUnknown)
{
CControlPanelEnum* pesf;
*ppenumUnknown = NULL;
if (!(grfFlags & SHCONTF_NONFOLDERS))
return S_FALSE;
pesf = new CControlPanelEnum(grfFlags);
if (NULL != pesf)
{
// get list of module names
if (SUCCEEDED(pesf->Init()))
{
*ppenumUnknown = (IEnumIDList*) pesf;
return NOERROR;
}
pesf->Release();
}
return E_OUTOFMEMORY;
}
STDMETHODIMP CControlPanelFolder::BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void** ppv)
{
*ppv = NULL;
return E_NOTIMPL;
}
STDMETHODIMP CControlPanelFolder::CompareIDs(LPARAM iCol, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
{
LPIDCONTROL pidc1 = _IsValid(pidl1);
LPIDCONTROL pidc2 = _IsValid(pidl2);
if (pidc1 && pidc2)
{
TCHAR szName1[max(MAX_CCH_CPLNAME, MAX_CCH_CPLINFO)];
TCHAR szName2[max(MAX_CCH_CPLNAME, MAX_CCH_CPLINFO)];
int iCmp;
switch (iCol)
{
case ICOL_COMMENT:
_GetDescription(pidc1, szName1, ARRAYSIZE(szName1));
_GetDescription(pidc2, szName2, ARRAYSIZE(szName2));
// They're both ANSI, so we can compare directly
iCmp = lstrcmp(szName1, szName2);
if (iCmp != 0)
return ResultFromShort(iCmp);
// Fall through if the help field compares the same...
case ICOL_NAME:
default:
GetDisplayName(pidc1, szName1, ARRAYSIZE(szName1));
GetDisplayName(pidc2, szName2, ARRAYSIZE(szName2));
return ResultFromShort(lstrcmp(szName1, szName2));
}
}
return E_INVALIDARG;
}
// background (no items) context menu callback
HRESULT CALLBACK CControls_DFMCallBackBG(IShellFolder *psf, HWND hwnd,
IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
HRESULT hres = NOERROR;
switch(uMsg)
{
case DFM_MERGECONTEXTMENU:
CDefFolderMenu_MergeMenu(HINST_THISDLL, 0, POPUP_CONTROLS_POPUPMERGE, (LPQCMINFO)lParam);
break;
case DFM_GETHELPTEXT:
LoadStringA(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPSTR)lParam, HIWORD(wParam));;
break;
case DFM_GETHELPTEXTW:
LoadStringW(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPWSTR)lParam, HIWORD(wParam));;
break;
case DFM_INVOKECOMMAND:
switch (wParam)
{
case FSIDM_SORTBYNAME:
ShellFolderView_ReArrange(hwnd, ICOL_NAME);
break;
case FSIDM_SORTBYCOMMENT:
ShellFolderView_ReArrange(hwnd, ICOL_COMMENT);
break;
default:
// This is one of view menu items, use the default code.
hres = S_FALSE;
break;
}
break;
default:
hres = E_NOTIMPL;
break;
}
return hres;
}
STDMETHODIMP CControlPanelFolder::CreateViewObject(HWND hwnd, REFIID riid, void** ppv)
{
if (IsEqualIID(riid, IID_IShellView))
{
SFV_CREATE sSFV;
HRESULT hres;
sSFV.cbSize = sizeof(sSFV);
sSFV.psvOuter = NULL;
sSFV.psfvcb = Controls_CreateSFVCB(_punkReg, _pidl);
QueryInterface(IID_IShellFolder, (void**) &sSFV.pshf); // in case we are agregated
hres = SHCreateShellFolderView(&sSFV, (IShellView**) ppv);
if (sSFV.pshf)
sSFV.pshf->Release();
if (sSFV.psfvcb)
sSFV.psfvcb->Release();
return hres;
}
else if (IsEqualIID(riid, IID_IContextMenu))
{
return CDefFolderMenu_Create(NULL, hwnd, 0, NULL,
(IShellFolder*) this, CControls_DFMCallBackBG, NULL, NULL, (IContextMenu**) ppv);
}
*ppv = NULL;
return E_NOINTERFACE;
}
STDMETHODIMP CControlPanelFolder::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD dwFlags, STRRET* pstrret)
{
LPIDCONTROL pidc = _IsValid(pidl);
TCHAR szName[max(MAX_PATH, MAX_CCH_CPLNAME)];
if (pidc)
{
if ((dwFlags & (SHGDN_FORPARSING | SHGDN_INFOLDER | SHGDN_FORADDRESSBAR)) == ((SHGDN_FORPARSING | SHGDN_INFOLDER)))
{
GetModule(pidc, szName, ARRAYSIZE(szName));
}
else
{
GetDisplayName(pidc, szName, ARRAYSIZE(szName));
}
return StringToStrRet(szName, pstrret);
}
return E_INVALIDARG;
}
STDMETHODIMP CControlPanelFolder::BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void** ppv)
{
*ppv = NULL;
return E_NOTIMPL;
}
STDMETHODIMP CControlPanelFolder::SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR lpszName,
DWORD dwReserved, LPITEMIDLIST* ppidlOut)
{
return E_FAIL;
}
STDMETHODIMP CControlPanelFolder::GetDefaultColumn(DWORD dwRes, ULONG* pSort, ULONG* pDisplay)
{
return E_NOTIMPL;
}
STDMETHODIMP CControlPanelFolder::GetDefaultColumnState(UINT iColumn, DWORD* pdwState)
{
return E_NOTIMPL;
}
STDMETHODIMP CControlPanelFolder::GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID* pscid, VARIANT* pv)
{
return E_NOTIMPL;
}
STDMETHODIMP CControlPanelFolder::GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS* psdret)
{
HRESULT hr = E_INVALIDARG;
if (pidl == NULL)
{
if (iColumn < ARRAYSIZE(c_cpl_cols))
{
psdret->fmt = c_cpl_cols[iColumn].iFmt;
psdret->cxChar = c_cpl_cols[iColumn].cchCol;
hr = ResToStrRet(c_cpl_cols[iColumn].ids, &psdret->str);
}
}
else
{
LPIDCONTROL pidc = _IsValid(pidl);
if (pidc)
{
TCHAR szTemp[max(max(MAX_PATH, MAX_CCH_CPLNAME), MAX_CCH_CPLINFO)];
psdret->str.uType = STRRET_CSTR;
psdret->str.cStr[0] = 0;
switch (iColumn)
{
case ICOL_NAME:
GetDisplayName(pidc, szTemp, ARRAYSIZE(szTemp));
break;
case ICOL_COMMENT:
_GetDescription(pidc, szTemp, ARRAYSIZE(szTemp));
break;
default:
szTemp[0] = 0;
break;
}
hr = StringToStrRet(szTemp, &psdret->str);
}
}
return hr;
}
STDMETHODIMP CControlPanelFolder::MapColumnToSCID(UINT iColumn, SHCOLUMNID* pscid)
{
return MapColumnToSCIDImpl(c_cpl_cols, ARRAYSIZE(c_cpl_cols), iColumn, pscid);
}
STDMETHODIMP CControlPanelFolder::GetClassID(CLSID* pCLSID)
{
*pCLSID = CLSID_ControlPanel;
return S_OK;
}
STDMETHODIMP CControlPanelFolder::Initialize(LPCITEMIDLIST pidl)
{
if (NULL != _pidl)
{
ILFree(_pidl);
_pidl = NULL;
}
return SHILClone(pidl, &_pidl);
}
STDMETHODIMP CControlPanelFolder::GetCurFolder(LPITEMIDLIST* ppidl)
{
return GetCurFolderImpl(_pidl, ppidl);
}
// list of item context menu callback
HRESULT CALLBACK CControlPanelFolder::DFMCallBack(IShellFolder *psf, HWND hwndView,
IDataObject *pdtobj, UINT uMsg,
WPARAM wParam, LPARAM lParam)
{
HRESULT hres = E_NOTIMPL;
if (pdtobj)
{
STGMEDIUM medium;
LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
if (pida)
{
hres = NOERROR;
switch(uMsg)
{
case DFM_MERGECONTEXTMENU:
{
LPQCMINFO pqcm = (LPQCMINFO)lParam;
int idCmdFirst = pqcm->idCmdFirst;
if (g_bRunOnNT5 && GetAsyncKeyState(VK_SHIFT) < 0)
{
// If the user is holding down shift, on NT5 we load the menu with both "Open" and "Run as..."
CDefFolderMenu_MergeMenu(HINST_THISDLL, MENU_GENERIC_CONTROLPANEL_VERBS, 0, pqcm);
}
else
{
// Just load the "Open" menu
CDefFolderMenu_MergeMenu(HINST_THISDLL, MENU_GENERIC_OPEN_VERBS, 0, pqcm);
}
SetMenuDefaultItem(pqcm->hmenu, 0, MF_BYPOSITION);
// Returning S_FALSE indicates no need to get verbs from
// extensions.
hres = S_FALSE;
break;
} // case DFM_MERGECONTEXTMENU
case DFM_GETHELPTEXT:
LoadStringA(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPSTR)lParam, HIWORD(wParam));;
break;
case DFM_GETHELPTEXTW:
LoadStringW(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPWSTR)lParam, HIWORD(wParam));;
break;
case DFM_INVOKECOMMAND:
{
int i;
for (i = pida->cidl - 1; i >= 0; i--)
{
LPIDCONTROL pidc = _IsValid(IDA_GetIDListPtr(pida, i));
if (pidc)
{
switch(wParam)
{
case FSIDM_OPENPRN:
case FSIDM_RUNAS:
{
TCHAR achParam[1 + MAX_PATH + 2 + MAX_CCH_CPLNAME]; // See wnsprintf in GetExecName
GetExecName(pidc, achParam, ARRAYSIZE(achParam));
SHRunControlPanelEx(achParam, hwndView, (wParam == FSIDM_RUNAS));
break;
}
default:
hres = S_FALSE;
} // switch(wParam)
}
else
hres = E_FAIL;
}
break;
} // case DFM_INVOKECOMMAND
default:
hres = E_NOTIMPL;
break;
} // switch (uMsg)
HIDA_ReleaseStgMedium(pida, &medium);
} // if (pida)
} // if (pdtobj)
return hres;
}
int MakeCPLCommandLine(LPCTSTR pszModule, LPCTSTR pszName, LPTSTR pszCommandLine, DWORD cchCommandLine)
{
RIP(pszCommandLine);
RIP(pszModule);
RIP(pszName);
return wnsprintf(pszCommandLine, cchCommandLine, TEXT("\"%s\",%s"), pszModule, pszName);
}
void CControlPanelFolder::GetExecName(LPIDCONTROL pidc, LPTSTR pszParseName, UINT cchParseName)
{
TCHAR szModule[MAX_PATH], szName[MAX_CCH_CPLNAME];
GetModuleMapped(pidc, szModule, ARRAYSIZE(szModule), NULL, szName, ARRAYSIZE(szName));
// If our GetModuleMapped call didn't override the applet name, get it the old fashioned way
if (*szName == 0)
GetDisplayName(pidc, szName, ARRAYSIZE(szName));
MakeCPLCommandLine(szModule, szName, pszParseName, cchParseName);
}
#ifdef WINNT
typedef struct _OLDCPLMAPPING
{
LPCTSTR szOldModule;
UINT idOldIcon;
LPCTSTR szNewModule;
UINT idNewIcon;
LPCTSTR szApplet;
// Put TEXT("") in szApplet to use the applet name stored in the cpl shortcut
} OLDCPLMAPPING, *LPOLDCPLMAPPING;
const OLDCPLMAPPING g_rgOldCPLMapping[] =
{
// Win95 shortcuts that don't work correctly
// Add New Hardware
{TEXT("SYSDM.CPL"), 0xfffffda6, TEXT("HDWWIZ.CPL"), (UINT) -100, TEXT("@0")},
// ODBC 32 bit
{TEXT("ODBCCP32.CPL"), 0xfffffa61, TEXT("ODBCCP32.CPL"), 0xfffffa61, TEXT("@0")},
// Mail
{TEXT("MLCFG32.CPL"), 0xffffff7f, TEXT("MLCFG32.CPL"), 0xffffff7f, TEXT("@0")},
// Modem
{TEXT("MODEM.CPL"), 0xfffffc18, TEXT("TELEPHON.CPL"), (UINT) -100, TEXT("")},
// Multimedia
{TEXT("MMSYS.CPL"), 0xffffff9d, TEXT("MMSYS.CPL"), (UINT) -110, TEXT("")},
// Network
{TEXT("NETCPL.CPL"), 0xffffff9c, TEXT("NCPA.CPL"), 0xfffffc17, TEXT("@0")},
// Password
{TEXT("PASSWORD.CPL"), 0xfffffc18, TEXT("PASSWORD.CPL"), 0xfffffc18, TEXT("@0")},
// Regional Settings
{TEXT("INTL.CPL"), 0xffffff9b, TEXT("INTL.CPL"), (UINT) -200, TEXT("@0")},
// System
{TEXT("SYSDM.CPL"), 0xfffffda8, TEXT("SYSDM.CPL"), (UINT) -6, TEXT("")},
// Users
{TEXT("INETCPL.CPL"), 0xfffffad5, TEXT("INETCPL.CPL"), 0xfffffad5, TEXT("@0")},
// NT4 Shortcuts that don't work
// Multimedia
{TEXT("MMSYS.CPL"), 0xfffff444, TEXT("MMSYS.CPL"), 0xfffff444, TEXT("@0")},
// Network
{TEXT("NCPA.CPL"), 0xfffffc17, TEXT("NCPA.CPL"), 0xfffffc17, TEXT("@0")},
// UPS
{TEXT("UPS.CPL"), 0xffffff9c, TEXT("POWERCFG.CPL"), (UINT) -202, TEXT("@0")},
// Synonyms for hardware management
// Devices
{TEXT("SRVMGR.CPL"), 0xffffff67, TEXT("HDWWIZ.CPL"), (UINT) -100, TEXT("@0")},
// Ports
{TEXT("PORTS.CPL"), 0xfffffffe, TEXT("HDWWIZ.CPL"), (UINT) -100, TEXT("@0")},
// SCSI Adapters
{TEXT("DEVAPPS.CPL"), 0xffffff52, TEXT("HDWWIZ.CPL"), (UINT) -100, TEXT("@0")},
// Tape Devices
{TEXT("DEVAPPS.CPL"), 0xffffff97, TEXT("HDWWIZ.CPL"), (UINT) -100, TEXT("@0")},
};
#endif
HRESULT CControlPanelFolder::GetModuleMapped(LPIDCONTROL pidc, LPTSTR pszModule, UINT cchModule,
UINT* pidNewIcon, LPTSTR pszApplet, UINT cchApplet)
{
HRESULT hr = S_FALSE;
#ifdef WINNT
LPTSTR pszFilename;
UINT cchFilenameBuffer;
#endif
GetModule(pidc, pszModule, cchModule);
#ifdef WINNT
// Compare just the .cpl file name, not the full path: Get this file name from the full path
pszFilename = PathFindFileName(pszModule);
// Calculate the size of the buffer available for the filename
cchFilenameBuffer = cchModule - (UINT)(pszFilename - pszModule);
if (((int) pidc->idIcon <= 0) && (pszFilename))
{
int i;
for (i = 0; i < ARRAYSIZE(g_rgOldCPLMapping); i++)
{
// See if the module names and old icon IDs match those in this
// entry of our mapping
if (((UINT) pidc->idIcon == g_rgOldCPLMapping[i].idOldIcon) &&
(lstrcmpi(pszFilename, g_rgOldCPLMapping[i].szOldModule) == 0))
{
hr = S_OK;
// Set the return values to those of the found item
if (pidNewIcon != NULL)
*pidNewIcon = g_rgOldCPLMapping[i].idNewIcon;
lstrcpyn(pszFilename, g_rgOldCPLMapping[i].szNewModule, cchFilenameBuffer);
if (pszApplet != NULL)
lstrcpyn(pszApplet, g_rgOldCPLMapping[i].szApplet, cchApplet);
break;
}
}
}
#endif
// Return old values if we didn't require a translation
if (hr == S_FALSE)
{
if (pidNewIcon != NULL)
*pidNewIcon = pidc->idIcon;
if (pszApplet != NULL)
*pszApplet = 0; //NULL String
}
#ifdef WINNT
// If the .cpl file can't be found, this may be a Win95 shortcut specifying
// the old system directory - possibly an upgraded system. We try to make
// this work by changing the directory specified to the actual system
// directory. For example c:\windows\system\foo.cpl will become
// c:\winnt\system32\foo.cpl.
// Note: The path substitution is done unconditionally because if we
// can't find the file it doesn't matter where we can't find it...
if ( !PathFileExists( pszModule ) )
{
TCHAR szNew[MAX_PATH];
TCHAR szSystem[MAX_PATH];
GetSystemDirectory(szSystem, ARRAYSIZE(szSystem));
PathCombine(szNew, szSystem, pszFilename);
lstrcpyn(pszModule, szNew, cchModule);
}
#endif
return hr;
}
// Unicode .cpl's will be flagged by having oName = 0, oInfo = 0,
// cBuf[0] = '\0', and cBuf[1] = UNICODE_CPL_SIGNATURE_BYTE
#define UNICODE_CPL_SIGNATURE_BYTE (BYTE)0x6a
LPIDCONTROLW CControlPanelFolder::_IsUnicodeCPL(LPIDCONTROL pidc)
{
ASSERT(_IsValid((LPCITEMIDLIST)pidc));
if ((pidc->oName == 0) && (pidc->oInfo == 0) && (pidc->cBuf[0] == '\0') && (pidc->cBuf[1] == UNICODE_CPL_SIGNATURE_BYTE))
return (LPIDCONTROLW)pidc;
return NULL;
}
// SHualUnicodeToTChar is like SHUnicodeToTChar except that it accepts
// an unaligned input string parameter.
#ifdef UNICODE
#define SHualUnicodeToTChar(src, dst, cch) ualstrcpyn(dst, src, cch)
#else // No ANSI platforms require alignment
#define SHualUnicodeToTChar SHUnicodeToTChar
#endif
void CControlPanelFolder::GetDisplayName(LPIDCONTROL pidc, LPTSTR pszName, UINT cchName)
{
LPIDCONTROLW pidcW = _IsUnicodeCPL(pidc);
if (pidcW)
SHualUnicodeToTChar(pidcW->cBufW + pidcW->oNameW, pszName, cchName);
else
SHAnsiToTChar(pidc->cBuf + pidc->oName, pszName, cchName);
}
void CControlPanelFolder::GetModule(LPIDCONTROL pidc, LPTSTR pszModule, UINT cchModule)
{
LPIDCONTROLW pidcW = _IsUnicodeCPL(pidc);
if (pidcW)
SHualUnicodeToTChar(pidcW->cBufW, pszModule, cchModule);
else
SHAnsiToTChar(pidc->cBuf, pszModule, cchModule);
}
void CControlPanelFolder::_GetDescription(LPIDCONTROL pidc, LPTSTR pszDesc, UINT cchDesc)
{
LPIDCONTROLW pidcW = _IsUnicodeCPL(pidc);
if (pidcW)
SHualUnicodeToTChar(pidcW->cBufW + pidcW->oInfoW, pszDesc, cchDesc);
else
SHAnsiToTChar(pidc->cBuf + pidc->oInfo, pszDesc, cchDesc);
}
#undef SHualUnicodeToTChar
CControlPanelEnum::CControlPanelEnum(UINT uFlags) :
_cRef (1),
_uFlags (uFlags),
_iModuleCur (0),
_cControlsOfCurrentModule (0),
_iControlCur (0),
_cControlsTotal (0),
_iRegControls (0)
{
ZeroMemory(&_minstCur, sizeof(_minstCur));
ZeroMemory(&_cplData, sizeof(_cplData));
}
CControlPanelEnum::~CControlPanelEnum()
{
CPLD_Destroy(&_cplData);
}
HRESULT CControlPanelEnum::Init()
{
HRESULT hr;
if (CPLD_GetModules(&_cplData))
{
CPLD_GetRegModules(&_cplData);
hr = S_OK;
}
else
{
hr = E_FAIL;
}
return hr;
}
STDMETHODIMP CControlPanelEnum::QueryInterface(REFIID riid, void **ppv)
{
static const QITAB qit[] = {
QITABENT(CControlPanelEnum, IEnumIDList),
{ 0 }
};
return QISearch(this, qit, riid, ppv);
}
STDMETHODIMP_(ULONG) CControlPanelEnum::AddRef()
{
return InterlockedIncrement(&_cRef);
}
STDMETHODIMP_(ULONG) CControlPanelEnum::Release()
{
if (InterlockedDecrement(&_cRef))
return _cRef;
delete this;
return 0;
}
void CPL_FillIDC(LPIDCONTROLW pidc, LPTSTR pszModule, int idIcon, LPTSTR pszName, LPTSTR pszInfo)
{
#ifdef UNICODE
CHAR szModuleA[MAX_PATH];
CHAR szNameA[MAX_CCH_CPLNAME];
CHAR szInfoA[MAX_CCH_CPLINFO];
BOOL fUnicode = FALSE;
LPSTR pTmpA;
#endif
UNALIGNED TCHAR* pTmp;
ASSERT(lstrlen(pszModule) < MAX_PATH);
ASSERT(lstrlen(pszName) < MAX_CCH_CPLNAME);
ASSERT(lstrlen(pszInfo) < MAX_CCH_CPLINFO);
pidc->idIcon = idIcon;
#ifdef UNICODE
// See if any of the three string inputs cannot be represented as ANSI
// First check pszModule
if (!DoesStringRoundTrip(pszModule, szModuleA, ARRAYSIZE(szModuleA)))
{
// Must create a full Unicode IDL. No need to test other inputs.
fUnicode = TRUE;
goto FillInIDL;
}
// Second, check pszName
if (!DoesStringRoundTrip(pszName, szNameA, ARRAYSIZE(szNameA)))
{
// Must create a full Unicode IDL. No need to test other inputs.
fUnicode = TRUE;
goto FillInIDL;
}
// Third, check pszInfo
if (!DoesStringRoundTrip(pszInfo, szInfoA, ARRAYSIZE(szInfoA)))
{
// Must create a full Unicode IDL
fUnicode = TRUE;
}
FillInIDL:
if (fUnicode)
{
pidc->oName = 0;
pidc->oInfo = 0;
pidc->cBuf[0] = '\0';
pidc->cBuf[1] = UNICODE_CPL_SIGNATURE_BYTE;
pidc->dwFlags = 0;
ualstrcpy(pidc->cBufW, pszModule);
pidc->oNameW = (USHORT)(ualstrlen(pidc->cBufW) + 1);
pTmp = pidc->cBufW + pidc->oNameW;
ualstrcpy(pTmp, pszName);
pidc->oInfoW = (USHORT)(pidc->oNameW + ualstrlen(pTmp) + 1);
pTmp = pidc->cBufW + pidc->oInfoW;
ualstrcpy(pTmp, pszInfo);
pidc->cb = (USHORT)(FIELD_OFFSET(IDCONTROLW, cBufW) + (pidc->oInfoW
+ ualstrlen(pTmp)
+ 1) * SIZEOF(WCHAR) );
}
else
{
// We can make an ANSI IDCONTROL
lstrcpyA(pidc->cBuf, szModuleA);
pidc->oName = (USHORT)(lstrlenA(pidc->cBuf) + 1);
pTmpA = pidc->cBuf + pidc->oName;
lstrcpyA(pTmpA, szNameA);
pidc->oInfo = (USHORT)(pidc->oName + lstrlenA(pTmpA) + 1);
pTmpA = pidc->cBuf + pidc->oInfo;
lstrcpyA(pTmpA, szInfoA);
pidc->cb = (USHORT)(FIELD_OFFSET(IDCONTROL, cBuf) + (pidc->oInfo
+ lstrlenA(pTmpA)
+ 1) * SIZEOF(CHAR) );
}
#else
lstrcpy(pidc->cBuf, pszModule);
pidc->oName = (USHORT) (lstrlen(pidc->cBuf) + 1);
pTmp = pidc->cBuf + pidc->oName;
lstrcpy(pTmp, pszName);
pidc->oInfo = (USHORT) (pidc->oName + lstrlen(pTmp) + 1);
pTmp = pidc->cBuf + pidc->oInfo;
lstrcpy(pTmp, pszInfo);
pidc->cb = (USHORT)(FIELD_OFFSET(IDCONTROL, cBuf) + (pidc->oInfo
+ lstrlen(pTmp)
+ 1) * SIZEOF(CHAR) );
#endif // UNICODE / !UNICODE
*(UNALIGNED USHORT *)((LPBYTE)(pidc) + pidc->cb) = 0; // NULL terminate
}
static BOOL DoesCplPolicyAllow(LPCTSTR pszName, LPCTSTR pszName2)
{
BOOL b;
b = TRUE;
if (SHRestricted(REST_RESTRICTCPL) &&
!IsNameListedUnderKey(pszName, REGSTR_POLICIES_RESTRICTCPL) &&
!IsNameListedUnderKey(pszName2, REGSTR_POLICIES_RESTRICTCPL)
)
{
b = FALSE;
}
if (b && SHRestricted(REST_DISALLOWCPL) && (
IsNameListedUnderKey(pszName, REGSTR_POLICIES_DISALLOWCPL) ||
IsNameListedUnderKey(pszName2, REGSTR_POLICIES_DISALLOWCPL)
))
{
b = FALSE;
}
return b;
}
STDMETHODIMP CControlPanelEnum::Next(ULONG celt, LPITEMIDLIST* ppidlOut, ULONG* pceltFetched)
{
IDCONTROLW idc;
HRESULT hres;
if (pceltFetched)
*pceltFetched = 0;
if (!(_uFlags & SHCONTF_NONFOLDERS))
return S_FALSE;
// Loop through lpData->pRegCPLs and use what cached information we can.
while (_iRegControls < _cplData.cRegCPLs)
{
PRegCPLInfo pRegCPL = (PRegCPLInfo) DPA_GetPtr(_cplData.hRegCPLs, _iRegControls);
PMODULEINFO pmi;
int i;
TCHAR szFilePath[MAX_PATH];
LPTSTR pszFileName;
#ifdef WINNT
// Debugging code to catch unaligned structs [stevecat]
// Of course Win95 did NOT dword-align the registry format, so
// we always hit this assert on Win95 machines. But it's okay
// there since we don't have to worry about alignment. [mikesh]
ASSERT(((INT_PTR)pRegCPL & 3)==0);
#endif
lstrcpyn(szFilePath, REGCPL_FILENAME(pRegCPL), MAX_PATH);
pszFileName = PathFindFileName(szFilePath);
// find this module in the hamiModule list
for (i = 0 ; i < _cplData.cModules ; i++)
{
pmi = (PMODULEINFO) DSA_GetItemPtr(_cplData.hamiModule, i);
if (!lstrcmpi(pszFileName, pmi->pszModuleName))
break;
}
if (i < _cplData.cModules)
{
LPCTSTR pszDisplayName = REGCPL_CPLNAME(pRegCPL);
// If this cpl is not supposed to be displayed let's bail
if (!DoesCplPolicyAllow(pszDisplayName, pszFileName))
{
_iRegControls++;
// we have to set this bit, so that the cpl doesn't get reregistered
pmi->flags |= MI_REG_ENUM;
continue;
}
// Get the module's creation time & size
if (!(pmi->flags & MI_FIND_FILE))
{
WIN32_FIND_DATA findData;
HANDLE hFindFile = FindFirstFile(pmi->pszModule, &findData);
if (hFindFile != INVALID_HANDLE_VALUE)
{
pmi->flags |= MI_FIND_FILE;
pmi->ftCreationTime = findData.ftCreationTime;
pmi->nFileSizeHigh = findData.nFileSizeHigh;
pmi->nFileSizeLow = findData.nFileSizeLow;
//DebugMsg(DM_TRACE,"sh CPLS: got timestamps for %s", REGCPL_FILENAME(pRegCPL));
FindClose(hFindFile);
}
else
{
// this module no longer exists... Blow it away.
DebugMsg(DM_TRACE,TEXT("sh CPLS: very stange, couldn't get timestamps for %s"), REGCPL_FILENAME(pRegCPL));
goto RemoveRegCPL;
}
}
if (pmi->ftCreationTime.dwLowDateTime != pRegCPL->ftCreationTime.dwLowDateTime ||
pmi->ftCreationTime.dwHighDateTime != pRegCPL->ftCreationTime.dwHighDateTime ||
pmi->nFileSizeHigh != pRegCPL->nFileSizeHigh ||
pmi->nFileSizeLow != pRegCPL->nFileSizeLow)
{
// this doesn't match -- remove it from pRegCPLs; it will
// get enumerated below.
DebugMsg(DM_TRACE,TEXT("sh CPLS: timestamps don't match for %s"), REGCPL_FILENAME(pRegCPL));
goto RemoveRegCPL;
}
// we have a match: mark this module so we skip it below
// and enumerate this cpl now
pmi->flags |= MI_REG_ENUM;
CPL_FillIDC(&idc, pmi->pszModule, EIRESID(pRegCPL->idIcon), REGCPL_CPLNAME(pRegCPL), REGCPL_CPLINFO(pRegCPL));
_iRegControls++;
goto return_item;
}
else
{
// hmm... this cpl's module must have been nuked off the disk
DebugMsg(DM_TRACE,TEXT("sh CPLS: %s not in module list!"), REGCPL_FILENAME(pRegCPL));
}
RemoveRegCPL:
// Nuke this cpl entry from the registry
if (!(pRegCPL->flags & REGCPL_FROMREG))
LocalFree(pRegCPL);
DPA_DeletePtr(_cplData.hRegCPLs, _iRegControls);
_cplData.cRegCPLs--;
_cplData.fRegCPLChanged = TRUE;
}
// Have we enumerated all the cpls in this module?
LPCPLMODULE pcplm;
LPCPLITEM pcpli;
do
{
while (_iControlCur >= _cControlsOfCurrentModule || // no more
_cControlsOfCurrentModule < 0) // error getting modules
{
PMODULEINFO pmi;
// Have we enumerated all the modules?
if (_iModuleCur >= _cplData.cModules)
{
CPLD_FlushRegModules(&_cplData); // flush changes for next guy
return S_FALSE;
}
// Was this module enumerated from the registry?
pmi = (PMODULEINFO) DSA_GetItemPtr(_cplData.hamiModule, _iModuleCur);
if (pmi->flags & MI_REG_ENUM)
{
// Yes. Don't do anything.
//DebugMsg(DM_TRACE,"sh CPLS: already enumerated module %s", pmi->pszModule);
}
else
{
// No. Load and init the module, set up counters.
//DebugMsg(DM_TRACE,"sh CPLS: loading module %s", pmi->pszModule);
pmi->flags |= MI_CPL_LOADED;
_cControlsOfCurrentModule = CPLD_InitModule(&_cplData, _iModuleCur, &_minstCur);
_iControlCur = 0;
}
// Point to next module
++_iModuleCur;
}
// We're enumerating the next control in this module
// Add the control to the registry
CPLD_AddControlToReg(&_cplData, &_minstCur, _iControlCur);
// Get the control's pidl name
pcplm = FindCPLModule(&_minstCur);
pcpli = (LPCPLITEM) DSA_GetItemPtr(pcplm->hacpli, _iControlCur);
++_iControlCur;
} while (!DoesCplPolicyAllow(pcpli->pszName, pcplm->szModule));
CPL_FillIDC(&idc, pcplm->szModule, EIRESID(pcpli->idIcon), pcpli->pszName, pcpli->pszInfo);
return_item:
hres = SHILClone((LPCITEMIDLIST)&idc, ppidlOut);
if (SUCCEEDED(hres))
{
++_cControlsTotal;
if (pceltFetched)
*pceltFetched = 1;
}
return hres;
}
STDMETHODIMP CControlPanelEnum::Reset()
{
_iModuleCur = 0;
_cControlsOfCurrentModule = 0;
_iControlCur = 0;
_cControlsTotal = 0;
_iRegControls = 0;
return NOERROR;
}