1474 lines
43 KiB
C++
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;
|
|
}
|
|
|