mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1755 lines
48 KiB
1755 lines
48 KiB
//---------------------------------------------------------------------------
|
|
//
|
|
// Copyright (c) Microsoft Corporation 1991-1993
|
|
//
|
|
// File: drivesx.c
|
|
//
|
|
// History:
|
|
// 05-19-94 GeorgeP Created.
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
#include "shellprv.h"
|
|
#pragma hdrstop
|
|
#include "ids.h"
|
|
|
|
#pragma warning(disable: 4200) // Zero-sized array in struct
|
|
|
|
#pragma pack(1)
|
|
typedef struct _IDREGITEM
|
|
{
|
|
WORD cb;
|
|
BYTE bFlags;
|
|
BYTE bReserved; // This is to get DWORD alignment
|
|
CLSID clsid;
|
|
} IDREGITEM, *LPIDREGITEM;
|
|
typedef const IDREGITEM *LPCIDREGITEM;
|
|
|
|
typedef struct _IDLREGITEM
|
|
{
|
|
IDREGITEM idri;
|
|
USHORT cbNext;
|
|
} IDLREGITEM;
|
|
#pragma pack()
|
|
|
|
typedef struct
|
|
{
|
|
IShellFolder sf;
|
|
UINT cRef;
|
|
|
|
REGITEMSINFO rii;
|
|
LPTSTR pszMachine; // NULL if local
|
|
|
|
REQREGITEM sReqItems[];
|
|
} CRegItemsSF;
|
|
|
|
#pragma data_seg(".text", "CODE")
|
|
// HACKHACK: This has incestuous knowledge about the bFlags field
|
|
const IDLREGITEM c_idlNet =
|
|
{
|
|
{SIZEOF(IDREGITEM), SHID_ROOT_REGITEM, 0,
|
|
{ 0x208D2C60, 0x3AEA, 0x1069, 0xA2,0xD7,0x08,0x00,0x2B,0x30,0x30,0x9D, },},
|
|
0,
|
|
} ;
|
|
|
|
const IDLREGITEM c_idlDrives =
|
|
{
|
|
{SIZEOF(IDREGITEM), SHID_ROOT_REGITEM, 0,
|
|
{ 0x20D04FE0, 0x3AEA, 0x1069, 0xA2,0xD8,0x08,0x00,0x2B,0x30,0x30,0x9D, },},
|
|
0,
|
|
} ;
|
|
#pragma data_seg()
|
|
|
|
const TCHAR c_szRegValDeleteMessage[] = REGSTR_VAL_REGITEMDELETEMESSAGE;
|
|
const TCHAR c_szCrLfLf[] = TEXT("\r\n\n");
|
|
|
|
#define _RegItems_IsRegFromInfo(_lpInfo, _pidri) ((_lpInfo)->bFlags==(_pidri)->bFlags)
|
|
#define _RegItems_GetAttributesFromCLSID(pclsid) (SHGetAttributesFromCLSID(pclsid, SFGAO_CANMOVE | SFGAO_CANDELETE))
|
|
|
|
STDMETHODIMP CRegItems_GetAttributesOf(IShellFolder *psf, UINT cidl, LPCITEMIDLIST * apidl, ULONG * prgfInOut);
|
|
HRESULT RegItems_GetNameRemote(LPTSTR pszMachine, LPCREGITEMSINFO lpInfo, LPCITEMIDLIST pidl, LPSTRRET pStrRet);
|
|
|
|
BOOL _RegItems_IsReg(IShellFolder *psf, LPCITEMIDLIST pidl)
|
|
{
|
|
CRegItemsSF * this = IToClass(CRegItemsSF, sf, psf);
|
|
|
|
if ((pidl == NULL) || ILIsEmpty(pidl))
|
|
return FALSE;
|
|
|
|
return _RegItems_IsRegFromInfo(&this->rii, (LPCIDREGITEM)pidl);
|
|
}
|
|
|
|
|
|
void _RegItems_FillID(LPCREGITEMSINFO lpInfo, LPSHITEMID pid, const CLSID *pclsid)
|
|
{
|
|
LPIDREGITEM pidri = (LPIDREGITEM)pid;
|
|
|
|
pidri->cb = SIZEOF(IDREGITEM);
|
|
pidri->bFlags = lpInfo->bFlags;
|
|
pidri->bReserved = 0;
|
|
pidri->clsid = *pclsid;
|
|
}
|
|
|
|
|
|
BOOL _RegItems_NReqItem(LPCREGITEMSINFO lpInfo, const CLSID *pclsid)
|
|
{
|
|
int iReqItems = lpInfo->iReqItems - 1;
|
|
|
|
for ( ; iReqItems>=0; --iReqItems)
|
|
{
|
|
if (IsEqualGUID(pclsid, lpInfo->pReqItems[iReqItems].pclsid))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return(iReqItems);
|
|
}
|
|
|
|
|
|
BOOL _RegItems_IsSubObject(LPCREGITEMSINFO lpInfo, const CLSID *pclsid)
|
|
{
|
|
TCHAR szClass[GUIDSTR_MAX];
|
|
LONG lSize = 0;
|
|
|
|
if (_RegItems_NReqItem(lpInfo, pclsid) >= 0)
|
|
{
|
|
return(TRUE);
|
|
}
|
|
|
|
StringFromGUID2A(pclsid, szClass, ARRAYSIZE(szClass));
|
|
|
|
// Acutually, I should probably do a RegOpenKey/RegCloseKey, but this
|
|
// is a faster way to see if a key exists
|
|
return RegQueryValue(lpInfo->hkRegItems, szClass, NULL, &lSize) == ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
HRESULT _RegItems_BindToObject(LPTSTR pszMachine, LPCREGITEMSINFO lpInfo,
|
|
LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, LPVOID * ppvOut, BOOL bOneLevel)
|
|
{
|
|
LPIDREGITEM pidri;
|
|
IPersistFolder * ppf;
|
|
LPCITEMIDLIST pidlNext, pidlHere, pidlAbs;
|
|
HRESULT hres;
|
|
|
|
if (!_RegItems_IsRegFromInfo(lpInfo, (LPCIDREGITEM)pidl))
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (!_RegItems_IsSubObject(lpInfo, &((LPCIDREGITEM)pidl)->clsid))
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
pidlNext = _ILNext(pidl);
|
|
if (ILIsEmpty(pidlNext))
|
|
{
|
|
pidlHere = pidl;
|
|
}
|
|
else
|
|
{
|
|
pidlHere = ILClone(pidl);
|
|
if (!pidlHere)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
_ILNext(pidlHere)->mkid.cb = 0;
|
|
}
|
|
|
|
pidri = (LPIDREGITEM)pidlHere;
|
|
|
|
pidlAbs = ILCombine(lpInfo->pidlThis, pidlHere);
|
|
if (pidlAbs)
|
|
{
|
|
IUnknown* punk;
|
|
|
|
hres = SHCoCreateInstance(NULL, &pidri->clsid, NULL, &IID_IUnknown, &punk);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
if (NULL != pszMachine)
|
|
{
|
|
// It's a remote registry item! Use IRemoteComputer
|
|
|
|
IRemoteComputer * premc;
|
|
|
|
hres = punk->lpVtbl->QueryInterface(punk, &IID_IRemoteComputer, &premc);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
hres = premc->lpVtbl->Initialize(premc, pszMachine, FALSE);
|
|
premc->lpVtbl->Release(premc);
|
|
}
|
|
#ifdef UNICODE
|
|
else
|
|
{
|
|
IRemoteComputerA * premca;
|
|
hres = punk->lpVtbl->QueryInterface(punk, &IID_IRemoteComputerA, &premca);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
CHAR szComputerA[MAX_PATH]; // BUGBUG: max_computername_len?
|
|
// Convert the computer name to ansi
|
|
WideCharToMultiByte(CP_ACP, 0, pszMachine, -1,
|
|
szComputerA, ARRAYSIZE(szComputerA),
|
|
NULL, NULL);
|
|
|
|
hres = premca->lpVtbl->Initialize(premca, szComputerA, FALSE);
|
|
premca->lpVtbl->Release(premca);
|
|
}
|
|
}
|
|
#endif // UNICODE
|
|
}
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
IPersistFolder * ppf;
|
|
|
|
hres = punk->lpVtbl->QueryInterface(punk, &IID_IPersistFolder, &ppf);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
hres = ppf->lpVtbl->Initialize(ppf, pidlAbs);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
if (ILIsEmpty(pidlNext) || bOneLevel)
|
|
{
|
|
hres = ppf->lpVtbl->QueryInterface(ppf, riid, ppvOut);
|
|
}
|
|
else
|
|
{
|
|
IShellFolder *psfNext;
|
|
|
|
// Recurse down to the next level
|
|
hres = ppf->lpVtbl->QueryInterface(ppf, &IID_IShellFolder,
|
|
&psfNext);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
hres = psfNext->lpVtbl->BindToObject(psfNext,
|
|
pidlNext, pbc, riid, ppvOut);
|
|
|
|
psfNext->lpVtbl->Release(psfNext);
|
|
}
|
|
}
|
|
}
|
|
ppf->lpVtbl->Release(ppf);
|
|
}
|
|
}
|
|
|
|
punk->lpVtbl->Release(punk);
|
|
}
|
|
|
|
ILFree((LPITEMIDLIST)pidlAbs);
|
|
}
|
|
else
|
|
{
|
|
hres = E_OUTOFMEMORY;
|
|
}
|
|
|
|
if (pidlHere != pidl)
|
|
{
|
|
// We can only get here if we allocated the pidlHere
|
|
ILFree((LPITEMIDLIST)pidlHere);
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
//
|
|
// EnumShellFolder stuff
|
|
//
|
|
typedef struct
|
|
{
|
|
IEnumIDList eu;
|
|
int cRef;
|
|
|
|
int iCur;
|
|
DWORD grfFlags;
|
|
|
|
LPENUMIDLIST peuFolder;
|
|
|
|
CRegItemsSF * psfri;
|
|
HDKA hdka;
|
|
} RegItemsESF, *PRegItemsESF;
|
|
|
|
STDMETHODIMP CRegItems_ESF_QueryInterface(LPENUMIDLIST peunk, REFIID riid, LPVOID * ppvObj)
|
|
{
|
|
PRegItemsESF this = IToClass(RegItemsESF, eu, peunk);
|
|
|
|
if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IEnumIDList))
|
|
{
|
|
this->cRef++;
|
|
|
|
*ppvObj = &this->eu;
|
|
return NOERROR;
|
|
}
|
|
else
|
|
{
|
|
*ppvObj = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CRegItems_ESF_AddRef(LPENUMIDLIST peunk)
|
|
{
|
|
PRegItemsESF this = IToClass(RegItemsESF, eu, peunk);
|
|
|
|
this->cRef++;
|
|
return this->cRef;
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CRegItems_ESF_Release(LPENUMIDLIST peunk)
|
|
{
|
|
PRegItemsESF this = IToClass(RegItemsESF, eu, peunk);
|
|
|
|
this->cRef--;
|
|
if (this->cRef > 0)
|
|
{
|
|
return(this->cRef);
|
|
}
|
|
|
|
this->peuFolder->lpVtbl->Release(this->peuFolder);
|
|
this->psfri->sf.lpVtbl->Release(&this->psfri->sf);
|
|
|
|
if (this->hdka)
|
|
{
|
|
DKA_Destroy(this->hdka);
|
|
}
|
|
|
|
LocalFree((HLOCAL)this);
|
|
|
|
return(0);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CRegItems_ESF_Next(LPENUMIDLIST peunk, ULONG celt,
|
|
LPITEMIDLIST * ppidlOut, ULONG * pceltFetched)
|
|
{
|
|
PRegItemsESF this = IToClass(RegItemsESF, eu, peunk);
|
|
IDLREGITEM idlRegItem;
|
|
|
|
HRESULT hres;
|
|
int iReqItems = this->psfri->rii.iReqItems;
|
|
CLSID clsid;
|
|
const CLSID * pclsid;
|
|
|
|
TryAgain:
|
|
if (this->iCur < iReqItems)
|
|
{
|
|
++this->iCur;
|
|
// feed the back in reverse order as they were given to us...
|
|
pclsid = this->psfri->sReqItems[iReqItems - this->iCur].pclsid;
|
|
|
|
goto FillAndRet;
|
|
}
|
|
|
|
if (this->hdka)
|
|
{
|
|
while ((this->iCur - iReqItems) < DKA_GetItemCount(this->hdka))
|
|
{
|
|
LPCTSTR pszKey = DKA_GetKey(this->hdka, this->iCur-iReqItems);
|
|
++this->iCur;
|
|
|
|
pclsid = &clsid;
|
|
if (FAILED(SHCLSIDFromString(pszKey, &clsid)))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// If this is one of the "required" items, then we have
|
|
// already enumerated it.
|
|
if (_RegItems_NReqItem(&this->psfri->rii, pclsid) >= 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
FillAndRet:
|
|
// We're filling the regitem with class id pclsid. If this is a
|
|
// remote item, first invoke the class to see if it really wants
|
|
// to be enumerated for this remote computer.
|
|
|
|
if (this->psfri->pszMachine)
|
|
{
|
|
IUnknown* punk;
|
|
hres = SHCoCreateInstance(NULL, pclsid, NULL, &IID_IUnknown, &punk);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
// It's a remote registry item! Use IRemoteComputer
|
|
|
|
IRemoteComputer * premc;
|
|
|
|
hres = punk->lpVtbl->QueryInterface(punk, &IID_IRemoteComputer, &premc);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
hres = premc->lpVtbl->Initialize(premc, this->psfri->pszMachine, TRUE);
|
|
premc->lpVtbl->Release(premc);
|
|
}
|
|
#ifdef UNICODE
|
|
else
|
|
{
|
|
IRemoteComputerA * premca;
|
|
hres = punk->lpVtbl->QueryInterface(punk, &IID_IRemoteComputerA, &premca);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
CHAR szComputerA[MAX_PATH]; // BUGBUG: max_computername_len?
|
|
// Convert the computer name to ansi
|
|
WideCharToMultiByte(CP_ACP, 0, this->psfri->pszMachine, -1,
|
|
szComputerA, ARRAYSIZE(szComputerA),
|
|
NULL, NULL);
|
|
|
|
hres = premca->lpVtbl->Initialize(premca, szComputerA, TRUE);
|
|
premca->lpVtbl->Release(premca);
|
|
}
|
|
}
|
|
#endif // UNICODE
|
|
|
|
punk->lpVtbl->Release(punk);
|
|
}
|
|
|
|
if (FAILED(hres))
|
|
{
|
|
goto TryAgain;
|
|
}
|
|
}
|
|
|
|
// Ok, actually enumerate the item
|
|
_RegItems_FillID(&this->psfri->rii, (LPSHITEMID)&idlRegItem.idri, pclsid);
|
|
idlRegItem.cbNext = 0;
|
|
|
|
// The "normal" case would want both types, so we will
|
|
// special case that to not look in the Registry
|
|
if ((this->grfFlags & (SHCONTF_FOLDERS|SHCONTF_NONFOLDERS))
|
|
!= (SHCONTF_FOLDERS|SHCONTF_NONFOLDERS))
|
|
{
|
|
DWORD rgfInOut = SFGAO_FOLDER;
|
|
LPCITEMIDLIST pidl = (LPCITEMIDLIST)&idlRegItem;
|
|
|
|
if (FAILED(CRegItems_GetAttributesOf(
|
|
&this->psfri->sf, 1, &pidl, &rgfInOut)))
|
|
{
|
|
rgfInOut = 0;
|
|
}
|
|
|
|
if (rgfInOut & SFGAO_FOLDER)
|
|
{
|
|
if (!(this->grfFlags & SHCONTF_FOLDERS))
|
|
{
|
|
goto TryAgain;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!(this->grfFlags & SHCONTF_NONFOLDERS))
|
|
{
|
|
goto TryAgain;
|
|
}
|
|
}
|
|
}
|
|
|
|
hres = SHILClone((LPCITEMIDLIST)&idlRegItem, ppidlOut);
|
|
|
|
if (SUCCEEDED(hres) && pceltFetched)
|
|
{
|
|
*pceltFetched = 1;
|
|
}
|
|
return(hres);
|
|
}
|
|
}
|
|
|
|
// Either there is no DKA or we are done with it, so just pass along to
|
|
// to the folder
|
|
return(this->peuFolder->lpVtbl->Next(this->peuFolder, celt, ppidlOut,
|
|
pceltFetched));
|
|
}
|
|
|
|
|
|
STDMETHODIMP CRegItems_QueryInterface(IShellFolder *psf, REFIID riid, LPVOID * ppvObj)
|
|
{
|
|
CRegItemsSF * this = IToClass(CRegItemsSF, sf, psf);
|
|
|
|
if (IsEqualIID(riid, &IID_IUnknown)
|
|
|| IsEqualIID(riid, &IID_IShellFolder))
|
|
{
|
|
*ppvObj = psf;
|
|
psf->lpVtbl->AddRef(psf);
|
|
return NOERROR;
|
|
}
|
|
|
|
return(this->rii.psfInner->lpVtbl->QueryInterface(this->rii.psfInner, riid, ppvObj));
|
|
}
|
|
|
|
|
|
STDMETHODIMP_(ULONG) CRegItems_AddRef(IShellFolder *psf)
|
|
{
|
|
CRegItemsSF * this = IToClass(CRegItemsSF, sf, psf);
|
|
|
|
++this->cRef;
|
|
|
|
return(this->cRef);
|
|
}
|
|
|
|
|
|
extern IShellFolder *g_psfDrives;
|
|
|
|
STDMETHODIMP_(ULONG) CRegItems_Release(IShellFolder *psf)
|
|
{
|
|
CRegItemsSF * this = IToClass(CRegItemsSF, sf, psf);
|
|
|
|
--this->cRef;
|
|
|
|
if (this->cRef)
|
|
{
|
|
return(this->cRef);
|
|
}
|
|
|
|
if (&this->sf == g_psfDrives)
|
|
{
|
|
#ifdef DEBUG
|
|
DebugMsg(DM_TRACE, TEXT("********** WRONG! ********* Call Chee..."));
|
|
Assert(0);
|
|
#endif
|
|
g_psfDrives = NULL;
|
|
}
|
|
|
|
this->rii.psfInner->lpVtbl->Release(this->rii.psfInner);
|
|
|
|
if (this->pszMachine)
|
|
{
|
|
LocalFree(this->pszMachine);
|
|
}
|
|
|
|
LocalFree((HLOCAL)this);
|
|
|
|
return(0);
|
|
}
|
|
|
|
HRESULT ParseNextLevel(IShellFolder *psf, HWND hwndOwner, LPBC pbc,
|
|
LPCITEMIDLIST pidlNext, LPOLESTR pwzRest, LPITEMIDLIST *ppidlOut,
|
|
ULONG * pdwAttributes)
|
|
{
|
|
IShellFolder *psfNext;
|
|
ULONG chEaten;
|
|
LPITEMIDLIST pidlRest;
|
|
HRESULT hres;
|
|
|
|
if (!*pwzRest)
|
|
{
|
|
// pidlNext should be a simple pidl.
|
|
Assert(!ILIsEmpty(pidlNext) && ILIsEmpty(_ILNext(pidlNext)));
|
|
if (pdwAttributes)
|
|
{
|
|
CRegItems_GetAttributesOf(psf, 1, &pidlNext, pdwAttributes);
|
|
}
|
|
return(SHILClone(pidlNext, ppidlOut));
|
|
}
|
|
|
|
Assert(*pwzRest == TEXT('\\'));
|
|
|
|
++pwzRest;
|
|
|
|
hres = psf->lpVtbl->BindToObject(psf, pidlNext, pbc, &IID_IShellFolder,
|
|
&psfNext);
|
|
if (FAILED(hres))
|
|
{
|
|
return(hres);
|
|
}
|
|
|
|
hres = psfNext->lpVtbl->ParseDisplayName(psfNext, hwndOwner, pbc, pwzRest,
|
|
&chEaten, &pidlRest, pdwAttributes);
|
|
psfNext->lpVtbl->Release(psfNext);
|
|
if (FAILED(hres))
|
|
{
|
|
return(hres);
|
|
}
|
|
|
|
hres = SHILCombine(pidlNext, pidlRest, ppidlOut);
|
|
SHFree(pidlRest);
|
|
|
|
return(hres);
|
|
}
|
|
|
|
|
|
// This is a separate function so that the string is not on the stack
|
|
// of the calling function
|
|
HRESULT _RegItems_ParseRegName(CRegItemsSF * this,
|
|
HWND hwndOwner, LPBC pbc, LPOLESTR pwzDisplayName, LPITEMIDLIST * ppidlOut,
|
|
ULONG * pdwAttributes)
|
|
{
|
|
IDLREGITEM idlRegItem;
|
|
TCHAR szDisplayName[GUIDSTR_MAX+10];
|
|
HRESULT hres;
|
|
CLSID clsid;
|
|
LPOLESTR pwzNext;
|
|
|
|
// Note that we add 2 to skip the RegItem identifier characters
|
|
pwzDisplayName += 2;
|
|
for (pwzNext=pwzDisplayName; *pwzNext && *pwzNext!=TEXT('\\'); ++pwzNext)
|
|
{
|
|
// Skip to a '\\'
|
|
}
|
|
|
|
OleStrToStrN(szDisplayName, ARRAYSIZE(szDisplayName),
|
|
pwzDisplayName, pwzNext-pwzDisplayName);
|
|
// Note that szDisplayName is NOT NULL terminated, but SHCLSIDFromString
|
|
// doesn't seem to mind.
|
|
hres = SHCLSIDFromString(szDisplayName, &clsid);
|
|
if (FAILED(hres))
|
|
{
|
|
return(hres);
|
|
}
|
|
|
|
if (!_RegItems_IsSubObject(&this->rii, &clsid))
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
_RegItems_FillID(&this->rii, (LPSHITEMID)&idlRegItem.idri, &clsid);
|
|
idlRegItem.cbNext = 0;
|
|
|
|
return(ParseNextLevel(&this->sf, hwndOwner, pbc, (LPCITEMIDLIST)&idlRegItem, pwzNext,
|
|
ppidlOut, pdwAttributes));
|
|
}
|
|
|
|
|
|
STDMETHODIMP CRegItems_ParseDisplayName(IShellFolder *psf,
|
|
HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName,
|
|
ULONG * pchEaten, LPITEMIDLIST * ppidlOut, ULONG* pdwAttributes)
|
|
{
|
|
CRegItemsSF * this = IToClass(CRegItemsSF, sf, psf);
|
|
|
|
// ::{guid} lets you get the pidl for a reg item
|
|
|
|
if (lpszDisplayName[0] == this->rii.cRegItem &&
|
|
lpszDisplayName[1] == this->rii.cRegItem)
|
|
{
|
|
return _RegItems_ParseRegName(this, hwndOwner,
|
|
pbc, lpszDisplayName, ppidlOut, pdwAttributes);
|
|
}
|
|
|
|
return this->rii.psfInner->lpVtbl->ParseDisplayName(this->rii.psfInner,
|
|
hwndOwner, pbc, lpszDisplayName, pchEaten, ppidlOut, pdwAttributes);
|
|
}
|
|
|
|
extern IEnumIDListVtbl c_RegItemsESFVtbl; // forward
|
|
|
|
STDMETHODIMP CRegItems_EnumObjects(IShellFolder * psf, HWND hwndOwner, DWORD grfFlags, LPENUMIDLIST *ppenumOut)
|
|
{
|
|
CRegItemsSF * this = IToClass(CRegItemsSF, sf, psf);
|
|
HRESULT hres;
|
|
PRegItemsESF pesf = (void*)LocalAlloc(LPTR, SIZEOF(RegItemsESF));
|
|
|
|
if (!pesf)
|
|
{
|
|
*ppenumOut = NULL; // assume error
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
hres = this->rii.psfInner->lpVtbl->EnumObjects(this->rii.psfInner, hwndOwner, grfFlags, &(pesf->peuFolder));
|
|
if (FAILED(hres))
|
|
{
|
|
LocalFree((HLOCAL)pesf);
|
|
return(hres);
|
|
}
|
|
|
|
// I don't really care that much if this fails
|
|
pesf->hdka = DKA_Create(this->rii.hkRegItems, NULL, NULL, NULL, FALSE);
|
|
// Note that hdka could be NULL if the hkey did not exist
|
|
|
|
psf->lpVtbl->AddRef(psf);
|
|
pesf->psfri = this;
|
|
|
|
pesf->eu.lpVtbl = &c_RegItemsESFVtbl;
|
|
pesf->cRef = 1;
|
|
pesf->iCur = 0;
|
|
pesf->grfFlags = grfFlags;
|
|
|
|
*ppenumOut = &pesf->eu;
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
// Vtable
|
|
#ifndef WINNT
|
|
#pragma data_seg(".text", "CODE")
|
|
#endif
|
|
|
|
IEnumIDListVtbl c_RegItemsESFVtbl =
|
|
{
|
|
CRegItems_ESF_QueryInterface,
|
|
CRegItems_ESF_AddRef,
|
|
CRegItems_ESF_Release,
|
|
CRegItems_ESF_Next,
|
|
CDefEnum_Skip,
|
|
CDefEnum_Reset,
|
|
CDefEnum_Clone,
|
|
};
|
|
#ifndef WINNT
|
|
#pragma data_seg()
|
|
#endif
|
|
|
|
|
|
|
|
STDMETHODIMP CRegItems_BindToObject(IShellFolder *psf, LPCITEMIDLIST pidl, LPBC pbc,
|
|
REFIID riid, LPVOID * ppvOut)
|
|
{
|
|
CRegItemsSF * this = IToClass(CRegItemsSF, sf, psf);
|
|
|
|
if (_RegItems_IsReg(psf, pidl))
|
|
{
|
|
return _RegItems_BindToObject(this->pszMachine, &this->rii, pidl, pbc, riid, ppvOut, FALSE);
|
|
}
|
|
else
|
|
{
|
|
return this->rii.psfInner->lpVtbl->BindToObject(this->rii.psfInner,
|
|
pidl, pbc, riid, ppvOut);
|
|
}
|
|
}
|
|
|
|
|
|
STDMETHODIMP CRegItems_CompareIDs(IShellFolder *psf, LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
|
|
{
|
|
CRegItemsSF * this = IToClass(CRegItemsSF, sf, psf);
|
|
LPCIDREGITEM pidri1 = (LPCIDREGITEM)pidl1;
|
|
LPCIDREGITEM pidri2 = (LPCIDREGITEM)pidl2;
|
|
LPCITEMIDLIST pidlNext1, pidlNext2;
|
|
IShellFolder *psfNext;
|
|
int iRes;
|
|
HRESULT hres;
|
|
|
|
// Put all RegItem's first if this->rii.iCmp==1, last if -1
|
|
if (_RegItems_IsReg(psf, pidl1))
|
|
{
|
|
if (_RegItems_IsReg(psf, pidl2))
|
|
{
|
|
// Both are RegItem's
|
|
STRRET StrRet1, StrRet2;
|
|
int nReqItem1, nReqItem2;
|
|
|
|
// All of the required items come first, in reverse
|
|
// order (to make this simpler)
|
|
nReqItem1 = _RegItems_NReqItem(&this->rii, &pidri1->clsid);
|
|
nReqItem2 = _RegItems_NReqItem(&this->rii, &pidri2->clsid);
|
|
|
|
if (nReqItem1==-1 && nReqItem2==-1)
|
|
{
|
|
#ifdef UNICODE
|
|
TCHAR szItemName1[MAX_PATH];
|
|
TCHAR szItemName2[MAX_PATH];
|
|
#endif
|
|
RegItems_GetName(&this->rii, pidl1, &StrRet1);
|
|
RegItems_GetName(&this->rii, pidl2, &StrRet2);
|
|
#ifdef UNICODE
|
|
StrRetToStrN(szItemName1,ARRAYSIZE(szItemName1),&StrRet1,pidl1);
|
|
StrRetToStrN(szItemName2,ARRAYSIZE(szItemName2),&StrRet2,pidl2);
|
|
iRes = lstrcmp(szItemName1,szItemName2);
|
|
#else
|
|
iRes = lstrcmp(StrRet1.cStr, StrRet2.cStr);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
iRes = nReqItem2 - nReqItem1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Only pidl1 is a RegItem.
|
|
iRes = -this->rii.iCmp;
|
|
}
|
|
}
|
|
else if (_RegItems_IsReg(psf, pidl2))
|
|
{
|
|
// Only pidl2 is a RegItem
|
|
iRes = this->rii.iCmp;
|
|
}
|
|
else
|
|
{
|
|
return(this->rii.psfInner->lpVtbl->CompareIDs(this->rii.psfInner,
|
|
lParam, pidl1, pidl2));
|
|
}
|
|
|
|
if (iRes != 0)
|
|
{
|
|
return(ResultFromShort(iRes));
|
|
}
|
|
|
|
// The names are the same; make sure the CLSID's are the same
|
|
iRes = memcmp(&pidri1->clsid, &pidri2->clsid, SIZEOF(CLSID));
|
|
if (iRes != 0)
|
|
{
|
|
return(ResultFromShort(iRes));
|
|
}
|
|
|
|
// If the class ID's really are the same, we'd better check the next
|
|
// level
|
|
pidlNext1 = _ILNext(pidl1);
|
|
pidlNext2 = _ILNext(pidl2);
|
|
if (ILIsEmpty(pidlNext1))
|
|
{
|
|
if (ILIsEmpty(pidlNext2))
|
|
{
|
|
return(ResultFromShort(0));
|
|
}
|
|
return(ResultFromShort(-1));
|
|
}
|
|
else if (ILIsEmpty(pidlNext2))
|
|
{
|
|
return(ResultFromShort(1));
|
|
}
|
|
|
|
hres = _RegItems_BindToObject(this->pszMachine, &this->rii, pidl1, NULL, &IID_IShellFolder,
|
|
&psfNext, TRUE);
|
|
if (FAILED(hres))
|
|
{
|
|
return(hres);
|
|
}
|
|
|
|
hres = psfNext->lpVtbl->CompareIDs(psfNext, lParam, pidlNext1, pidlNext2);
|
|
psfNext->lpVtbl->Release(psfNext);
|
|
|
|
return(hres);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CRegItems_CreateViewObject(IShellFolder *psf, HWND hwndOwner, REFIID riid, LPVOID * ppvOut)
|
|
{
|
|
CRegItemsSF * this = IToClass(CRegItemsSF, sf, psf);
|
|
|
|
return this->rii.psfInner->lpVtbl->CreateViewObject(this->rii.psfInner, hwndOwner, riid, ppvOut);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CRegItems_GetAttributesOf(IShellFolder *psf, UINT cidl, LPCITEMIDLIST * apidl,
|
|
ULONG * prgfInOut)
|
|
{
|
|
CRegItemsSF * this = IToClass(CRegItemsSF, sf, psf);
|
|
UINT rgfThis, rgfOut = *prgfInOut;
|
|
LPCITEMIDLIST *ppidl, *ppidlEnd;
|
|
int i;
|
|
HRESULT hres;
|
|
|
|
if (!cidl)
|
|
{
|
|
// This is a special case for the folder as a whole, so I know
|
|
// nothing about it.
|
|
return(this->rii.psfInner->lpVtbl->GetAttributesOf(
|
|
this->rii.psfInner, cidl, apidl, prgfInOut));
|
|
}
|
|
|
|
// REVIEW: If this is too slow, we can cache this buffer
|
|
ppidl = (void*)LocalAlloc(LPTR, cidl*SIZEOF(LPCITEMIDLIST));
|
|
if (!ppidl)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
ppidlEnd = ppidl + cidl;
|
|
|
|
for (i=cidl-1; i>=0; --i)
|
|
{
|
|
LPIDREGITEM pidri = (LPIDREGITEM)apidl[i];
|
|
int nReqItem;
|
|
|
|
if (_RegItems_IsReg(psf, (LPCITEMIDLIST)pidri))
|
|
{
|
|
--cidl;
|
|
rgfThis = this->rii.rgfRegItems;
|
|
nReqItem = _RegItems_NReqItem(&this->rii, &pidri->clsid);
|
|
if (nReqItem >= 0)
|
|
{
|
|
rgfThis |= this->rii.pReqItems[nReqItem].dwAttributes;
|
|
}
|
|
else
|
|
{
|
|
rgfThis |= _RegItems_GetAttributesFromCLSID(&pidri->clsid);
|
|
}
|
|
rgfOut &= rgfThis;
|
|
}
|
|
else
|
|
{
|
|
--ppidlEnd;
|
|
*ppidlEnd = (LPCITEMIDLIST)pidri;
|
|
}
|
|
}
|
|
|
|
if (cidl)
|
|
{
|
|
rgfThis = rgfOut;
|
|
hres = this->rii.psfInner->lpVtbl->GetAttributesOf(this->rii.psfInner,
|
|
cidl, ppidlEnd, &rgfThis);
|
|
if (FAILED(hres))
|
|
{
|
|
return(hres);
|
|
}
|
|
|
|
rgfOut &= rgfThis;
|
|
}
|
|
|
|
LocalFree((HLOCAL)ppidl);
|
|
|
|
*prgfInOut = rgfOut;
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
//
|
|
// Returns: TRUE, if any of pidl points to a reg item.
|
|
//
|
|
BOOL CRegItem_AnyRegItem(IShellFolder * psf, UINT cidl, LPCITEMIDLIST apidl[])
|
|
{
|
|
UINT i;
|
|
for (i = 0; i < cidl; i++) {
|
|
if (_RegItems_IsReg(psf, apidl[i]))
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
STDMETHODIMP CRegItems_GetUIObjectOf(IShellFolder *psf, HWND hwndOwner, UINT cidl, LPCITEMIDLIST * apidl,
|
|
REFIID riid, UINT * prgfInOut, LPVOID * ppvOut)
|
|
{
|
|
CRegItemsSF * this = IToClass(CRegItemsSF, sf, psf);
|
|
|
|
if ((IsEqualIID(riid, &IID_IExtractIcon)
|
|
#ifdef UNICODE
|
|
|| IsEqualIID(riid, &IID_IExtractIconA)
|
|
#endif
|
|
) && cidl == 1 && apidl && apidl[0] && _RegItems_IsReg(psf, apidl[0]))
|
|
{
|
|
HRESULT hres;
|
|
HKEY hkCLSID;
|
|
LPCTSTR pszIconFile;
|
|
int iDefIcon;
|
|
LPCIDREGITEM pidri = (LPCIDREGITEM)apidl[0];
|
|
int nReqItem = _RegItems_NReqItem(&this->rii, &pidri->clsid);
|
|
|
|
if (nReqItem >= 0)
|
|
{
|
|
pszIconFile = this->rii.pReqItems[nReqItem].pszIconFile;
|
|
iDefIcon = this->rii.pReqItems[nReqItem].iDefIcon;
|
|
}
|
|
else
|
|
{
|
|
pszIconFile = NULL;
|
|
iDefIcon = II_FOLDER;
|
|
}
|
|
|
|
*ppvOut = NULL;
|
|
hres = SHRegGetCLSIDKey(&((LPCIDREGITEM)apidl[0])->clsid, NULL, TRUE, &hkCLSID);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
LPEXTRACTICON pxicon;
|
|
|
|
hres = SHCreateDefExtIconKey(hkCLSID, pszIconFile, iDefIcon, iDefIcon, GIL_PERCLASS, &pxicon);
|
|
|
|
if (hres == NOERROR) // Normal good iextracticon guy
|
|
*ppvOut = pxicon;
|
|
else if (SUCCEEDED(hres)) // Probably S_FALSE guy
|
|
pxicon->lpVtbl->Release(pxicon); // Lose this bad guy
|
|
|
|
RegCloseKey(hkCLSID);
|
|
}
|
|
|
|
if (*ppvOut == NULL)
|
|
{
|
|
RegItems_GetClassKey(apidl[0], &hkCLSID);
|
|
hres = SHCreateDefExtIconKey(hkCLSID, pszIconFile, iDefIcon, iDefIcon, GIL_PERCLASS, (LPEXTRACTICON *)ppvOut);
|
|
RegCloseKey(hkCLSID);
|
|
}
|
|
|
|
#ifdef UNICODE
|
|
if (SUCCEEDED(hres) && IsEqualIID(riid, &IID_IExtractIconA))
|
|
{
|
|
LPEXTRACTICON pxicon = *ppvOut;
|
|
hres = pxicon->lpVtbl->QueryInterface(pxicon,riid,ppvOut);
|
|
pxicon->lpVtbl->Release(pxicon);
|
|
}
|
|
#endif
|
|
return hres;
|
|
}
|
|
else if (CRegItem_AnyRegItem(psf, cidl, apidl))
|
|
{
|
|
if (IsEqualIID(riid, &IID_IDataObject))
|
|
{
|
|
return CIDLData_CreateFromIDArray(this->rii.pidlThis, cidl, apidl, (LPDATAOBJECT *)ppvOut);
|
|
}
|
|
}
|
|
|
|
return this->rii.psfInner->lpVtbl->GetUIObjectOf(this->rii.psfInner, hwndOwner,
|
|
cidl, apidl, riid, prgfInOut, ppvOut);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CRegItems_GetDisplayNameOf(IShellFolder *psf, LPCITEMIDLIST pidl,
|
|
DWORD uFlags, LPSTRRET lpName)
|
|
{
|
|
CRegItemsSF * this = IToClass(CRegItemsSF, sf, psf);
|
|
HRESULT hres;
|
|
|
|
if (_RegItems_IsReg(psf, pidl))
|
|
{
|
|
IShellFolder * psfNext;
|
|
LPCITEMIDLIST pidlNext = _ILNext(pidl);
|
|
|
|
if (ILIsEmpty(pidlNext))
|
|
{
|
|
if (this->pszMachine && (uFlags == SHGDN_NORMAL))
|
|
{
|
|
return RegItems_GetNameRemote(this->pszMachine, &this->rii, pidl, lpName);
|
|
}
|
|
else
|
|
{
|
|
return RegItems_GetName(&this->rii, pidl, lpName);
|
|
}
|
|
}
|
|
|
|
// Yes, it contains more than one ID
|
|
|
|
// REVIEW: Then again, maybe we are just supposed to show the
|
|
// name of the last guy in the list, and if it is a FS guy,
|
|
// then it may show the full path depending on the flags.
|
|
|
|
hres = _RegItems_BindToObject(this->pszMachine, &this->rii, pidl, NULL, &IID_IShellFolder, &psfNext, TRUE);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
hres = psfNext->lpVtbl->GetDisplayNameOf(psfNext, pidlNext, uFlags, lpName);
|
|
// If it returns an offset to the pidlNext, we should
|
|
// change the offset relative to pidl.
|
|
if (SUCCEEDED(hres) && lpName->uType==STRRET_OFFSET)
|
|
{
|
|
lpName->uOffset += (LPBYTE)pidlNext - (LPBYTE)pidl;
|
|
}
|
|
|
|
psfNext->lpVtbl->Release(psfNext);
|
|
}
|
|
|
|
return(hres);
|
|
}
|
|
else
|
|
{
|
|
return this->rii.psfInner->lpVtbl->GetDisplayNameOf(this->rii.psfInner, pidl, uFlags, lpName);
|
|
}
|
|
}
|
|
|
|
|
|
STDMETHODIMP CRegItems_SetNameOf(IShellFolder *psf, HWND hwndOwner,
|
|
LPCITEMIDLIST pidl, LPCOLESTR lpszName, DWORD uFlags, LPITEMIDLIST * ppidlOut)
|
|
{
|
|
CRegItemsSF * this = IToClass(CRegItemsSF, sf, psf);
|
|
|
|
if (_RegItems_IsReg(psf, pidl))
|
|
{
|
|
HRESULT hres = E_INVALIDARG;
|
|
HKEY hkCLSID;
|
|
|
|
if (ppidlOut)
|
|
*ppidlOut = NULL;
|
|
|
|
hres = RegItems_GetClassKey(pidl, &hkCLSID);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
TCHAR szName[MAX_PATH];
|
|
|
|
OleStrToStr(szName, lpszName);
|
|
|
|
if (RegSetValue(hkCLSID, NULL, REG_SZ, szName, lstrlen(szName))
|
|
== ERROR_SUCCESS)
|
|
{
|
|
LPITEMIDLIST pidlAbs;
|
|
|
|
hres = NOERROR;
|
|
|
|
// Generate an UpdateItem notification to let the
|
|
// system know to regenerate the information...
|
|
pidlAbs = ILCombine(this->rii.pidlThis, pidl);
|
|
|
|
if (pidlAbs)
|
|
{
|
|
SHChangeNotify(SHCNE_RENAMEFOLDER, SHCNF_IDLIST, pidlAbs, pidlAbs);
|
|
ILFree(pidlAbs);
|
|
}
|
|
|
|
if (ppidlOut)
|
|
{
|
|
// The caller wanted a pidl returned, so clone it...
|
|
*ppidlOut = ILClone(pidl);
|
|
}
|
|
}
|
|
else
|
|
hres = E_FAIL;
|
|
}
|
|
return hres;
|
|
}
|
|
else
|
|
{
|
|
return this->rii.psfInner->lpVtbl->SetNameOf(this->rii.psfInner, hwndOwner, pidl, lpszName, uFlags, ppidlOut);
|
|
}
|
|
}
|
|
|
|
#ifndef WINNT
|
|
#pragma data_seg(".text", "CODE")
|
|
#endif
|
|
|
|
IShellFolderVtbl c_RegItemsSFVtbl =
|
|
{
|
|
CRegItems_QueryInterface,
|
|
CRegItems_AddRef,
|
|
CRegItems_Release,
|
|
|
|
CRegItems_ParseDisplayName,
|
|
CRegItems_EnumObjects,
|
|
CRegItems_BindToObject,
|
|
CDefShellFolder_BindToStorage,
|
|
CRegItems_CompareIDs,
|
|
CRegItems_CreateViewObject,
|
|
CRegItems_GetAttributesOf,
|
|
CRegItems_GetUIObjectOf,
|
|
CRegItems_GetDisplayNameOf,
|
|
CRegItems_SetNameOf,
|
|
};
|
|
#ifndef WINNT
|
|
#pragma data_seg()
|
|
#endif
|
|
|
|
// WARNING: Note that we copy the pointer to lpInfo->pidlThis.
|
|
//
|
|
HRESULT RegItems_AddToShellFolder(LPCREGITEMSINFO lpInfo, IShellFolder **ppsf)
|
|
{
|
|
int iReqItems = lpInfo->iReqItems;
|
|
CRegItemsSF * this = (void*)LocalAlloc(LPTR, SIZEOF(CRegItemsSF) + iReqItems * SIZEOF(REQREGITEM));
|
|
if (!this)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
lpInfo->psfInner->lpVtbl->AddRef(lpInfo->psfInner);
|
|
|
|
this->sf.lpVtbl = &c_RegItemsSFVtbl;
|
|
this->cRef = 1;
|
|
this->pszMachine = NULL;
|
|
|
|
this->rii = *lpInfo;
|
|
|
|
for (--iReqItems; iReqItems>=0; --iReqItems)
|
|
{
|
|
this->sReqItems[iReqItems] = lpInfo->pReqItems[iReqItems];
|
|
}
|
|
|
|
this->rii.pReqItems = &this->sReqItems[0];
|
|
|
|
*ppsf = &this->sf;
|
|
return NOERROR;
|
|
}
|
|
|
|
// WARNING: Note that we copy the pointer to lpInfo->pidlThis.
|
|
//
|
|
HRESULT RegItems_AddToShellFolderRemote(LPCREGITEMSINFO lpInfo, LPTSTR pszMachine, IShellFolder **ppsf)
|
|
{
|
|
CRegItemsSF * this;
|
|
|
|
HRESULT hres = RegItems_AddToShellFolder(lpInfo, ppsf);
|
|
if (FAILED(hres))
|
|
{
|
|
return hres;
|
|
}
|
|
|
|
this = IToClass(CRegItemsSF, sf, *ppsf);
|
|
|
|
if (NULL != pszMachine)
|
|
{
|
|
this->pszMachine = (LPTSTR)LocalAlloc(LPTR, (1 + lstrlen(pszMachine)) * sizeof(TCHAR));
|
|
if (!this->pszMachine)
|
|
{
|
|
CRegItems_Release(&this->sf);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
lstrcpy(this->pszMachine, pszMachine);
|
|
}
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
HRESULT RegItems_GetName(LPCREGITEMSINFO lpInfo, LPCITEMIDLIST pidl, LPSTRRET pStrRet)
|
|
{
|
|
LPCIDREGITEM pidri = (LPCIDREGITEM)pidl;
|
|
HRESULT hres = E_INVALIDARG;
|
|
HKEY hkCLSID;
|
|
LONG lLenBuff;
|
|
LONG lLen;
|
|
LPTSTR lpNameBuff;
|
|
#ifdef UNICODE
|
|
TCHAR szName[MAX_PATH];
|
|
|
|
pStrRet->uType = STRRET_OLESTR;
|
|
lpNameBuff = szName;
|
|
lLen = lLenBuff = ARRAYSIZE(szName);
|
|
#else
|
|
pStrRet->uType = STRRET_CSTR;
|
|
lpNameBuff = pStrRet->cStr;
|
|
lLen = lLenBuff = ARRAYSIZE(pStrRet->cStr);
|
|
#endif
|
|
*lpNameBuff = TEXT('\0');
|
|
|
|
hres = RegItems_GetClassKey(pidl, &hkCLSID);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
if (RegQueryValue(hkCLSID, NULL, lpNameBuff, &lLen)
|
|
!= ERROR_SUCCESS || *lpNameBuff==TEXT('\0'))
|
|
{
|
|
// Can this ever happen?
|
|
hres = E_FAIL;
|
|
}
|
|
SHRegCloseKey(hkCLSID);
|
|
}
|
|
|
|
//
|
|
// We need to be able to open/display controls and printers
|
|
// even though the registry is broken.
|
|
//
|
|
if (FAILED(hres))
|
|
{
|
|
int nReqItem = _RegItems_NReqItem(lpInfo, &pidri->clsid);
|
|
|
|
if (nReqItem >= 0)
|
|
{
|
|
lLen = lLenBuff;
|
|
LoadString(HINST_THISDLL, lpInfo->pReqItems[nReqItem].uNameID,
|
|
lpNameBuff, lLen);
|
|
hres = NOERROR;
|
|
}
|
|
else
|
|
{
|
|
// REVIEW: We have some Registry problem here, and I
|
|
// don't know what to do about it
|
|
Assert(FALSE);
|
|
}
|
|
}
|
|
|
|
#ifdef UNICODE
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
pStrRet->pOleStr = SHAlloc((lstrlen(lpNameBuff)+1)*SIZEOF(TCHAR));
|
|
if (pStrRet->pOleStr == NULL)
|
|
{
|
|
hres = E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
{
|
|
lstrcpy(pStrRet->pOleStr,lpNameBuff);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// We can't leave a NULL pOleStr in the failure case,
|
|
// so patch it up to be a blank CSTR
|
|
|
|
pStrRet->uType = STRRET_CSTR;
|
|
pStrRet->cStr[0] = TEXT('\0');
|
|
}
|
|
|
|
#endif
|
|
return(hres);
|
|
}
|
|
|
|
|
|
HRESULT RegItems_GetNameRemote(LPTSTR pszMachine, LPCREGITEMSINFO lpInfo, LPCITEMIDLIST pidl, LPSTRRET pStrRet)
|
|
{
|
|
LPCIDREGITEM pidri = (LPCIDREGITEM)pidl;
|
|
HRESULT hres = E_INVALIDARG;
|
|
HKEY hkCLSID;
|
|
TCHAR szName[MAX_PATH];
|
|
LPTSTR pszRet;
|
|
LONG lNameLen = ARRAYSIZE(szName);
|
|
|
|
if (NULL == pszMachine)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
hres = RegItems_GetClassKey(pidl, &hkCLSID);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
if (RegQueryValue(hkCLSID, NULL, szName, &lNameLen)
|
|
!= ERROR_SUCCESS || *szName==TEXT('\0'))
|
|
{
|
|
// Can this ever happen?
|
|
hres = E_FAIL;
|
|
}
|
|
SHRegCloseKey(hkCLSID);
|
|
}
|
|
|
|
//
|
|
// We need to be able to open/display controls and printers
|
|
// even though the registry is broken.
|
|
//
|
|
if (FAILED(hres))
|
|
{
|
|
int nReqItem = _RegItems_NReqItem(lpInfo, &pidri->clsid);
|
|
|
|
if (nReqItem >= 0)
|
|
{
|
|
LoadString(HINST_THISDLL, lpInfo->pReqItems[nReqItem].uNameID,
|
|
szName, ARRAYSIZE(szName));
|
|
hres = NOERROR;
|
|
}
|
|
else
|
|
{
|
|
// REVIEW: We have some Registry problem here, and I
|
|
// don't know what to do about it
|
|
Assert(FALSE);
|
|
}
|
|
}
|
|
|
|
// szName now holds the item name, and pszMachine holds the machine.
|
|
// Note that pszMachine is in UNC form, i.e. "\\machine", so skip the first
|
|
// two characters. Be careful, just in case.
|
|
if (pszMachine[0] == TEXT('\\') && pszMachine[1] == TEXT('\\'))
|
|
{
|
|
pszMachine += 2;
|
|
}
|
|
pszRet = ShellConstructMessageString(HINST_THISDLL, MAKEINTRESOURCE(IDS_DSPTEMPLATE_WITH_ON), pszMachine, szName);
|
|
if (pszRet)
|
|
{
|
|
#ifdef UNICODE
|
|
pStrRet->uType = STRRET_OLESTR;
|
|
pStrRet->pOleStr = pszRet;
|
|
#else
|
|
pStrRet->uType = STRRET_CSTR;
|
|
lstrcpyn(pStrRet->cStr, pszRet, ARRAYSIZE(pStrRet->cStr));
|
|
SHFree(pszRet);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
// Set failure to be a blank CSTR
|
|
|
|
pStrRet->uType = STRRET_CSTR;
|
|
pStrRet->cStr[0] = TEXT('\0');
|
|
}
|
|
|
|
return(hres);
|
|
}
|
|
|
|
|
|
LPITEMIDLIST RegItems_CreateRelID(LPCREGITEMSINFO lpInfo, const CLSID *pclsid)
|
|
{
|
|
IDLREGITEM idlRegItem;
|
|
|
|
_RegItems_FillID(lpInfo, (LPSHITEMID)&idlRegItem.idri, pclsid);
|
|
idlRegItem.cbNext = 0;
|
|
|
|
return ILClone((LPCITEMIDLIST)&idlRegItem);
|
|
}
|
|
|
|
|
|
const CLSID * RegItems_GetClassID(LPCITEMIDLIST pidl)
|
|
{
|
|
LPCIDREGITEM pidri = (LPCIDREGITEM)pidl;
|
|
|
|
return &pidri->clsid;
|
|
}
|
|
|
|
|
|
HRESULT RegItems_BindToObject(LPCREGITEMSINFO lpInfo, LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, LPVOID * ppvOut)
|
|
{
|
|
return _RegItems_BindToObject(NULL, lpInfo, pidl, pbc, riid, ppvOut, FALSE);
|
|
}
|
|
|
|
|
|
IShellFolder * RegItems_GetInnerShellFolder(IShellFolder *psf)
|
|
{
|
|
CRegItemsSF * this = IToClass(CRegItemsSF, sf, psf);
|
|
|
|
if (psf->lpVtbl == &c_RegItemsSFVtbl)
|
|
{
|
|
return(this->rii.psfInner);
|
|
}
|
|
else
|
|
{
|
|
return(psf);
|
|
}
|
|
}
|
|
|
|
|
|
HRESULT RegItems_GetClassKeys(IShellFolder *psf, LPCITEMIDLIST pidl,
|
|
HKEY *phkCLSID, HKEY *phkBase)
|
|
{
|
|
LPCIDREGITEM pidri = (LPCIDREGITEM)pidl;
|
|
TCHAR szThisCLSID[GUIDSTR_MAX];
|
|
TCHAR szPath[GUIDSTR_MAX + 20]; // Add room for "CLSID\\"
|
|
|
|
if (phkCLSID)
|
|
{
|
|
StringFromGUID2A(&pidri->clsid, szThisCLSID, ARRAYSIZE(szThisCLSID));
|
|
wsprintf(szPath, c_szSSlashS, c_szCLSID, szThisCLSID);
|
|
|
|
if (SHRegOpenKey(HKEY_CLASSES_ROOT, szPath, phkCLSID) != ERROR_SUCCESS)
|
|
{
|
|
// BUGBUG: We should probably look for more error codes
|
|
return E_INVALIDARG;
|
|
}
|
|
}
|
|
|
|
if (phkBase)
|
|
{
|
|
LONG rgfInOut = SFGAO_FOLDER;
|
|
|
|
if (FAILED(psf->lpVtbl->GetAttributesOf(psf, 1, &pidl, &rgfInOut)))
|
|
{
|
|
rgfInOut = 0;
|
|
}
|
|
|
|
_SHGetBaseKey(rgfInOut&SFGAO_FOLDER, phkBase);
|
|
}
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
HRESULT
|
|
_RegItems_DeleteRegItem(IShellFolder *psf, LPCITEMIDLIST pidl, BOOL fIgnoreAttrs)
|
|
{
|
|
CRegItemsSF *this;
|
|
const CLSID *pclsid;
|
|
HKEY hkParent;
|
|
TCHAR szClass[GUIDSTR_MAX];
|
|
LPITEMIDLIST pidlAbs;
|
|
|
|
//
|
|
// sanity
|
|
//
|
|
if (!_RegItems_IsReg(psf, pidl))
|
|
return E_INVALIDARG;
|
|
|
|
this = IToClass(CRegItemsSF, sf, psf);
|
|
pclsid = &((LPIDREGITEM)pidl)->clsid;
|
|
|
|
if (_RegItems_NReqItem(&this->rii, pclsid) >= 0)
|
|
{
|
|
// attempting to delete a 'required' item!!!
|
|
Assert(FALSE);
|
|
return E_ACCESSDENIED;
|
|
}
|
|
|
|
if (!fIgnoreAttrs &&
|
|
!(_RegItems_GetAttributesFromCLSID(pclsid) & SFGAO_CANDELETE))
|
|
{
|
|
return E_ACCESSDENIED;
|
|
}
|
|
|
|
//
|
|
// delete the item
|
|
//
|
|
StringFromGUID2A(pclsid, szClass, ARRAYSIZE(szClass));
|
|
hkParent = this->rii.hkRegItems;
|
|
|
|
if (!*szClass || !hkParent ||
|
|
(RegDeleteKey(hkParent, szClass) != ERROR_SUCCESS))
|
|
{
|
|
Assert(FALSE);
|
|
return E_FAIL;
|
|
}
|
|
|
|
//
|
|
// tell the world
|
|
//
|
|
pidlAbs = ILCombine(this->rii.pidlThis, pidl);
|
|
if (pidlAbs)
|
|
{
|
|
SHChangeNotify(SHCNE_DELETE, SHCNF_IDLIST, pidlAbs, NULL);
|
|
ILFree(pidlAbs);
|
|
}
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
BOOL
|
|
_RegItems_GetDeleteMessage(IShellFolder *psf, LPCITEMIDLIST pidl, LPTSTR buffer, int cchMax)
|
|
{
|
|
CRegItemsSF *this;
|
|
const CLSID *pclsid;
|
|
HKEY hkParent, hk;
|
|
TCHAR szClass[GUIDSTR_MAX];
|
|
LONG cbMax = cchMax * SIZEOF(TCHAR);
|
|
VDATEINPUTBUF(buffer, TCHAR, cchMax);
|
|
|
|
//
|
|
// sanity
|
|
//
|
|
if (cbMax > 0)
|
|
*buffer = 0;
|
|
else
|
|
return FALSE;
|
|
|
|
if (!_RegItems_IsReg(psf, pidl))
|
|
return FALSE;
|
|
|
|
this = IToClass(CRegItemsSF, sf, psf);
|
|
pclsid = &((LPIDREGITEM)pidl)->clsid;
|
|
|
|
if (_RegItems_NReqItem(&this->rii, pclsid) >= 0)
|
|
return FALSE;
|
|
|
|
//
|
|
// get the item key
|
|
//
|
|
StringFromGUID2A(pclsid, szClass, ARRAYSIZE(szClass));
|
|
hkParent = this->rii.hkRegItems;
|
|
|
|
if (!*szClass || !hkParent ||
|
|
(RegOpenKey(hkParent, szClass, &hk) != ERROR_SUCCESS))
|
|
{
|
|
Assert(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// get the message
|
|
//
|
|
if (RegQueryValueEx(hk, c_szRegValDeleteMessage, NULL, NULL, (LPBYTE)buffer, &cbMax)
|
|
!= ERROR_SUCCESS)
|
|
{
|
|
*buffer = 0;
|
|
}
|
|
|
|
RegCloseKey(hk);
|
|
return (*buffer != 0);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//BUGBUG: this routine is a stack pig (but we have to ship)
|
|
#define MAX_REGITEM_WARNTEXT 1024
|
|
void RegItems_Delete(LPSHELLFOLDER psfReg, HWND hwndOwner, LPDATAOBJECT pdtobj)
|
|
{
|
|
STGMEDIUM medium;
|
|
LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
|
|
|
|
if (pida)
|
|
{
|
|
TCHAR szItemWarning[MAX_REGITEM_WARNTEXT];
|
|
UINT i, count = pida->cidl;
|
|
UINT nregfirst = (UINT)-1;
|
|
UINT creg = 0;
|
|
UINT cwarn = 0;
|
|
LPCITEMIDLIST pidl;
|
|
|
|
//
|
|
// calc number of regitems and index of first
|
|
//
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
pidl = IDA_GetIDListPtr(pida, i);
|
|
Assert(pidl);
|
|
|
|
if (_RegItems_IsReg(psfReg, pidl))
|
|
{
|
|
creg++;
|
|
if (nregfirst == (UINT)-1)
|
|
nregfirst = i;
|
|
|
|
if ((cwarn < 2) && _RegItems_GetDeleteMessage(psfReg, pidl,
|
|
szItemWarning, ARRAYSIZE(szItemWarning)))
|
|
{
|
|
cwarn++;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// compose the confirmation message / ask the user / fry the items...
|
|
//
|
|
if (creg)
|
|
{
|
|
TCHAR szItemName[MAX_PATH];
|
|
TCHAR szWarnText[1024 + MAX_REGITEM_WARNTEXT];
|
|
TCHAR szWarnCaption[128];
|
|
TCHAR szTemp[256];
|
|
MSGBOXPARAMS mbp = {SIZEOF(MSGBOXPARAMS), hwndOwner,
|
|
HINST_THISDLL, szWarnText, szWarnCaption,
|
|
MB_YESNO | MB_USERICON, MAKEINTRESOURCE(IDI_NUKEFILE),
|
|
0, NULL, 0};
|
|
|
|
//
|
|
// so we can tell if we got these later
|
|
//
|
|
*szItemName = 0;
|
|
*szWarnText = 0;
|
|
|
|
//
|
|
// if there is only one, mention it by name
|
|
//
|
|
if (creg == 1)
|
|
{
|
|
TCHAR szTemp[256];
|
|
STRRET str;
|
|
pidl = IDA_GetIDListPtr(pida, nregfirst);
|
|
|
|
if (SUCCEEDED(psfReg->lpVtbl->GetDisplayNameOf(psfReg, pidl,
|
|
SHGDN_NORMAL, &str)))
|
|
{
|
|
int idString = (creg == count)?
|
|
IDS_CANTRECYCLEREGITEMS_NAME :
|
|
IDS_CANTRECYCLEREGITEMS_INCL_NAME;
|
|
|
|
StrRetToStrN(szItemName, MAX_PATH, &str, pidl);
|
|
LoadString(HINST_THISDLL, idString, szTemp,
|
|
ARRAYSIZE(szTemp));
|
|
wsprintf(szWarnText, szTemp, szItemName);
|
|
}
|
|
}
|
|
|
|
//
|
|
// otherwise, say "these items..." or "some of these items..."
|
|
//
|
|
if (!*szWarnText)
|
|
{
|
|
int idString = (creg == count)?
|
|
IDS_CANTRECYCLEREGITEMS_ALL : IDS_CANTRECYCLEREGITEMS_SOME;
|
|
LoadString(HINST_THISDLL, idString, szWarnText,
|
|
ARRAYSIZE(szWarnText));
|
|
|
|
//
|
|
// we just loaded a very vague message
|
|
// don't confuse the user any more by adding random text
|
|
// if these is a special warning, force it to show separately
|
|
//
|
|
if (cwarn == 1)
|
|
cwarn++;
|
|
}
|
|
lstrcat(szWarnText, c_szCrLfLf);
|
|
|
|
//
|
|
// if there is exactly one special warning message, add it in
|
|
//
|
|
if (cwarn == 1)
|
|
{
|
|
lstrcat(szWarnText, szItemWarning);
|
|
lstrcat(szWarnText, c_szCrLfLf);
|
|
}
|
|
|
|
//
|
|
// add the question "are you sure..."
|
|
//
|
|
if ((count == 1) && *szItemName)
|
|
{
|
|
TCHAR szTemp2[256];
|
|
LoadString(HINST_THISDLL, IDS_CONFIRMDELETEDESKTOPREGITEM,
|
|
szTemp2, ARRAYSIZE(szTemp2));
|
|
wsprintf(szTemp, szTemp2, szItemName);
|
|
}
|
|
else
|
|
{
|
|
LoadString(HINST_THISDLL, IDS_CONFIRMDELETEDESKTOPREGITEMS,
|
|
szTemp, ARRAYSIZE(szTemp));
|
|
}
|
|
lstrcat(szWarnText, szTemp);
|
|
|
|
//
|
|
// finally, the message box caption (also needed in loop below)
|
|
//
|
|
LoadString(HINST_THISDLL, IDS_CONFIRMDELETE_CAPTION, szWarnCaption,
|
|
ARRAYSIZE(szWarnCaption));
|
|
|
|
//
|
|
// make sure the user is cool with it
|
|
//
|
|
if (MessageBoxIndirect(&mbp) == IDYES)
|
|
{
|
|
LPCITEMIDLIST *ppidlFS, *apidlFS = NULL;
|
|
UINT nstart = nregfirst;
|
|
|
|
//
|
|
// if there are fs items, remember them so we can delete later
|
|
//
|
|
if (creg < count)
|
|
{
|
|
apidlFS = (LPCITEMIDLIST *)LocalAlloc(LPTR, (count - creg) *
|
|
SIZEOF(LPCITEMIDLIST));
|
|
|
|
if (apidlFS)
|
|
{
|
|
ppidlFS = apidlFS;
|
|
nstart = 0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// go ahead and delete the reg items
|
|
//
|
|
for (i = nstart; i < count; i++)
|
|
{
|
|
pidl = IDA_GetIDListPtr(pida, i);
|
|
|
|
if (_RegItems_IsReg(psfReg, pidl))
|
|
{
|
|
if ((cwarn > 1) && _RegItems_GetDeleteMessage(psfReg,
|
|
pidl, szItemWarning, ARRAYSIZE(szItemWarning)))
|
|
{
|
|
STRRET str;
|
|
if (SUCCEEDED(psfReg->lpVtbl->GetDisplayNameOf(
|
|
psfReg, pidl, SHGDN_NORMAL, &str)))
|
|
{
|
|
StrRetToStrN(szItemName, MAX_PATH, &str, pidl);
|
|
}
|
|
else
|
|
lstrcpy(szItemName, szWarnCaption);
|
|
|
|
MessageBox(hwndOwner, szItemWarning, szItemName,
|
|
MB_OK | MB_ICONINFORMATION);
|
|
}
|
|
|
|
_RegItems_DeleteRegItem(psfReg, pidl, FALSE);
|
|
}
|
|
else if (apidlFS)
|
|
{
|
|
// remember this one
|
|
*ppidlFS++ = pidl;
|
|
}
|
|
}
|
|
|
|
//
|
|
// now delete the fs objects
|
|
//
|
|
if (apidlFS)
|
|
{
|
|
IContextMenu *pcm;
|
|
if (SUCCEEDED(psfReg->lpVtbl->GetUIObjectOf(psfReg,
|
|
hwndOwner, (ppidlFS - apidlFS), apidlFS,
|
|
&IID_IContextMenu, NULL, &pcm)))
|
|
{
|
|
// BUGBUG: everybody ignores CMIC_MASK_FLAG_NO_UI
|
|
CMINVOKECOMMANDINFOEX ici =
|
|
{
|
|
SIZEOF(CMINVOKECOMMANDINFOEX),
|
|
CMIC_MASK_FLAG_NO_UI,
|
|
hwndOwner,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
SW_NORMAL,
|
|
};
|
|
#ifdef UNICODE
|
|
CHAR szDeleteAnsi[MAX_PATH];
|
|
WideCharToMultiByte(CP_ACP, 0,
|
|
c_szDelete, -1,
|
|
szDeleteAnsi, ARRAYSIZE(szDeleteAnsi),
|
|
NULL, NULL);
|
|
ici.lpVerb = szDeleteAnsi;
|
|
ici.lpVerbW = c_szDelete;
|
|
ici.fMask |= CMIC_MASK_UNICODE;
|
|
#else
|
|
ici.lpVerb = c_szDelete;
|
|
#endif
|
|
|
|
pcm->lpVtbl->InvokeCommand(pcm,
|
|
(CMINVOKECOMMANDINFO*)&ici);
|
|
pcm->lpVtbl->Release(pcm);
|
|
}
|
|
|
|
LocalFree((HANDLE)apidlFS);
|
|
}
|
|
}
|
|
}
|
|
|
|
HIDA_ReleaseStgMedium(pida, &medium);
|
|
}
|
|
}
|