Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

2123 lines
60 KiB

//---------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation 1991-1992
//
// File: ultrootx.c
//
// History:
// 03-16-93 SatoNa Created.
//
//---------------------------------------------------------------------------
#include "shellprv.h"
#pragma hdrstop
#ifdef USE_OLEDB
#include "oledbshl.h"
#endif
#define MAX_REGITEMCCH 128
#define Desktop_IsReg(_pidl) (SIL_GetType(_pidl) == SHID_ROOT_REGITEM)
HRESULT _Desktop_InitRegItems(void);
//
// netviewx.c externals
//
LPSHELLFOLDER CNetRoot_GetPSF(HWND hwnd);
extern IShellFolderVtbl c_NetRootVtbl;
//
// Global variables
//
#pragma pack(1)
typedef struct _IDROOT
{
USHORT cb;
BYTE bFlags;
USHORT cbNext;
} IDROOT, *LPIDROOT;
#pragma pack()
typedef struct _ROOTOBJ // rtobj
{
UINT uType;
LPFNCREATEINSTANCE lpfnCreateInstance;
LPCTSTR pszModule;
UINT iIcon;
} ROOTOBJ;
const TCHAR c_szDesktopNameSpace[] = TEXT("Desktop\\NameSpace");
#define INDEX_DRIVES 0
#define INDEX_NETWORK 1
#define CB_ROOTITEMID FIELDOFFSET(IDROOT, cbNext)
// BUGBUG: this will be localized
TCHAR const c_szExplorerExe[] = TEXT("Explorer.exe");
const REQREGITEM c_asDesktopReqItems[] =
{
{ &CLSID_ShellNetwork, IDS_ROOTNAMES + INDEX_NETWORK, c_szShell32Dll, -IDI_MYNETWORK, SFGAO_HASSUBFOLDER | SFGAO_HASPROPSHEET | SFGAO_FILESYSANCESTOR | SFGAO_DROPTARGET | SFGAO_FOLDER | SFGAO_CANRENAME},
{ &CLSID_ShellDrives, IDS_ROOTNAMES + INDEX_DRIVES, c_szExplorerExe, 0, SFGAO_HASSUBFOLDER | SFGAO_HASPROPSHEET | SFGAO_FILESYSANCESTOR | SFGAO_DROPTARGET | SFGAO_FOLDER | SFGAO_CANRENAME},
};
// BUGBUG: applet names sensitive to internationalization
const int c_piDesktopRegProperties[] =
{
IDS_NETCPL,
IDS_SYSDMCPL,
};
const ITEMIDLIST c_idlDesktop = { { 0, 0 } };
HRESULT CDesktop_GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, LPSHELLDETAILS lpDetails);
BOOL CDesktop_IsDesktItem(LPCITEMIDLIST pidl, UINT uRegItem)
{
const CLSID *pclsid;
// This should only be the Drives or Network folder
if (uRegItem >= ARRAYSIZE(c_asDesktopReqItems))
{
Assert(FALSE);
return(FALSE);
}
if (!Desktop_IsReg(pidl))
{
return(FALSE);
}
pclsid = RegItems_GetClassID(pidl);
if (!pclsid)
{
return(FALSE);
}
return(IsEqualGUID(pclsid, c_asDesktopReqItems[uRegItem].pclsid));
}
//===========================================================================
//
// CRootOfEvil stuff
//
// Notes:
// Because an instance of CRootOfEvil does not have any instance data,
// we put it in the code section. Therefore, we don't do anything in its
// AddRef or Release.
//
//===========================================================================
TCHAR const c_szDesktopClass[] = TEXT(STR_DESKTOPCLASS);
BOOL CDesktop_IsDesktop(HWND hwnd)
{
TCHAR szClassName[50];
if (!GetClassName(hwnd, szClassName, ARRAYSIZE(szClassName)))
{
return(FALSE);
}
return(lstrcmpi(szClassName, c_szDesktopClass) == 0);
}
//
// EnumShellFolder stuff
//
typedef struct _RootOfEvilESF
{
IEnumIDList eu;
int cRef;
int iCur;
LPENUMIDLIST peuFolder;
BOOL bUseAltEnum;
LPENUMIDLIST peuAltFolder;
} RootOfEvilESF, * PRootOfEvilESF;
STDMETHODIMP CDesktop_ESF_QueryInterface(LPENUMIDLIST peunk, REFIID riid, LPVOID * ppvObj);
STDMETHODIMP_(ULONG) CDesktop_ESF_AddRef(LPENUMIDLIST peunk) ;
STDMETHODIMP_(ULONG) CDesktop_ESF_Release(LPENUMIDLIST peunk);
STDMETHODIMP CDesktop_ESF_Next(LPENUMIDLIST peunk, ULONG celt, LPITEMIDLIST * rgelt, ULONG * pceltFetched);
// Vtable
IEnumIDListVtbl c_RootOfEvilESFVtbl =
{
CDesktop_ESF_QueryInterface,
CDesktop_ESF_AddRef,
CDesktop_ESF_Release,
CDesktop_ESF_Next,
CDefEnum_Skip,
CDefEnum_Reset,
CDefEnum_Clone
};
STDMETHODIMP CDesktop_ESF_QueryInterface(LPENUMIDLIST peunk, REFIID riid, LPVOID * ppvObj)
{
PRootOfEvilESF this = IToClassN(RootOfEvilESF, eu, peunk);
if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IEnumIDList))
{
*ppvObj = &this->eu;
this->cRef++;
return NOERROR;
}
*ppvObj = NULL;
return((E_NOINTERFACE));
}
STDMETHODIMP_(ULONG) CDesktop_ESF_AddRef(LPENUMIDLIST peunk)
{
PRootOfEvilESF this = IToClassN(RootOfEvilESF, eu, peunk);
this->cRef++;
return this->cRef;
}
STDMETHODIMP_(ULONG) CDesktop_ESF_Release(LPENUMIDLIST peunk)
{
PRootOfEvilESF this = IToClassN(RootOfEvilESF, eu, peunk);
this->cRef--;
if (this->cRef > 0)
return this->cRef;
if (this->peuFolder)
this->peuFolder->lpVtbl->Release(this->peuFolder);
if (this->peuAltFolder)
this->peuAltFolder->lpVtbl->Release(this->peuAltFolder);
LocalFree((HLOCAL)this);
}
STDMETHODIMP CDesktop_ESF_Next(LPENUMIDLIST peunk, ULONG celt,
LPITEMIDLIST * ppidl,
ULONG * pceltFetched)
{
PRootOfEvilESF this = IToClassN(RootOfEvilESF, eu, peunk);
HRESULT hres;
ULONG i, iCount;
TryAgain:
if (this->bUseAltEnum) {
if (this->peuAltFolder) {
hres = this->peuAltFolder->lpVtbl->Next(this->peuAltFolder, celt, ppidl, pceltFetched);
if (S_OK != hres) {
return hres;
}
//
// Users can pass NULL for pceltFetched if celt is 1.
//
if (pceltFetched) {
iCount = *pceltFetched;
} else {
iCount = 1;
}
//
// Mark the pidl's as common items
//
for (i=0; i < iCount; i++ ) {
ppidl[i]->mkid.abID[0] |= SHID_FS_COMMONITEM;
}
}
} else {
hres = this->peuFolder->lpVtbl->Next(this->peuFolder, celt, ppidl, pceltFetched);
if (hres == S_FALSE) {
this->bUseAltEnum = TRUE;
goto TryAgain;
}
}
++this->iCur;
return(hres);
}
//
// ShellFolder stuff
//
HRESULT STDMETHODCALLTYPE CDesktop_SF_QueryInterface(LPSHELLFOLDER psf, REFIID riid, LPVOID * ppvObj);
ULONG STDMETHODCALLTYPE CDesktop_SF_AddRef(LPSHELLFOLDER psf);
ULONG STDMETHODCALLTYPE CDesktop_SF_Release(LPSHELLFOLDER psf);
STDMETHODIMP CDesktop_ParseDisplayName(LPSHELLFOLDER psf,
HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName,
ULONG * pchEaten, LPITEMIDLIST * ppidl, ULONG * pdwAttributes);
STDMETHODIMP CDesktop_EnumObjects( LPSHELLFOLDER psf, HWND hwndOwner, DWORD grfFlags, LPENUMIDLIST * ppenumUnknown);
STDMETHODIMP CDesktop_BindToObject(LPSHELLFOLDER psf, LPCITEMIDLIST pidl, LPBC pbc,
REFIID riid, LPVOID * ppvOut);
STDMETHODIMP CDesktop_CompareIDs(LPSHELLFOLDER psf, LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
STDMETHODIMP CDesktop_CreateViewObject(LPSHELLFOLDER psf, HWND hwnd, REFIID riid, LPVOID * ppvOut);
STDMETHODIMP CDesktop_GetAttributesOf(LPSHELLFOLDER psf, UINT cidl, LPCITEMIDLIST * apidl, ULONG * rgfOut);
STDMETHODIMP CDesktop_GetUIObjectOf(LPSHELLFOLDER psf, HWND hwndOwner, UINT cidl, LPCITEMIDLIST * apidl,
REFIID riid, UINT * prgfInOut, LPVOID * ppvOut);
STDMETHODIMP CDesktop_GetDisplayNameOf(LPSHELLFOLDER psf, LPCITEMIDLIST pidl, DWORD dwReserved, LPSTRRET pStrRet);
STDMETHODIMP CDesktop_SetNameOf(LPSHELLFOLDER psf, HWND hwndOwner,
LPCITEMIDLIST pidl, LPCOLESTR lpszName, DWORD dwReserved, LPITEMIDLIST * ppidlOut);
//===========================================================================
// CRootOfEvil : VTable
//===========================================================================
IShellFolderVtbl c_RootOfEvilSFVtbl =
{
CDesktop_SF_QueryInterface,
CDesktop_SF_AddRef,
CDesktop_SF_Release,
CDesktop_ParseDisplayName,
CDesktop_EnumObjects,
CDesktop_BindToObject,
CDefShellFolder_BindToStorage,
CDesktop_CompareIDs,
CDesktop_CreateViewObject,
CDesktop_GetAttributesOf,
CDesktop_GetUIObjectOf,
CDesktop_GetDisplayNameOf,
CDesktop_SetNameOf,
};
typedef struct _RootOfEvilSF
{
IShellFolder sf;
int cRef;
IShellFolder *psfDesktop; // "Desktop" shell folder (real files live here)
IShellFolder *psfRegItems; // RegItems like Recycle Bin (our outer folder)
ULONG uRegister; // SHChangeNotifyRegister results
IShellFolder *psfAltDesktop; // "Common Desktop" shell folder
} RootOfEvilSF, *PRootOfEvilSF;
//
// We have a single instance of this RootOfEvil class / per process.
//
// Notes: We must put this object in per-instance DS because
// psfDesktop and pidl will be allocated by LocalAlloc().
//
#pragma data_seg(DATASEG_PERINSTANCE)
static RootOfEvilSF c_sfRootOfEvil = { { &c_RootOfEvilSFVtbl }, 0, NULL, NULL } ;
REGITEMSINFO g_sDesktopRegInfo =
{
&c_sfRootOfEvil.sf,
NULL,
TEXT(':'),
SHID_ROOT_REGITEM,
&c_idlDesktop,
1,
SFGAO_CANLINK,
ARRAYSIZE(c_asDesktopReqItems),
c_asDesktopReqItems,
} ;
#pragma data_seg()
// During shell32.dll process detach, we will call here to do the final
// release of the IShellFolder ptrs which used to be left around for the
// life of the process. This quiets things such as OLE's debug allocator,
// which detected the leak.
void ReleaseRootFolders()
{
if (c_sfRootOfEvil.psfDesktop)
{
c_sfRootOfEvil.psfDesktop->lpVtbl->Release(c_sfRootOfEvil.psfDesktop);
}
if (c_sfRootOfEvil.psfRegItems)
{
c_sfRootOfEvil.psfRegItems->lpVtbl->Release(c_sfRootOfEvil.psfRegItems);
}
if (c_sfRootOfEvil.psfAltDesktop)
{
c_sfRootOfEvil.psfAltDesktop->lpVtbl->Release(c_sfRootOfEvil.psfAltDesktop);
}
}
LPITEMIDLIST CDesktop_CreateRegIDFromCLSID(const CLSID * pclsid)
{
LPITEMIDLIST pidlReg, pidlAbs;
pidlReg = RegItems_CreateRelID(&g_sDesktopRegInfo, pclsid);
if (!pidlReg)
{
return(NULL);
}
pidlAbs = ILCombine((LPCITEMIDLIST)&c_idlDesktop, pidlReg);
ILFree(pidlReg);
return(pidlAbs);
}
LPITEMIDLIST CDesktop_CreateRegID(UINT uRegItem)
{
const CLSID * pclsid;
// This should only be the Drives or Network folder
if (uRegItem >= ARRAYSIZE(c_asDesktopReqItems))
{
Assert(FALSE);
return(NULL);
}
pclsid = c_asDesktopReqItems[uRegItem].pclsid;
return CDesktop_CreateRegIDFromCLSID(pclsid);
}
//===========================================================================
// CRootOfEvil : Constructors
//===========================================================================
//
// Te be called from IClassFactory::CreateInstance
//
HRESULT CALLBACK CDesktop_CreateInstance(LPUNKNOWN punkOuter, REFIID riid, LPVOID * ppv)
{
Assert(punkOuter==NULL);
return c_sfRootOfEvil.sf.lpVtbl->QueryInterface(&c_sfRootOfEvil.sf, riid, ppv);
}
//
// Helper function which returns a IShellFolder interface to the desktop
// folder. This is equivalent to call CoCreateInstance with CLSID_ShellDesktop.
//
//
// CoCreateInstance(CLSID_Desktop, NULL,
// CLSCTX_INPROC, IID_IShellFolder, &pshf);
//
HRESULT WINAPI SHGetDesktopFolder(LPSHELLFOLDER *ppshf)
{
return CDesktop_CreateInstance(NULL, &IID_IShellFolder, ppshf);
}
LPSHELLFOLDER Desktop_GetShellFolder(BOOL fInit)
{
// Always make sure the RegItems are initialized
_Desktop_InitRegItems();
if (fInit && !c_sfRootOfEvil.psfDesktop)
{
//
// Initialize the task allocator before entering the critical
// section to avoid calling _LoadOLE from within the critical
// section.
//
extern LPMALLOC SHGetTaskAllocator(HRESULT *phres);
SHGetTaskAllocator(NULL);
ENTERCRITICAL;
if (!c_sfRootOfEvil.psfDesktop)
{
//
// We don't need to release this reference count.
//
LPSHELLFOLDER psf;
CDesktop_CreateInstance(NULL, &IID_IShellFolder, &psf);
}
LEAVECRITICAL;
}
return c_sfRootOfEvil.psfRegItems ? c_sfRootOfEvil.psfRegItems : &c_sfRootOfEvil.sf;
}
//===========================================================================
// CRootOfEvil : members
//===========================================================================
HRESULT _Desktop_InitRegItems(void)
{
HRESULT hres=NOERROR;
if (c_sfRootOfEvil.psfRegItems)
{
return(NOERROR);
}
ENTERCRITICAL;
if (c_sfRootOfEvil.psfRegItems)
{
// Check again in case we were re-entered
goto AlreadyDone;
}
if (!g_sDesktopRegInfo.hkRegItems)
{
g_sDesktopRegInfo.hkRegItems = SHGetExplorerSubHkey(HKEY_LOCAL_MACHINE, c_szDesktopNameSpace, FALSE);
}
// BUGBUG DAVEPL
//
// This temporarily removes the nethood until we get
// the required net APIs
//#define NONET
#ifdef NONET
g_sDesktopRegInfo.iReqItems = ARRAYSIZE(c_asDesktopReqItems)-1;
g_sDesktopRegInfo.pReqItems = c_asDesktopReqItems+1;
#endif
//
// Algorithm:
//
// "NoNetHood" restruction -> always hide the hood.
// Otherwise, show the hood if
// either MPR says so or we have RNA.
//
if ( (!(GetSystemMetrics(SM_NETWORK) & RNC_NETWORKS))
|| SHRestricted(REST_NONETHOOD))
{
// Get rid of the "My Network" thing if no net running.
// Also, get rid of it if a restriction is in place.
g_sDesktopRegInfo.iReqItems = ARRAYSIZE(c_asDesktopReqItems)-1;
g_sDesktopRegInfo.pReqItems = c_asDesktopReqItems+1;
}
hres = RegItems_AddToShellFolder(&g_sDesktopRegInfo,
&c_sfRootOfEvil.psfRegItems);
AlreadyDone:
LEAVECRITICAL
return(hres);
}
//
// QueryInterface
//
HRESULT STDMETHODCALLTYPE CDesktop_SF_QueryInterface(LPSHELLFOLDER psf, REFIID riid, LPVOID * ppvObj)
{
PRootOfEvilSF this = IToClassN(RootOfEvilSF, sf, psf);
HRESULT hres = (E_NOINTERFACE);
*ppvObj = NULL;
if (IsEqualIID(riid, &IID_IShellFolder))
{
if (!this->psfDesktop)
{
#ifdef USE_OLEDB
hres = COFSFolder_CreateFromIDList((LPITEMIDLIST)&c_idlDesktop, riid, &this->psfDesktop);
#else
hres = CFSFolder_CreateFromIDList((LPITEMIDLIST)&c_idlDesktop, riid, &this->psfDesktop);
#endif
if (FAILED(hres))
{
this->psfDesktop = NULL;
DebugMsg(DM_TRACE, TEXT("Failed to create desktop IShellFolder!"));
goto Error1;
}
}
if (!this->psfAltDesktop && !SHRestricted(REST_NOCOMMONGROUPS))
{
LPCITEMIDLIST pidlAltDesk;
pidlAltDesk = GetSpecialFolderIDList(NULL, CSIDL_COMMON_DESKTOPDIRECTORY, TRUE);
#ifdef USE_OLEDB
hres = COFSFolder_CreateFromIDList(pidlAltDesk, riid, &this->psfAltDesktop);
#else
hres = CFSFolder_CreateFromIDList(pidlAltDesk, riid, &this->psfAltDesktop);
#endif
if (FAILED(hres))
{
this->psfDesktop->lpVtbl->Release(this->psfDesktop);
this->psfDesktop = NULL;
this->psfAltDesktop = NULL;
DebugMsg(DM_TRACE, TEXT("Failed to create common desktop IShellFolder!"));
goto Error1;
}
}
if (SUCCEEDED(_Desktop_InitRegItems()))
{
psf = c_sfRootOfEvil.psfRegItems;
}
goto RetUnknown;
}
if (IsEqualIID(riid, &IID_IShellIcon) && this->psfDesktop)
{
// BUGBUG this realy isn't a PC thing to do, but FSTree deal with it.
hres = this->psfDesktop->lpVtbl->QueryInterface(this->psfDesktop,riid, ppvObj);
}
if (IsEqualIID(riid, &IID_IUnknown))
{
RetUnknown:
*ppvObj = psf;
psf->lpVtbl->AddRef(psf);
return NOERROR;
}
Error1:
return(hres);
}
//
// AddRef
//
ULONG STDMETHODCALLTYPE CDesktop_SF_AddRef(LPSHELLFOLDER psf)
{
PRootOfEvilSF this = IToClassN(RootOfEvilSF, sf, psf);
++this->cRef;
return(this->cRef);
}
//
// Release
//
ULONG STDMETHODCALLTYPE CDesktop_SF_Release(LPSHELLFOLDER psf)
{
PRootOfEvilSF this = IToClassN(RootOfEvilSF, sf, psf);
--this->cRef;
if (this->cRef > 0)
{
return(this->cRef);
}
if (this->cRef < 0)
{
// Somebody released too many times
Assert(FALSE);
this->cRef = 0;
return(0);
}
if (this->psfDesktop)
{
IShellFolder *psfFolder;
psfFolder = this->psfDesktop;
this->psfDesktop = NULL;
// BUGBUG: Can we get re-entered at a bad time here?
// Somebody would have to create a new instance of the ultimate
// root between the previous line and the next one.
psfFolder->lpVtbl->Release(psfFolder);
}
if (this->psfAltDesktop)
{
IShellFolder *psfFolder;
psfFolder = this->psfAltDesktop;
this->psfAltDesktop = NULL;
// BUGBUG: Can we get re-entered at a bad time here?
// Somebody would have to create a new instance of the ultimate
// root between the previous line and the next one.
psfFolder->lpVtbl->Release(psfFolder);
}
return(0);
}
//----------------------------------------------------------------------------
STDMETHODIMP CDesktop_ParseDisplayName(LPSHELLFOLDER psf,
HWND hwndOwner, LPBC pbc, LPOLESTR pwzDisplayName, ULONG *pchEaten,
LPITEMIDLIST * ppidl, ULONG * pdwAttributes)
{
PRootOfEvilSF this = IToClassN(RootOfEvilSF, sf, psf);
// BUGBUG: Implement it later (it always returns "drives")
HRESULT hres = (E_INVALIDARG);
*ppidl = NULL; // assume error
if (pwzDisplayName)
{
TCHAR szDisplayName[MAX_PATH]; // This is enough to dispatch
OleStrToStrN(szDisplayName, ARRAYSIZE(szDisplayName), pwzDisplayName, -1);
#ifdef SN_TRACE
DebugMsg(DM_TRACE, TEXT("sh TR - CRoot::ParseDisplayName called (%s)"),
szDisplayName);
#endif
if (*szDisplayName == TEXT('\0')) // Is this empty
{
// The string is empty, return the moniker to "My Computer"
*ppidl = CDesktop_CreateRegID(CDESKTOP_REGITEM_DRIVES);
if (*ppidl)
{
hres = NOERROR;
if (pchEaten)
{
*pchEaten=0;
}
if (pdwAttributes)
{
// Note that we can't call CDesktop_GetAttributesOf directly.
c_sfRootOfEvil.psfRegItems->lpVtbl->GetAttributesOf(
c_sfRootOfEvil.psfRegItems,
1, ppidl, pdwAttributes);
}
}
else
{
hres = (E_OUTOFMEMORY);
}
}
else
{
LPITEMIDLIST pidlLeft = NULL;
Assert(hres == (E_INVALIDARG));
if ((InRange(szDisplayName[0], TEXT('A'), TEXT('Z')) || InRange(szDisplayName[0], TEXT('a'), TEXT('z')))
&& szDisplayName[1] == TEXT(':'))
{
// The string contains a path, let "My Computer" figire it out.
pidlLeft = CDesktop_CreateRegID(CDESKTOP_REGITEM_DRIVES);
if (pchEaten) {
*pchEaten=0;
}
}
else if (PathIsUNC(szDisplayName))
{
// The path is UNC, let "World" figure it out.
pidlLeft = CDesktop_CreateRegID(CDESKTOP_REGITEM_NETWORK);
}
else
{
// This must be a desktop item, psfDesktop may not be inited in
// the case where we are called from ILCreateFromPath()
if (FS_IsCommonItem(ppidl[0])) {
if (this->psfAltDesktop)
hres = this->psfAltDesktop->lpVtbl->ParseDisplayName(this->psfAltDesktop, hwndOwner, pbc, pwzDisplayName, pchEaten, ppidl, pdwAttributes);
} else {
if (this->psfDesktop)
hres = this->psfDesktop->lpVtbl->ParseDisplayName(this->psfDesktop, hwndOwner, pbc, pwzDisplayName, pchEaten, ppidl, pdwAttributes);
}
}
if (pidlLeft)
{
LPSHELLFOLDER psfRight;
Assert(Desktop_IsReg(pidlLeft));
hres = RegItems_BindToObject(&g_sDesktopRegInfo, pidlLeft, pbc, &IID_IShellFolder, &psfRight);
if (SUCCEEDED(hres))
{
LPITEMIDLIST pidlRight;
hres = psfRight->lpVtbl->ParseDisplayName(psfRight,
hwndOwner, pbc, pwzDisplayName, pchEaten, &pidlRight,
pdwAttributes);
if (SUCCEEDED(hres))
{
*ppidl = ILCombine(pidlLeft, pidlRight);
if (*ppidl==NULL) {
hres = (E_OUTOFMEMORY);
}
ILFree(pidlRight);
}
psfRight->lpVtbl->Release(psfRight);
}
ILFree(pidlLeft);
}
}
}
return hres;
}
STDMETHODIMP CDesktop_EnumObjects(LPSHELLFOLDER psf, HWND hwndOwner,
DWORD grfFlags, LPENUMIDLIST * ppenumUnknown)
{
PRootOfEvilSF this = IToClassN(RootOfEvilSF, sf, psf);
PRootOfEvilESF pesf = (void*)LocalAlloc(LPTR, SIZEOF(RootOfEvilESF));
HRESULT hres;
if (!pesf)
{
return((E_OUTOFMEMORY));
}
hres = this->psfDesktop->lpVtbl->EnumObjects(this->psfDesktop, hwndOwner, grfFlags, &(pesf->peuFolder));
if (!SUCCEEDED(hres))
{
LocalFree((HLOCAL)pesf);
return(hres);
}
if (this->psfAltDesktop) {
hres = this->psfAltDesktop->lpVtbl->EnumObjects(this->psfAltDesktop, hwndOwner, grfFlags, &(pesf->peuAltFolder));
if (!SUCCEEDED(hres))
{
pesf->peuFolder->lpVtbl->Release(pesf->peuFolder);
LocalFree((HLOCAL)pesf);
return(hres);
}
}
pesf->eu.lpVtbl = &c_RootOfEvilESFVtbl;
pesf->cRef = 1;
pesf->iCur = 0;
pesf->bUseAltEnum = FALSE;
*ppenumUnknown = &pesf->eu;
return(NOERROR);
}
STDMETHODIMP CDesktop_BindToObject(LPSHELLFOLDER psf, LPCITEMIDLIST pidl, LPBC pbc,
REFIID riid, LPVOID * ppvOut)
{
PRootOfEvilSF this = IToClassN(RootOfEvilSF, sf, psf);
HRESULT hres=(E_INVALIDARG);
if (pidl == NULL)
return(hres); // quick test
{
// We should never get called here without being initialized
Assert(this->psfDesktop);
// This must be a "real" desktop item
if (this->psfAltDesktop && FS_IsCommonItem(pidl)) {
hres = this->psfAltDesktop->lpVtbl->BindToObject(this->psfAltDesktop, pidl, pbc,
riid, ppvOut);
} else {
hres = this->psfDesktop->lpVtbl->BindToObject(this->psfDesktop, pidl, pbc,
riid, ppvOut);
}
Assert(hres != (E_INVALIDARG));
}
return hres;
}
STDMETHODIMP CDesktop_CompareIDs(LPSHELLFOLDER psf, LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
{
PRootOfEvilSF this = IToClassN(RootOfEvilSF, sf, psf);
HRESULT hres;
// special case
// are they both desktop objects?
if (pidl1->mkid.cb == 0 && pidl2->mkid.cb == 0)
return ResultFromShort(0);
//
// If both objects aren't from the same directory, they won't match.
//
if (this->psfAltDesktop) {
if (FS_IsCommonItem(pidl1)) {
if (FS_IsCommonItem(pidl2)) {
return this->psfAltDesktop->lpVtbl->CompareIDs(this->psfAltDesktop, lParam,
pidl1, pidl2);
} else {
return ResultFromShort(-1);
}
} else {
if (FS_IsCommonItem(pidl2)) {
return ResultFromShort(1);
} else {
return this->psfDesktop->lpVtbl->CompareIDs(this->psfDesktop, lParam,
pidl1, pidl2);
}
}
} else {
return this->psfDesktop->lpVtbl->CompareIDs(this->psfDesktop, lParam,
pidl1, pidl2);
}
}
//----------------------------------------------------------------------------
HRESULT BackgroundMenu_HandleCommand(PRootOfEvilSF this, HWND hwndOwner,
WPARAM wparam, BOOL bExecute)
{
HRESULT hres = NOERROR;
switch (wparam) {
case FSIDM_SORTBYNAME:
case FSIDM_SORTBYTYPE:
case FSIDM_SORTBYSIZE:
case FSIDM_SORTBYDATE:
if (bExecute)
{
ShellFolderView_ReArrange(hwndOwner, wparam - FSIDM_SORT_FIRST);
}
break;
case DFM_CMD_PROPERTIES:
case FSIDM_PROPERTIESBG:
if (bExecute)
{
// run the default applet in desk.cpl
SHRunControlPanel( TEXT("desk.cpl"), hwndOwner );
}
break;
case DFM_CMD_MOVE:
case DFM_CMD_COPY:
hres = (E_FAIL);
break;
case FSIDM_NEWFOLDER:
case DFM_CMD_NEWFOLDER:
if (bExecute)
{
CFSFolder_CreateFolder(hwndOwner, (LPITEMIDLIST)&c_idlDesktop);
}
break;
case FSIDM_NEWLINK:
if (bExecute)
{
CreateEmptyLink((LPITEMIDLIST)&c_idlDesktop, hwndOwner);
}
break;
case FSIDM_NEWOTHER:
if (bExecute)
{
CFSFolder_HandleNewOther((LPITEMIDLIST)&c_idlDesktop, hwndOwner);
}
break;
default:
// This is common menu items, use the default code.
hres = (S_FALSE);
break;
}
return hres;
}
//----------------------------------------------------------------------------
// To be called back from within CDefFolderMenu
//
// Returns:
// NOERROR, if successfully processed.
// (S_FALSE), if default code should be used.
//
extern void CleanupRegMenu();
HRESULT CALLBACK CDesktop_DFMCallBackBG(LPSHELLFOLDER psf, HWND hwndOwner,
LPDATAOBJECT pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
PRootOfEvilSF this = IToClassN(RootOfEvilSF, sf, psf);
HRESULT hres = NOERROR;
LPQCMINFO pqcm;
UINT id;
HMENU hmenu;
extern HMENU g_hmenuRegMenu;
switch(uMsg)
{
// BUGBUG, this could be combined with the one(s) in fstreex.c
case DFM_WM_MEASUREITEM: {
LPMEASUREITEMSTRUCT lpdis = (LPMEASUREITEMSTRUCT)lParam;
if (lpdis->itemID == (wParam + FSIDM_NEWOTHER)) {
NewObjMenu_MeasureItem(lpdis);
}
break;
}
case DFM_WM_DRAWITEM: {
LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT)lParam;
if (lpdis->itemID == (wParam + FSIDM_NEWOTHER)) {
NewObjMenu_DrawItem(lpdis);
}
break;
}
case DFM_WM_INITMENUPOPUP:
hmenu = (HMENU)wParam;
id = GetMenuItemID(hmenu, 0);
if (id == (UINT)(lParam + FSIDM_NEWFOLDER)) {
if (NewObjMenu_InitMenuPopup(hmenu, 3)) {
CleanupRegMenu();
g_hmenuRegMenu = hmenu;
}
}
break;
case DFM_RELEASE:
CleanupRegMenu();
break;
case DFM_MERGECONTEXTMENU:
if (!(wParam & CMF_VERBSONLY))
{
UINT idCmdFirst;
BOOL bDesktop = CDesktop_IsDesktop(hwndOwner);
pqcm = (LPQCMINFO)lParam;
// This needs to be saved before MergeMenu
idCmdFirst = pqcm->idCmdFirst;
// HACK: Note the Desktop and FSView menus are the same
CDefFolderMenu_MergeMenu(HINST_THISDLL, POPUP_FSVIEW_BACKGROUND,
POPUP_FSVIEW_POPUPMERGE, pqcm);
if (bDesktop)
{
// HACK: We only want LargeIcons on the real desktop
// so we remove the View menu
DeleteMenu(pqcm->hmenu, SFVIDM_MENU_VIEW, MF_BYCOMMAND);
}
else
{
// HACK: We want no Properties for DesktopInExplorer
DeleteMenu(pqcm->hmenu, FSIDM_PROPERTIESBG+idCmdFirst,
MF_BYCOMMAND);
}
}
break;
case DFM_GETHELPTEXT:
case DFM_GETHELPTEXTW:
hres = CFSFolder_DFMCallBackBG(this->psfDesktop, hwndOwner,
pdtobj, uMsg, wParam, lParam);
break;
case DFM_INVOKECOMMAND:
case DFM_VALIDATECMD:
hres = BackgroundMenu_HandleCommand(this, hwndOwner, wParam,
uMsg==DFM_INVOKECOMMAND);
break;
default:
hres = E_NOTIMPL;
break;
}
return hres;
}
//----------------------------------------------------------------------------
// To be called back from within CDefFolderMenu
//
// Returns:
// NOERROR, if successfully processed.
// (S_FALSE), if default code should be used.
//
HRESULT CALLBACK CDesktop_DFMCallBack(LPSHELLFOLDER psf, HWND hwndOwner,
LPDATAOBJECT pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
PRootOfEvilSF this;
STGMEDIUM medium;
HRESULT hres = NOERROR;
LPIDA pida;
psf = RegItems_GetInnerShellFolder(psf);
// Note we MUST wait until after getting the inner shell folder before
// getting this
this = IToClass(RootOfEvilSF, sf, psf);
switch(uMsg)
{
case DFM_MERGECONTEXTMENU:
if (!(wParam & CMF_VERBSONLY))
{
pida = DataObj_GetHIDA(pdtobj, &medium);
if (medium.hGlobal)
{
if (HIDA_GetCount(medium.hGlobal) == 1)
{
LPCITEMIDLIST pidlFirst = IDA_GetIDListPtr(pida, 0);
// We add those only for net & computer
if (CDesktop_IsMyComputer(pidlFirst) ||
CDesktop_IsMyNetwork(pidlFirst))
{
// Add them only if the network is enabled.
if ((GetSystemMetrics(SM_NETWORK) && RNC_NETWORKS) &&
!SHRestricted(REST_NONETCONNECTDISCONNECT) )
{
CDefFolderMenu_MergeMenu(HINST_THISDLL,
POPUP_DESKTOP_ITEM, 0, (LPQCMINFO)lParam);
}
}
}
HIDA_ReleaseStgMedium(pida, &medium);
}
}
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_CONNECT:
SHStartNetConnectionDialog(NULL, NULL, RESOURCETYPE_DISK);
break;
case FSIDM_DISCONNECT:
WNetDisconnectDialog(NULL, RESOURCETYPE_DISK);
SHChangeNotifyHandleEvents(); // flush any drive notifications
break;
case DFM_CMD_LINK:
hres = FS_CreateLinks(hwndOwner, this->psfDesktop, pdtobj, (LPTSTR)lParam);
break;
case DFM_CMD_PROPERTIES:
pida = DataObj_GetHIDA(pdtobj, &medium);
if (medium.hGlobal)
{
LPCITEMIDLIST pidlFirst = IDA_GetIDListPtr(pida, 0);
UINT i;
Assert(pidlFirst);
for (i = 0; i < ARRAYSIZE(c_piDesktopRegProperties) ; i++)
{
if (CDesktop_IsDesktItem(pidlFirst, i))
{
SHRunControlPanel(MAKEINTRESOURCE(c_piDesktopRegProperties[i]), hwndOwner);
break;
}
}
if ((i == ARRAYSIZE(c_piDesktopRegProperties)) && (Desktop_IsReg(pidlFirst))) {
HKEY ahkeys[1];
STRRET str;
RegItems_GetName(&g_sDesktopRegInfo, pidlFirst, &str);
if (SUCCEEDED(RegItems_GetClassKey(pidlFirst, &ahkeys[0])))
{
#ifdef UNICODE
Assert(str.uType == STRRET_OLESTR);
// REVIEW: Should we pass "Folder" key as well?
SHOpenPropSheet(str.pOleStr, ahkeys, 1, NULL, pdtobj, NULL, (LPCTSTR)lParam);
SHFree(str.pOleStr);
#else
// BUGBUG - This should have an assert about STRRET_CSTR
// REVIEW: Should we pass "Folder" key as well?
SHOpenPropSheet(str.cStr, ahkeys, 1, NULL, pdtobj, NULL, (LPCTSTR)lParam);
#endif
SHRegCloseKey(ahkeys[0]);
}
}
HIDA_ReleaseStgMedium(pida, &medium);
}
break;
case DFM_CMD_DELETE:
//
// we only get here if there is at least one regitem selected
//
RegItems_Delete(this->psfRegItems, hwndOwner, pdtobj);
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;
}
HRESULT CDesktop_CreateContextMenu(IShellFolder *psf, HWND hwndOwner,
UINT cidl, LPCITEMIDLIST *apidl, LPVOID *ppvOut)
{
HKEY hkeyProgID = NULL;
HKEY hkeyBaseID = NULL;
HRESULT hres;
if (Desktop_IsReg(apidl[0]))
{
RegItems_GetClassKeys(psf, apidl[0], &hkeyProgID, &hkeyBaseID);
}
else
{
SHGetClassKey((LPIDFOLDER)apidl[0], &hkeyProgID, NULL, FALSE);
SHGetBaseClassKey((LPIDFOLDER)apidl[0], &hkeyBaseID);
}
hres = CDefFolderMenu_Create(&c_idlDesktop, hwndOwner, cidl, apidl,
psf, CDesktop_DFMCallBack,
hkeyProgID, hkeyBaseID, (LPCONTEXTMENU *)ppvOut);
SHCloseClassKey(hkeyProgID);
SHCloseClassKey(hkeyBaseID);
return(hres);
}
//
// Callback from SHCreateShellFolderViewEx
//
HRESULT CALLBACK Desktop_FNVCallBack(LPSHELLVIEW psvOuter,
LPSHELLFOLDER psf,
HWND hwndOwner,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
PRootOfEvilSF this;
HRESULT hres = NOERROR; // assume no error
psf = RegItems_GetInnerShellFolder(psf);
this = IToClass(RootOfEvilSF, sf, psf);
switch(uMsg)
{
case DVM_MERGEMENU:
CDefFolderMenu_MergeMenu(HINST_THISDLL, 0, POPUP_FSVIEW_POPUPMERGE, (LPQCMINFO)lParam);
break;
case DVM_INVOKECOMMAND:
hres = CDesktop_DFMCallBackBG(psf, hwndOwner, NULL, DFM_INVOKECOMMAND, wParam, lParam);
break;
case DVM_DIDDRAGDROP:
if (wParam == DROPEFFECT_MOVE)
{
Assert(lParam);
RegItems_Delete(this->psfRegItems, hwndOwner, (LPDATAOBJECT)lParam);
}
break;
case DVM_GETDETAILSOF:
#define pdi ((DETAILSINFO *)lParam)
if (pdi->pidl && Desktop_IsReg(pdi->pidl))
hres = CDesktop_GetDetailsOf(pdi->pidl, wParam, (LPSHELLDETAILS)&pdi->fmt);
else
hres = FS_FNVCallBack(psvOuter, this->psfDesktop, hwndOwner, uMsg, wParam, lParam);
break;
#undef pdi
case DVM_COLUMNCLICK:
hres = FS_FNVCallBack(psvOuter, this->psfDesktop, hwndOwner, uMsg, wParam, lParam);
break;
case DVM_GETHELPTEXT:
#ifdef UNICODE
hres = CDesktop_DFMCallBackBG(psf, hwndOwner, NULL, DFM_GETHELPTEXTW, wParam, lParam);
#else
hres = CDesktop_DFMCallBackBG(psf, hwndOwner, NULL, DFM_GETHELPTEXT, wParam, lParam);
#endif
break;
//
// Some cases we forward to the file system callback .
case DVM_GETCCHMAX:
{
if (this->psfDesktop)
{
if (Desktop_IsReg((LPCITEMIDLIST)wParam))
{
*((int *)lParam) = MAX_REGITEMCCH;
hres = NOERROR;
}
else
hres = FS_FNVCallBack(psvOuter, this->psfDesktop, hwndOwner, uMsg, wParam, lParam);
}
else
hres = (E_FAIL);
break;
}
case DVM_FOLDERISPARENT:
{
// View needs to know if we are the parent of a particular pidl, so test
// against the two possible "parents" of our confused children
LPCITEMIDLIST pidlChild = (LPCITEMIDLIST) lParam;
LPCITEMIDLIST pidlCommonDesktop = GetSpecialFolderIDList(NULL, CSIDL_COMMON_DESKTOPDIRECTORY, TRUE);
if (FALSE == ILIsParent(&c_idlDesktop, pidlChild, TRUE) &&
FALSE == ILIsParent(pidlCommonDesktop, pidlChild, TRUE))
{
hres = S_FALSE;
}
else
{
hres = S_OK;
}
break;
}
default:
hres = (E_FAIL);
}
return hres;
}
STDMETHODIMP CDesktop_CreateViewObject(LPSHELLFOLDER psf, HWND hwnd, REFIID riid, LPVOID * ppvOut)
{
PRootOfEvilSF this = IToClassN(RootOfEvilSF, sf, psf);
if (IsEqualIID(riid, &IID_IShellView))
{
CSFV csfv = {
SIZEOF(CSFV), // cbSize
this->psfRegItems ? this->psfRegItems : psf, // pshf
NULL, // psvOuter
(LPCITEMIDLIST)&c_idlDesktop, //this->pidl, // pidl
SHCNE_DISKEVENTS | SHCNE_ASSOCCHANGED | SHCNE_NETSHARE | SHCNE_NETUNSHARE, // lEvents
Desktop_FNVCallBack, // pfnCallback
0,
};
return SHCreateShellFolderViewEx(&csfv, (LPSHELLVIEW *)ppvOut);
}
else if (IsEqualIID(riid, &IID_IDropTarget))
{
return CIDLDropTarget_Create(hwnd, &c_CFSDropTargetVtbl,
(LPITEMIDLIST)&c_idlDesktop, (LPDROPTARGET *)ppvOut);
}
else if (IsEqualIID(riid, &IID_IContextMenu))
{
return CDefFolderMenu_Create(&c_idlDesktop, hwnd, 0, NULL,
psf, CDesktop_DFMCallBackBG,
NULL, NULL, (LPCONTEXTMENU *)ppvOut);
}
return((E_NOINTERFACE));
}
STDMETHODIMP CDesktop_GetAttributesOf(LPSHELLFOLDER psf, UINT cidl, LPCITEMIDLIST * apidl, ULONG * rgfOut)
{
PRootOfEvilSF this = IToClass(RootOfEvilSF, sf, psf);
LPSHELLFOLDER psfTemp;
// asking about the root itself?
if (cidl == 0 || ((cidl == 1) && ((*apidl)->mkid.cb == 0)))
{
*rgfOut &= SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_CANRENAME |
SFGAO_HASSUBFOLDER | SFGAO_HASPROPSHEET | SFGAO_FILESYSANCESTOR;
return NOERROR;
}
if (this->psfAltDesktop && FS_IsCommonItem(apidl[0])) {
psfTemp = this->psfAltDesktop;
} else {
psfTemp = this->psfDesktop;
}
return psfTemp->lpVtbl->GetAttributesOf(psfTemp, cidl, apidl, rgfOut);
}
STDMETHODIMP CDesktop_GetUIObjectOf(LPSHELLFOLDER psf, HWND hwndOwner, UINT cidl, LPCITEMIDLIST * apidl,
REFIID riid, UINT * prgfInOut, LPVOID * ppvOut)
{
PRootOfEvilSF this = IToClass(RootOfEvilSF, sf, psf);
HRESULT hres=(E_INVALIDARG);
//
// Special case NULL or empty pidl for desktop object itself
//
if (cidl==1 && (apidl==NULL || apidl[0]==NULL || ILIsEmpty(apidl[0])))
{
if (IsEqualIID(riid, &IID_IExtractIcon)) {
hres = SHCreateDefExtIcon(NULL, // this dll
II_DESKTOP,
II_DESKTOP,
GIL_PERCLASS, // meaningless
(LPEXTRACTICON *)ppvOut);
}
#ifdef UNICODE
else if (IsEqualIID(riid, &IID_IExtractIconA)) {
LPEXTRACTICON pxicon;
hres = SHCreateDefExtIcon(NULL, // this dll
II_DESKTOP,
II_DESKTOP,
GIL_PERCLASS,
&pxicon);
if (SUCCEEDED(hres)) {
hres = pxicon->lpVtbl->QueryInterface(pxicon,riid,ppvOut);
pxicon->lpVtbl->Release(pxicon);
}
}
#endif
}
else
{
if (IsEqualIID(riid, &IID_IContextMenu))
{
if (cidl == 0)
{
hres = (E_INVALIDARG);
}
else if (FS_AreTheyAllFSObjects(cidl, apidl))
{
// Yes, they are all file system objects.
if (this->psfAltDesktop && FS_IsCommonItem(apidl[0])) {
hres = this->psfAltDesktop->lpVtbl->GetUIObjectOf(this->psfAltDesktop,
hwndOwner, cidl, apidl, riid, prgfInOut, ppvOut);
} else {
hres = this->psfDesktop->lpVtbl->GetUIObjectOf(this->psfDesktop,
hwndOwner, cidl, apidl, riid, prgfInOut, ppvOut);
}
}
else
{
// No, it contains at least one non-file system objects.
hres = CDesktop_CreateContextMenu(this->psfRegItems ? this->psfRegItems : psf,
hwndOwner, cidl, apidl, ppvOut);
}
}
else if (IsEqualIID(riid, &IID_IDropTarget) && cidl==1
&& (Desktop_IsReg(apidl[0]) || CDesktop_IsMyComputer(apidl[0]) || CDesktop_IsMyNetwork(apidl[0])))
{
//
// This block of code enable us drag&drop onto an iconized
// MyComputer.
//
LPSHELLFOLDER psfT;
Assert(this->psfRegItems);
hres = this->psfRegItems->lpVtbl->BindToObject(this->psfRegItems,
apidl[0], NULL, &IID_IShellFolder, &psfT);
if (SUCCEEDED(hres))
{
hres = psfT->lpVtbl->CreateViewObject(psfT,
hwndOwner, &IID_IDropTarget, ppvOut);
psfT->lpVtbl->Release(psfT);
}
}
else if (cidl > 0 && IsEqualIID(riid, &IID_IDataObject))
{
UINT i;
USHORT uNullPidl = 0;
LPITEMIDLIST * ppidl;
LPCITEMIDLIST pidlCommonDesktop;
if ((cidl ==1))
{
if (this->psfAltDesktop && FS_IsCommonItem(apidl[0])) {
hres = this->psfAltDesktop->lpVtbl->GetUIObjectOf(this->psfAltDesktop,
hwndOwner, cidl, apidl, riid, prgfInOut, ppvOut);
} else {
hres = this->psfDesktop->lpVtbl->GetUIObjectOf(this->psfDesktop,
hwndOwner, cidl, apidl, riid, prgfInOut, ppvOut);
}
return hres;
}
//
// Allocate space for an array of pidls.
//
ppidl = LocalAlloc(LPTR, sizeof(LPITEMIDLIST) * cidl);
if (!ppidl)
return(hres);
pidlCommonDesktop = GetSpecialFolderIDList(NULL, CSIDL_COMMON_DESKTOPDIRECTORY, FALSE);
for (i=0; i<cidl; i++) {
if (FS_IsCommonItem(apidl[i])) {
ppidl[i] = ILCombine(pidlCommonDesktop, apidl[i]);
} else {
ppidl[i] = ILCombine(&c_idlDesktop, apidl[i]);
}
}
hres = CIDLData_CreateFromIDArray2(&c_CFSIDLDataVtbl,
(LPCITEMIDLIST)&uNullPidl, cidl,
ppidl,
(LPDATAOBJECT FAR*)ppvOut);
//
// now to cleanup what we created.
//
for (i=0; i<cidl; i++) {
ILFree(ppidl[i]);
}
LocalFree (ppidl);
}
else
{
if (this->psfAltDesktop && FS_IsCommonItem(apidl[0])) {
hres = this->psfAltDesktop->lpVtbl->GetUIObjectOf(this->psfAltDesktop,
hwndOwner, cidl, apidl, riid, prgfInOut, ppvOut);
} else {
hres = this->psfDesktop->lpVtbl->GetUIObjectOf(this->psfDesktop,
hwndOwner, cidl, apidl, riid, prgfInOut, ppvOut);
}
}
}
return hres;
}
HRESULT CDesktop_SimpleDisplayName(UINT iRoot, LPSTRRET pStrRet)
{
// No, it contains only one ID
TCHAR szT[MAX_PATH];
UINT cch;
LPOLESTR pwsz;
HRESULT hres;
hres = (E_OUTOFMEMORY);
cch = LoadString(HINST_THISDLL, IDS_ROOTNAMES + iRoot, szT, ARRAYSIZE(szT));
if ( cch && (NULL != (pwsz = SHAlloc((cch + 1) * SIZEOF(WCHAR)))) )
{
StrToOleStr(pwsz, szT);
pStrRet->uType = STRRET_OLESTR;
pStrRet->pOleStr = pwsz; // Client should use SHFree()
hres = NOERROR;
}
return(hres);
}
STDMETHODIMP CDesktop_GetDisplayNameOf(LPSHELLFOLDER psf, LPCITEMIDLIST pidl, DWORD dwReserved,
LPSTRRET pStrRet)
{
PRootOfEvilSF this = IToClassN(RootOfEvilSF, sf, psf);
HRESULT hres=(E_INVALIDARG);
pStrRet->uType=STRRET_CSTR;
//
// Special case for NULL or empty pidl
//
if (pidl==NULL || ILIsEmpty(pidl))
{
UINT cch;
#ifdef UNICODE
TCHAR szName[MAX_PATH];
cch=LoadString(HINST_THISDLL, IDS_DESKTOP, szName, ARRAYSIZE(szName));
pStrRet->pOleStr = cch ? SHAlloc((lstrlen(szName)+1)*SIZEOF(TCHAR)) : NULL;
if (pStrRet->pOleStr == NULL)
{
hres = E_OUTOFMEMORY;
}
else
{
pStrRet->uType=STRRET_OLESTR;
lstrcpy(pStrRet->pOleStr,szName);
hres = NOERROR;
}
#else
cch=LoadString(HINST_THISDLL, IDS_DESKTOP, pStrRet->cStr, ARRAYSIZE(pStrRet->cStr));
hres = cch ? NOERROR : (E_OUTOFMEMORY);
#endif
}
else
{
if (this->psfAltDesktop && FS_IsCommonItem(pidl)) {
hres = this->psfAltDesktop->lpVtbl->GetDisplayNameOf(this->psfAltDesktop, pidl, dwReserved,
pStrRet);
} else {
hres = this->psfDesktop->lpVtbl->GetDisplayNameOf(this->psfDesktop, pidl, dwReserved,
pStrRet);
}
}
return hres;
}
STDMETHODIMP CDesktop_SetNameOf(LPSHELLFOLDER psf, HWND hwndOwner, LPCITEMIDLIST pidl, LPCOLESTR lpszName,
DWORD dwReserved, LPITEMIDLIST * ppidlOut)
{
PRootOfEvilSF this = IToClassN(RootOfEvilSF, sf, psf);
LPSHELLFOLDER psfTemp;
if (this->psfAltDesktop && FS_IsCommonItem(pidl)) {
psfTemp = this->psfAltDesktop;
} else {
psfTemp = this->psfDesktop;
}
return psfTemp->lpVtbl->SetNameOf(psfTemp, hwndOwner,
pidl, lpszName, dwReserved, ppidlOut);
}
//===========================================================================
// CSIShellFolder stuff
//===========================================================================
//
// AddRef
//
ULONG STDMETHODCALLTYPE CSIShellFolder_AddRef(LPSHELLFOLDER psf)
{
return 3;
}
//
// Release
//
ULONG STDMETHODCALLTYPE CSIShellFolder_Release(LPSHELLFOLDER psf)
{
return 2;
}
//===========================================================================
// CDefShellFolder stuff
//===========================================================================
//
// QueryInterface
//
HRESULT STDMETHODCALLTYPE CDefShellFolder_QueryInterface(LPSHELLFOLDER psf, REFIID riid, LPVOID * ppvObj)
{
if (IsEqualIID(riid, &IID_IUnknown)
|| IsEqualIID(riid, &IID_IShellFolder))
{
*ppvObj = psf;
psf->lpVtbl->AddRef(psf);
return NOERROR;
}
*ppvObj = NULL;
return((E_NOINTERFACE));
}
//
// Member: IShellFolder::BindToObject
//
// bugbug, this could be combined with printers and control panels and such
STDMETHODIMP CDefShellFolder_BindToObject(LPSHELLFOLDER psf,
LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, LPVOID * ppvOut)
{
*ppvOut = NULL;
return (E_NOTIMPL);
}
//
// Member: IShellFolder::SetNameOf
//
STDMETHODIMP CDefShellFolder_SetNameOf(LPSHELLFOLDER psf, HWND hwndOwner,
LPCITEMIDLIST pidl, LPCOLESTR lpszName, DWORD dwReserved, LPITEMIDLIST * ppidlOut)
{
return (E_FAIL);
}
STDMETHODIMP CDefShellFolder_BindToStorage(LPSHELLFOLDER psf, LPCITEMIDLIST pidl, LPBC pbc,
REFIID riid, LPVOID *ppvOut)
{
*ppvOut = NULL;
return (E_NOTIMPL);
}
STDMETHODIMP CDefShellFolder_GetAttributesOf(LPSHELLFOLDER psf, UINT cidl, LPCITEMIDLIST * apidl, ULONG * rgfOut)
{
*rgfOut = (SFGAO_HASSUBFOLDER | SFGAO_CANLINK | SFGAO_HASPROPSHEET);
return NOERROR;
}
//
// This function is called from CFSIDLData_GetData().
//
// Paramters:
// this -- Specifies the IDLData object (selected objects)
// pmedium -- Pointer to STDMEDIUM to be filled; NULL if just querying.
//
HRESULT CDesktopIDLData_GetNetResourceForFS(IDataObject *pdtobj, LPSTGMEDIUM pmedium)
{
STGMEDIUM medium;
BOOL bIsMyNet;
LPIDA pida;
pida = DataObj_GetHIDA(pdtobj, &medium);
// Handle failure from GetHIDA...
if (!pida)
return E_OUTOFMEMORY;
bIsMyNet = CDesktop_IsMyNetwork(IDA_GetIDListPtr(pida, (UINT)-1));
HIDA_ReleaseStgMedium(pida, &medium);
if (!bIsMyNet)
return (DV_E_FORMATETC);
if (!pmedium)
return NOERROR; // query, yes we have it
else
return CNETIDLData_GetNetResourceForFS(pdtobj, pmedium);
}
#ifdef DEBUG
void AssertValidIDList(LPCITEMIDLIST pidl)
{
Assert(pidl);
Assert(pidl->mkid.cb < 2048); // reasonable size
Assert(ILGetSize(pidl) < (8 * 1024));// may fault on invalid
}
#else
#define AssertValidIDList(pidl)
#endif
//
// Requires:
// The pidl MUST point a file system object. Otherwise, the result
// is unpredictable.
//
BOOL WINAPI SHGetPathFromIDListEx(LPCITEMIDLIST pidl, LPTSTR pszPath, UINT uOpts)
{
BOOL fSuccess = FALSE;
pszPath[0] = TEXT('\0'); // assume error
if (!pidl) {
Assert(0);
return FALSE;
}
AssertValidIDList(pidl);
if (CDesktop_IsMyComputer(pidl))
{
//
// The first one is the drives root.
//
fSuccess = Drives_GetPathFromIDList(_ILNext(pidl), pszPath, uOpts);
}
else if (CDesktop_IsMyNetwork(pidl))
{
//
// The first one is the network root.
//
fSuccess = NET_GetPathFromIDList(_ILNext(pidl), pszPath, uOpts);
}
else if (Desktop_IsReg(pidl))
{
//
// The first one is a Registry item.
//
const CLSID *pclsid = RegItems_GetClassID(pidl);
// we know how to do bitbucket stuffs
if (!memcmp(pclsid, &CLSID_ShellBitBucket, SIZEOF(CLSID))) {
fSuccess = BBGetPathFromIDList(_ILNext(pidl), pszPath, uOpts);
} else {
fSuccess = FALSE;
}
}
else
{
LPCITEMIDLIST pidlDesk;
ENTERCRITICAL;
if (FS_IsCommonItem(pidl)) {
pidlDesk = GetSpecialFolderIDList(NULL, CSIDL_COMMON_DESKTOPDIRECTORY, FALSE);
} else {
pidlDesk = GetSpecialFolderIDList(NULL, CSIDL_DESKTOPDIRECTORY, FALSE);
}
if (pidlDesk &&
// make sure we don't recurse!
(CDesktop_IsMyComputer(pidlDesk) || CDesktop_IsMyNetwork(pidlDesk)))
{
LPITEMIDLIST pidlFull = ILCombine(pidlDesk, pidl);
if (pidlFull)
{
fSuccess = SHGetPathFromIDListEx(pidlFull, pszPath, uOpts);
ILFree(pidlFull);
}
}
LEAVECRITICAL;
}
return fSuccess;
}
BOOL WINAPI SHGetPathFromIDList(LPCITEMIDLIST pidl, LPTSTR pszPath)
{
//
// We simply calls EX function with uOpts = 0 ie no special options.
//
return SHGetPathFromIDListEx(pidl, pszPath, 0);
}
#ifdef UNICODE
BOOL WINAPI SHGetPathFromIDListA(LPCITEMIDLIST pidl, LPSTR pszPath)
{
BOOL fDefUsed;
WCHAR wszPath[MAX_PATH];
pszPath[0] = '\0'; // Assume error
if (SHGetPathFromIDListW(pidl, wszPath))
{
// Thunk the output result string back to ANSI. If the conversion fails,
// or if the default char is used, we fail the API call.
// BUGBUG (DavePl) Mapped chars could still make the path useless.
if (0 == WideCharToMultiByte(CP_ACP,
WC_COMPOSITECHECK | WC_DEFAULTCHAR,
wszPath,
-1,
pszPath,
MAX_PATH,
"_",
&fDefUsed) || fDefUsed)
{
return FALSE; // BUGBUG (DavePl) Note failure only due to text thunking
}
return TRUE;
}
return FALSE;
}
#else
BOOL WINAPI SHGetPathFromIDListW(LPCITEMIDLIST pidl, LPWSTR pszPath)
{
return FALSE; // BUGBUG - BobDay - We should move this into SHUNIMP.C
}
#endif
//
// This creates an IDList without hitting the disk
// If you want a "full" IDList, use ILCreateFromPath
//
LPITEMIDLIST WINAPI SHSimpleIDListFromPath(LPCTSTR pszPath)
{
LPITEMIDLIST pidl, pidlRight, pidlLeft;
HRESULT hres;
// First let Drives try to recognize the string
hres = Drives_SimpleIDListFromPath(pszPath, &pidlRight);
if (SUCCEEDED(hres))
{
pidlLeft = CDesktop_CreateRegID(CDESKTOP_REGITEM_DRIVES);
if (!pidlLeft)
{
goto Error1;
}
pidl = ILAppendID(pidlRight, &pidlLeft->mkid, FALSE);
ILFree(pidlLeft);
if (!pidl)
{
Error1:;
ILFree(pidlRight);
return(NULL);
}
return(pidl);
}
// Check whether the string was just not recognized by Drives
if (GetScode(hres) != E_INVALIDARG)
{
return(NULL);
}
// Try again with World
hres = World_SimpleIDListFromPath(pszPath, &pidlRight);
if (SUCCEEDED(hres))
{
pidlLeft = CDesktop_CreateRegID(CDESKTOP_REGITEM_NETWORK);
if (!pidlLeft)
{
goto Error2;
}
pidl = ILAppendID(pidlRight, &pidlLeft->mkid, FALSE);
ILFree(pidlLeft);
if (!pidl)
{
Error2:;
ILFree(pidlRight);
return(NULL);
}
return(pidl);
}
// The string was not recognized
return(NULL);
}
LPITEMIDLIST WINAPI SHLogILFromFSIL(LPCITEMIDLIST pidlFS)
{
LPCITEMIDLIST pidlDesktop;
BOOL fEmpty;
// Check if this is under the "drives"
if (!CDesktop_IsMyComputer(pidlFS))
{
return(NULL);
}
ENTERCRITICAL;
pidlDesktop = _ILNext(GetSpecialFolderIDList(NULL, CSIDL_DESKTOPDIRECTORY, TRUE));
pidlFS = _ILNext(pidlFS);
Drives_CommonPrefix(&pidlDesktop, &pidlFS);
fEmpty = ILIsEmpty(pidlDesktop);
LEAVECRITICAL;
if (fEmpty)
{
// The desktop is an ancestor of the FS object
return(ILCombine((LPCITEMIDLIST)&c_idlDesktop, pidlFS));
}
return(NULL);
}
// if something under the desktop directory moves, generate
// a parallel event under the desktop.
void CDesktop_FSEvent(LONG lEvent, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlExtra)
{
LPCITEMIDLIST pidl2;
LPCITEMIDLIST pidlExtra2;
LPITEMIDLIST pidl2Temp = NULL, pidlExtra2Temp = NULL;
BOOL fResend = FALSE;
LPCITEMIDLIST pidfDesktop, pidfCommonDesktop;
if (lEvent & (SHCNE_DISKEVENTS | SHCNE_NETSHARE | SHCNE_NETUNSHARE))
{
ENTERCRITICAL;
pidfDesktop = GetSpecialFolderIDList(NULL, CSIDL_DESKTOPDIRECTORY, TRUE);
pidfCommonDesktop = GetSpecialFolderIDList(NULL, CSIDL_COMMON_DESKTOPDIRECTORY, TRUE);
if (pidfDesktop && pidfCommonDesktop) {
if (NULL != (pidl2 = ILFindChild(pidfDesktop, pidl))) {
fResend = TRUE;
} else if (NULL != (pidl2 = ILFindChild(pidfCommonDesktop, pidl))) {
fResend = TRUE;
if (!ILIsEmpty(pidl2)) {
pidl2Temp = ILClone (pidl2);
if (pidl2Temp) {
pidl2Temp->mkid.abID[0] |= SHID_FS_COMMONITEM;
pidl2 = pidl2Temp;
}
}
} else
pidl2 = (LPITEMIDLIST)pidl;
if (pidlExtra && (NULL != (pidlExtra2 = ILFindChild(pidfDesktop, pidlExtra)))) {
fResend = TRUE;
} else if (pidlExtra && (NULL != (pidlExtra2 = ILFindChild(pidfCommonDesktop, pidlExtra)))) {
fResend = TRUE;
if (!ILIsEmpty(pidlExtra2)) {
pidlExtra2Temp = ILClone (pidlExtra2);
if (pidlExtra2Temp) {
pidlExtra2Temp->mkid.abID[0] |= SHID_FS_COMMONITEM;
pidlExtra2 = pidlExtra2Temp;
}
}
} else
pidlExtra2 = (LPITEMIDLIST)pidlExtra;
}
LEAVECRITICAL;
if (fResend) {
SHChangeNotifyReceive(lEvent, SHCNF_NONOTIFYINTERNALS | SHCNF_IDLIST, pidl2, pidlExtra2);
}
if (pidl2Temp) {
ILFree (pidl2Temp);
}
if (pidlExtra2Temp) {
ILFree (pidlExtra2Temp);
}
}
}
//
// This function converts a simple PIDL to a real PIDL.
//
HRESULT WINAPI SHGetRealIDL(LPSHELLFOLDER psf, LPCITEMIDLIST pidlSimple, LPITEMIDLIST * ppidlReal)
{
HRESULT hres;
LPSHELLFOLDER psfAlt = NULL;
*ppidlReal = NULL;
Assert(pidlSimple && !ILIsEmpty(pidlSimple) && ILIsEmpty(_ILNext(pidlSimple)));
// Note this returns psf if not a RegItems ShellFolder
psf = RegItems_GetInnerShellFolder(psf);
if (psf->lpVtbl == &c_RootOfEvilSFVtbl && !Desktop_IsReg(pidlSimple))
{
PRootOfEvilSF this = IToClassN(RootOfEvilSF, sf, psf);
psf = this->psfDesktop;
psfAlt = this->psfAltDesktop;
}
else if ((psf->lpVtbl == &c_NetRootVtbl) && FS_IsValidID(pidlSimple))
{
// This case is for links in NetHood, so FS_IsValidID is the correct
// check
psf = CNetRoot_GetPSF(NULL);
}
if (psfAlt && FS_IsCommonItem(pidlSimple)) {
hres = FS_GetRealIDL(psfAlt, pidlSimple, ppidlReal);
if (SUCCEEDED(hres)) {
(*ppidlReal)->mkid.abID[0] |= SHID_FS_COMMONITEM;
}
} else {
hres = FS_GetRealIDL(psf, pidlSimple, ppidlReal);
}
if (hres == E_INVALIDARG)
hres = Drives_GetRealIDL(psf, pidlSimple, ppidlReal);
if (hres == E_INVALIDARG)
{
*ppidlReal = ILClone(pidlSimple);
hres = (*ppidlReal == NULL) ? E_OUTOFMEMORY : S_OK;
}
return hres;
}
#if 0
STRRET str;
TCHAR pszPath[MAX_PATH];
WCHAR wszPath[MAX_PATH];
ULONG pcchEaten;
hresTemp = psf->lpVtbl->GetDisplayNameOf(psf, pidlSimple,
SHGDN_FORPARSING|SHGDN_INFOLDER, &str);
if (SUCCEEDED(hres))
{
StrRetToStrN(pszPath, MAX_PATH, &str, pidlSimple);
StrToOleStrN(wszPath, ARRAYSIZE(wszPath), pszPath, -1);
hres = psf->lpVtbl->ParseDisplayName(psf, (HWND)NULL, (LPBC)NULL,
wszPath, &pcchEaten, ppidlReal, NULL);
}
if (FAILED(hres))
#endif
HRESULT RegItems_GetName(LPCREGITEMSINFO lpInfo, LPCITEMIDLIST pidl, LPSTRRET pStrRet);
HRESULT CDesktop_GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, LPSHELLDETAILS lpDetails)
{
HRESULT hres;
LPSTRRET pStrRet = &lpDetails->str;
// Only show information in the name column and type for special roots
if (iColumn == 0)
{
hres = RegItems_GetName(&g_sDesktopRegInfo, pidl,
pStrRet);;
}
else if (iColumn == FS_ICOL_TYPE)
{
#ifdef UNICODE
TCHAR szName[MAX_PATH];
pStrRet->uType = STRRET_OLESTR;
LoadString(HINST_THISDLL, IDS_DRIVES_REGITEM,
szName, ARRAYSIZE(szName));
pStrRet->pOleStr = SHAlloc((lstrlen(szName)+1)*SIZEOF(TCHAR));
if (pStrRet->pOleStr == NULL)
{
hres = E_OUTOFMEMORY;
}
else
{
lstrcpy(pStrRet->pOleStr,szName);
hres = NOERROR;
}
#else
pStrRet->uType = STRRET_CSTR;
*pStrRet->cStr = TEXT('\0');
LoadString(HINST_THISDLL, IDS_DRIVES_REGITEM,
pStrRet->cStr, ARRAYSIZE(pStrRet->cStr));
hres = NOERROR;
#endif
} else {
hres = (E_NOTIMPL);
}
return(hres);
}