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.
 
 
 
 
 
 

2257 lines
59 KiB

#include "cabinet.h"
#include "rcids.h"
#include <shguidp.h>
//#define OT_DEBUG
#include "onetree.h"
#ifdef DEBUG
UINT g_cTotalOTBindToFolder = 0;
UINT g_cHitOTBindToFolder = 0;
#define DEBUG_INCL_TOTAL g_cTotalOTBindToFolder++;
#define DEBUG_INCL_HIT g_cHitOTBindToFolder++;
#else
#define DEBUG_INCL_TOTAL
#define DEBUG_INCL_HIT
#endif
// We want to use the real ILIsParent in this file
#undef ILIsParent
// these do no checking.
// just convenience macros
//
#define GetNthKid(lpnParent, i) ((LPOneTreeNode)DPA_GetPtr(lpnParent->hdpaKids, i))
#define GetKidCount(lpnParent) DPA_GetPtrCount(lpnParent->hdpaKids)
#define NodeHasKids(lpNode) ((lpNode)->hdpaKids != KIDSUNKNOWN && (lpNode)->hdpaKids != NOKIDS)
int g_nDefOpenSysIndex = -1;
int g_nDefNormalSysIndex = -1;
#define ADDCHILD_FAILED 0 // error, didn't add it
#define ADDCHILD_ADDED 1 // didn't find it, so we added it.
#define ADDCHILD_EXISTED 2 // didn't add, it already existed.
UINT AddChild(IShellFolder *lpsfParent, LPOneTreeNode lpnParent,
LPCITEMIDLIST pidl, BOOL bAllowDup,
LPOneTreeNode *lplpnKid);
#ifdef DEBUG
void OTValidate(LPOneTreeNode lpNode);
#else
#define OTValidate(lpn)
#endif
//
// BUGBUG: remove the below once SFGAO_BROWSABLE is defined
// in the header files (post NASHVILLE merge).
#ifndef SFGAO_BROWSABLE
#define SFGAO_BROWSABLE 0
#endif
LPOneTreeNode s_lpnRoot = NULL;
LPSHELLFOLDER s_pshfRoot = NULL;
LPSHELLFOLDER s_pshfRootParent = NULL;
LPCITEMIDLIST s_pidlRoot = NULL;
BOOL s_bDesktopRoot = FALSE;
HDPA s_hdpaAppHwnds = NULL;
ULONG s_uFSRegisterID = 0;
HWND s_hwndOT = NULL;
#pragma data_seg(".text", "CODE")
const TCHAR c_szOTClass[] = TEXT("OTClass");
#pragma data_seg()
void _OTGetImageIndex(LPSHELLFOLDER psfParent, LPOneTreeNode lpNode);
void HandleFileSysChange(LONG lNotification, LPITEMIDLIST*lplpidl);
BOOL SearchForKids(HWND hwndOwner, LPOneTreeNode lpnParent, PFileCabinet pfc, BOOL fInteractive);
LRESULT CALLBACK _export OneTreeWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
LPOneTreeNode _GetNodeFromIDList(LPCITEMIDLIST pidl, UINT uFlags, HRESULT * phresOut);
void InvalidateImageIndices();
#define WM_OT_FSNOTIFY (WM_USER + 11)
#ifdef DEBUG
void OTValidate(LPOneTreeNode lpNode)
{
if (lpNode) {
Assert(lpNode->dwDebugSig == OTDEBUG_SIG);
Assert(lpNode->cRef > 0);
}
}
#endif
//
// OneTree DPA_Search comparison function for LPOneTreeNodes
//
INT CALLBACK OTCompareNodes( LPOneTreeNode lpn1, LPOneTreeNode lpn2, LPOTCompareInfo lpinfo )
{
HRESULT hres;
//
// Compare the two LPOneTreeNodes
//
hres = lpinfo->psf->lpVtbl->CompareIDs( lpinfo->psf,
0,
OTGetFolderID(lpn1),
OTGetFolderID(lpn2)
);
lpinfo->bFound |= ((INT)ShortFromResult(hres) == 0);
return (INT)ShortFromResult(hres);
}
//
// OneTree DPA_Search comparison function for LPOneTreeNodes
//
INT CALLBACK OTCompareIDs( LPITEMIDLIST pidl, LPOneTreeNode lpn, LPOTCompareInfo lpinfo )
{
HRESULT hres;
//
// Compare the two LPOneTreeNodes
//
hres = lpinfo->psf->lpVtbl->CompareIDs( lpinfo->psf,
0,
pidl,
OTGetFolderID(lpn)
);
lpinfo->bFound |= ((INT)ShortFromResult(hres) == 0);
return (INT)ShortFromResult(hres);
}
LRESULT CALLBACK _export OneTreeWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_OT_FSNOTIFY:
if (s_hdpaAppHwnds)
{
LPSHChangeNotificationLock pshcnl;
LPITEMIDLIST *ppidl;
LONG lEvent;
pshcnl = SHChangeNotification_Lock((HANDLE)wParam, (DWORD)lParam, &ppidl, &lEvent);
if (pshcnl)
{
HandleFileSysChange(lEvent, ppidl);
SHChangeNotification_Unlock(pshcnl);
}
}
break;
// debug only. someone is killing this window and
// I wanna know who it is!
case WM_DESTROY:
// null only in the process of shutting down everything
if (s_hwndOT) {
DebugMsg(DM_ERROR, TEXT("***OneTree Window: Someone's murdering us!**"));
s_hwndOT = NULL;
SHChangeNotifyDeregister(s_uFSRegisterID);
if (s_hdpaAppHwnds) {
HDPA hdpa = s_hdpaAppHwnds;
s_hdpaAppHwnds = NULL;
DPA_Destroy(hdpa);
}
}
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0L;
}
void OneTree_Terminate()
{
if (IsWindow(s_hwndOT))
DestroyWindow(s_hwndOT);
s_hwndOT = NULL;
s_hdpaAppHwnds = NULL;
s_lpnRoot = NULL;
OTRelease(s_lpnRoot);
if (s_pidlRoot)
{
SHFree((LPITEMIDLIST)s_pidlRoot);
s_pidlRoot = NULL;
}
if (s_pshfRoot)
{
IUnknown_Release(s_pshfRoot);
s_pshfRoot = NULL;
}
if (s_pshfRootParent)
{
IUnknown_Release(s_pshfRootParent);
s_pshfRootParent = NULL;
}
}
#define GUIDSTR_MAX (1+ 8 + 1 + 4 + 1 + 4 + 1 + 4 + 1 + 12 + 1 + 1)
//----------------------------------------------------------------------------
// converts GUID into (...) form without leading identifier; returns
// amount of data copied to lpsz if successful; 0 if buffer too small.
STDAPI_(int) StringFromGUID2A(REFGUID rguid, LPTSTR lpsz, int cbMax)
{
if (cbMax < GUIDSTR_MAX)
return 0;
#pragma data_seg(".text", "CODE")
wsprintf(lpsz, TEXT("{%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}"),
rguid->Data1, rguid->Data2, rguid->Data3,
rguid->Data4[0], rguid->Data4[1],
rguid->Data4[2], rguid->Data4[3],
rguid->Data4[4], rguid->Data4[5],
rguid->Data4[6], rguid->Data4[7]);
#pragma data_seg()
Assert(lstrlen(lpsz) + 1 == GUIDSTR_MAX);
return GUIDSTR_MAX;
}
//----------------------------------------------------------------------------
BOOL GetTextFromCLSID(const CLSID *pclsid, LPTSTR pszText, int cchText)
{
HKEY hkeyNew;
BOOL fRet = FALSE;
DWORD dwType;
TCHAR szCLSID[GUIDSTR_MAX+6];
DWORD cbText = cchText * sizeof(TCHAR);
VDATEINPUTBUF(pszText, TCHAR, cchText);
#pragma data_seg(DATASEG_READONLY)
lstrcpy(szCLSID, TEXT("CLSID\\"));
#pragma data_seg()
StringFromGUID2A(pclsid, szCLSID+6, ARRAYSIZE(szCLSID)-6);
if (!GetSystemMetrics(SM_CLEANBOOT) && (RegOpenKey(HKEY_CLASSES_ROOT, szCLSID, &hkeyNew) == ERROR_SUCCESS))
{
if (RegQueryValueEx(hkeyNew, NULL, 0, &dwType, (LPBYTE) pszText, &cbText) == ERROR_SUCCESS)
{
fRet = TRUE;
}
RegCloseKey(hkeyNew);
}
return fRet;
}
//----------------------------------------------------------------------------
int MapCLSIDToSystemImageListIndex(const CLSID *pclsid)
{
HKEY hkeyNew;
DWORD dwType;
TCHAR sz[MAX_PATH];
TCHAR szDefIcon[MAX_PATH];
DWORD cbDefIcon = SIZEOF(szDefIcon);
int i = 0;
#pragma data_seg(DATASEG_READONLY)
lstrcpy(sz, TEXT("CLSID\\"));
StringFromGUID2A(pclsid, sz+6, ARRAYSIZE(sz)-6);
lstrcat(sz, TEXT("\\DefaultIcon"));
#pragma data_seg()
if (!GetSystemMetrics(SM_CLEANBOOT) && (RegOpenKey(HKEY_CLASSES_ROOT, sz, &hkeyNew) == ERROR_SUCCESS))
{
if (RegQueryValueEx(hkeyNew, NULL, 0, &dwType, (LPBYTE) szDefIcon, &cbDefIcon) == ERROR_SUCCESS)
{
i = PathParseIconLocation(szDefIcon);
i = Shell_GetCachedImageIndex(szDefIcon, i, 0);
return i;
}
RegCloseKey(hkeyNew);
}
return 0;
}
UINT AddChildEx(IShellFolder *lpsfParent, LPOneTreeNode lpnParent,
LPCITEMIDLIST pidl, BOOL bAllowDup,
LPOneTreeNode *lplpnKid, LPCTSTR pszText);
BOOL OneTree_Initialize(const CLSID *pclsid, LPCITEMIDLIST pidlRoot)
{
WNDCLASS wndclass ;
SHChangeNotifyEntry fsne = { NULL, TRUE }; // Global & recursive.
IShellFolder *psfDesktop;
int iSelectedImage;
if (!SFCInitialize())
return FALSE;
// dummy window for the FSNotify
wndclass.style = 0;
wndclass.lpfnWndProc = OneTreeWndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hinstCabinet;
wndclass.hIcon = NULL;
wndclass.hCursor = NULL;
wndclass.hbrBackground = NULL;
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = c_szOTClass;
if (!RegisterClass (&wndclass))
return FALSE;
s_hdpaAppHwnds = NULL;
s_hwndOT = CreateWindow (c_szOTClass, c_szNULL,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hinstCabinet, NULL) ;
if (!s_hwndOT) {
DebugMsg(DM_ERROR, TEXT("OneTree: failed to create window"));
return FALSE;
}
if (FAILED(ICoCreateInstance(&CLSID_ShellDesktop, &IID_IShellFolder,
&psfDesktop)))
{
DebugMsg(DM_ERROR, TEXT("OneTree: failed to bind to Desktop root"));
return(FALSE);
}
// get the root
if (pidlRoot)
{
LPITEMIDLIST pidlLast;
// Actually, I just need any error value
HRESULT hres = (E_OUTOFMEMORY);
// I want the "global" CreateFromPath, not the Cabinet version
s_pidlRoot = ILClone(pidlRoot);
if (!s_pidlRoot)
{
DebugMsg(DM_ERROR, TEXT("OneTree: failed to get root IDList"));
goto Error0;
}
pidlLast = ILFindLastID(s_pidlRoot);
if (pidlLast == s_pidlRoot)
{
s_pshfRootParent = psfDesktop;
IUnknown_AddRef(s_pshfRootParent);
}
else
{
WORD cbSave;
cbSave = pidlLast->mkid.cb;
pidlLast->mkid.cb = 0;
if (FAILED(psfDesktop->lpVtbl->BindToObject(psfDesktop, s_pidlRoot, NULL,
&IID_IShellFolder, &s_pshfRootParent)))
{
goto Error0;
}
pidlLast->mkid.cb = cbSave;
}
if (pclsid)
{
IPersistFolder *ppf;
TCHAR szText[MAX_PATH];
GetTextFromCLSID(pclsid, szText, ARRAYSIZE(szText));
if (AddChildEx(s_pshfRootParent, NULL, pidlLast, FALSE, &s_lpnRoot, szText)
!= ADDCHILD_ADDED)
{
goto Error0;
}
#ifdef DEBUG
s_lpnRoot->dwDebugSig = OTDEBUG_SIG;
#endif
s_lpnRoot->iImage = MapCLSIDToSystemImageListIndex(pclsid);
s_lpnRoot->iSelectedImage = s_lpnRoot->iImage;
OTAddRef(s_lpnRoot);
if (FAILED(ICoCreateInstance(pclsid, &IID_IShellFolder, &s_pshfRoot)))
{
DebugMsg(DM_ERROR, TEXT("OneTree: failed to get IShellFolder"));
goto Error0;
}
if (FAILED(IUnknown_QueryInterface(s_pshfRoot, &IID_IPersistFolder,
&ppf)))
{
DebugMsg(DM_ERROR, TEXT("OneTree: failed to get IPersistFolder"));
goto Error0;
}
hres = ppf->lpVtbl->Initialize(ppf, s_pidlRoot);
IUnknown_Release(ppf);
}
else
{
if (AddChild(s_pshfRootParent, NULL, pidlLast, FALSE, &s_lpnRoot)
!= ADDCHILD_ADDED)
{
goto Error0;
}
s_lpnRoot->iImage = SHMapPIDLToSystemImageListIndex(s_pshfRootParent,
pidlLast, &iSelectedImage);
s_lpnRoot->iSelectedImage = iSelectedImage;
OTAddRef(s_lpnRoot);
hres = s_pshfRootParent->lpVtbl->BindToObject(s_pshfRootParent,
pidlLast, NULL, &IID_IShellFolder, &s_pshfRoot);
}
if (FAILED(hres))
{
Error0:;
IUnknown_Release(psfDesktop);
OneTree_Terminate();
return(FALSE);
}
IUnknown_Release(psfDesktop);
}
else
{
ITEMIDLIST idl = { 0 } ;
if (AddChild(NULL, NULL, &idl, FALSE, &s_lpnRoot) != ADDCHILD_ADDED)
{
goto Error0;
}
s_lpnRoot->iImage = SHMapPIDLToSystemImageListIndex(psfDesktop,
&idl, &iSelectedImage);
s_lpnRoot->iSelectedImage = iSelectedImage;
s_pshfRoot = psfDesktop;
s_bDesktopRoot = TRUE;
}
s_lpnRoot->dwAttribs = SFGAO_HASSUBFOLDER | SFGAO_SHARE | SFGAO_REMOVABLE |SFGAO_FILESYSANCESTOR | SFGAO_FILESYSTEM | SFGAO_CAPABILITYMASK | SFGAO_COMPRESSED;
s_pshfRoot->lpVtbl->GetAttributesOf(s_pshfRoot, 0, NULL, &s_lpnRoot->dwAttribs);
s_lpnRoot->dwAttribs &= ~(SFGAO_CANRENAME);
s_lpnRoot->cChildren = 1;
s_lpnRoot->hdpaKids = KIDSUNKNOWN;
fsne.pidl = s_pidlRoot;
s_uFSRegisterID = SHChangeNotifyRegister(s_hwndOT,
SHCNRF_NewDelivery | SHCNRF_ShellLevel | SHCNRF_InterruptLevel,
SHCNE_ALLEVENTS & ~(SHCNE_FREESPACE|SHCNE_CREATE|SHCNE_DELETE|SHCNE_RENAMEITEM),
WM_OT_FSNOTIFY,
1, &fsne);
return TRUE;
}
LPITEMIDLIST OTCloneFolderID(LPOneTreeNode lpn)
{
LPITEMIDLIST pidl = NULL;
ENTERCRITICAL;
if (lpn) {
pidl = ILClone(lpn->pidl);
}
LEAVECRITICAL;
return pidl;
}
LPTSTR OTGetNodeName(LPOneTreeNode lpn, LPTSTR pszText, int cch)
{
pszText[0] = 0;
ENTERCRITICAL;
if (lpn->lpText && lpn->lpText != LPSTR_TEXTCALLBACK) {
Assert(pszText);
lstrcpyn(pszText, lpn->lpText, cch);
}
LEAVECRITICAL;
return pszText;
}
BOOL OTILIsEqual(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
{
HRESULT hres;
// Special-case the root
if (ILIsEmpty(pidl1) && ILIsEmpty(pidl2))
{
return(TRUE);
}
hres = s_pshfRoot->lpVtbl->CompareIDs(s_pshfRoot, 0, pidl1, pidl2);
return (hres==ResultFromShort(0));
}
BOOL OTILIsParent(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, BOOL fImmediate)
{
if (!s_bDesktopRoot)
{
// When this was written we could never get here since
// ILIsParent was only used for the FSNotify stuff, plus this
// would be way too annoying to rewrite here
Assert(FALSE);
return(FALSE);
}
return ILIsParent(pidl1, pidl2, fImmediate);
}
HRESULT OTILCreateFromPath(LPCTSTR pszPath, LPITEMIDLIST *ppidl, DWORD *rgfInOut)
{
WCHAR wszPath[MAX_PATH];
ULONG pcchEaten;
if (!pszPath || !*pszPath)
{
// We must be referencing the root
ITEMIDLIST idl = { 0 };
*ppidl = ILClone(&idl);
return(*ppidl ? S_OK : (E_OUTOFMEMORY));
}
StrToOleStrN(wszPath, ARRAYSIZE(wszPath), pszPath, -1);
return(s_pshfRoot->lpVtbl->ParseDisplayName(s_pshfRoot,
NULL, NULL, wszPath, &pcchEaten, ppidl, rgfInOut));
}
LPITEMIDLIST OTPIDLFromPath(LPCTSTR pszPath)
{
LPITEMIDLIST pidl;
if (SUCCEEDED(OTILCreateFromPath(pszPath, &pidl, NULL)))
{
return(pidl);
}
return(NULL);
}
BOOL OTTranslateIDList(LPCITEMIDLIST pidl, LPITEMIDLIST *ppidlLog)
{
LPITEMIDLIST pidlLog = NULL;
// Some code depends on this being NULL on failure
*ppidlLog = NULL;
if (s_bDesktopRoot)
{
// We no longer translate IDLists, since we get a "pre-translated"
// IDList from ShellExecute. This allows us to browse into
// C:\WINDOWS\DESKTOP separately from the "logical" Desktop
// pidlLog = SHLogILFromFSIL(pidl);
pidlLog = ILClone(pidl);
}
else
{
LPCITEMIDLIST pidlRoot;
if (!ILIsParent(s_pidlRoot, pidl, FALSE))
{
// These guy are not in the same space, so can't open
// it
return(FALSE);
}
// Skip past the root pidl
for (pidlRoot=s_pidlRoot; !ILIsEmpty(pidlRoot);
pidlRoot=_ILNext(pidlRoot))
{
pidl = _ILNext(pidl);
}
pidlLog = ILClone(pidl);
}
*ppidlLog = pidlLog;
return(pidlLog != NULL);
}
BOOL OTIsDesktopRoot(void)
{
return(s_bDesktopRoot);
}
void KillKids(LPOneTreeNode lpNode)
{
//
// free this kid and all its decendants
//
if (NodeHasKids(lpNode))
{
int i;
HDPA hdpaKids = lpNode->hdpaKids;
// null out the hdpaKids var first because debug version
// make sure that the parent doesn't have a pointe to us (above)
lpNode->hdpaKids = KIDSUNKNOWN;
for( i = DPA_GetPtrCount(hdpaKids) - 1; i >= 0 ; i--) {
OTRelease((LPOneTreeNode)DPA_GetPtr(hdpaKids, i));
}
DPA_Destroy(hdpaKids);
}
}
// returns TRUE if the lpText points somewhere in the pidl
BOOL IsPtrInPidl(LPBYTE p, LPITEMIDLIST pidl)
{
return (p && (p > (LPBYTE)pidl) && (p <= (((LPBYTE)pidl) + pidl->mkid.cb)));
}
void OTSetNodeName(LPOneTreeNode lpNode, LPTSTR pszName)
{
if (lpNode->lpText) {
if (!IsPtrInPidl((LPBYTE)lpNode->lpText, lpNode->pidl)) {
LocalFree(lpNode->lpText);
}
}
lpNode->lpText = pszName;
}
void OTFreeNodeData(LPOneTreeNode lpNode)
{
ENTERCRITICAL;
OTSetNodeName(lpNode, NULL);
if (lpNode->pidl) {
ILFree(lpNode->pidl);
lpNode->pidl = NULL;
}
LEAVECRITICAL;
}
void KillNode(LPOneTreeNode lpNode)
{
OTValidate(lpNode);
//
// I'm disabling this debug code since we'll hit GPF when the parent
// node is already freed.
//
#if 0
#ifdef DEBUG
// make sure our parent doesn't have a pointer to us. This will help
// (but not ensure) that we're not doing extra OTReleases.
{
LPOneTreeNode lpnParent = lpNode->lpnParent;
int i;
if (lpnParent && NodeHasKids(lpnParent)) {
i = GetKidCount(lpnParent);
while( i--) {
// this is REALLY REALLY bad if this assert fails.
// let chee know.
Assert(GetNthKid(lpnParent, i) != lpNode);
}
}
}
#endif
#endif
KillKids(lpNode);
SFCFreeNode(lpNode);
OTFreeNodeData(lpNode);
LocalFree(lpNode);
}
void OTRelease(LPOneTreeNode lpNode)
{
OTValidate(lpNode);
if (lpNode) {
Assert(lpNode->cRef > 0);
Assert((lpNode->cRef > 1) || (lpNode != s_lpnRoot));
// this should NEVER happen.. but just in case,
// don't free out the s_lpnRoot while it's still
// in our global
if ((lpNode->cRef == 1) && (lpNode == s_lpnRoot))
return;
lpNode->cRef--;
if (lpNode->cRef == 0)
KillNode(lpNode);
}
}
LPOneTreeNode OTGetParent(LPOneTreeNode lpNode)
{
OTValidate(lpNode);
// sanity check
if (!lpNode)
return NULL;
if (lpNode->lpnParent)
OTAddRef(lpNode->lpnParent);
return lpNode->lpnParent;
}
void DoInvalidateAll(LPOneTreeNode lpNode, int iImage)
{
int i;
LPOneTreeNode lpnKid ;
if (lpNode == NULL)
return;
OTValidate(lpNode); // this validate just validates that it's a node...
if (iImage == -1 ||
iImage == lpNode->iImage ||
iImage == lpNode->iSelectedImage) {
OTInvalidateNode(lpNode);
////////DebugMsg(DM_TRACE, "DoInvalidateAll, found one to invalidate");
}
if (NodeHasKids(lpNode)) {
for (i = GetKidCount(lpNode) - 1; i >= 0; i--) {
lpnKid = GetNthKid(lpNode, i);
if (lpnKid) {
DoInvalidateAll(lpnKid, iImage);
}
}
}
}
//
// traverses the tree starting from lpNode and tries to Release cached
// IShellFolder.
//
void OTSweepFolders(LPOneTreeNode lpNode)
{
if (NodeHasKids(lpNode)) {
int i;
Assert(lpNode);
OTValidate(lpNode);
for (i = GetKidCount(lpNode) - 1; i >= 0; i--)
{
LPOneTreeNode lpnKid = GetNthKid(lpNode, i);
if (lpnKid)
{
OTSweepFolders(lpnKid);
SFCFreeNode(lpnKid);
}
}
}
}
void OTActivate()
{
// BUGBUG: This should never happen.. but just in case
if (!s_hwndOT)
{
SHChangeNotifyEntry fsne = { s_pidlRoot, TRUE }; // Global & recursive.
DebugMsg(DM_ERROR, TEXT("OneTree Window got killed... recovering..."));
s_hwndOT = CreateWindow (c_szOTClass, c_szNULL,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hinstCabinet, NULL) ;
s_uFSRegisterID = SHChangeNotifyRegister(s_hwndOT,
SHCNRF_NewDelivery | SHCNRF_ShellLevel | SHCNRF_InterruptLevel,
SHCNE_ALLEVENTS & ~(SHCNE_CREATE|SHCNE_DELETE),
WM_OT_FSNOTIFY,
1, &fsne);
}
}
LPOneTreeNode FindKid(LPSHELLFOLDER psfParent, LPOneTreeNode lpnParent, LPCITEMIDLIST pidlKid, BOOL bValidate, INT * pPos)
{
HRESULT hres;
Assert(psfParent);
// there can be only one...
Assert(ILIsEmpty(_ILNext(pidlKid)));
*pPos = 0;
if (lpnParent->hdpaKids == KIDSUNKNOWN || lpnParent->fInvalid) {
if (bValidate)
SearchForKids(NULL, lpnParent, NULL, FALSE);
}
//
// since we might not have validated, we might still have kidsunknow.
//
if ((lpnParent->hdpaKids != NOKIDS) && (lpnParent->hdpaKids != KIDSUNKNOWN)) {
OTCompareInfo info;
info.psf = psfParent;
info.bFound = FALSE;
*pPos = DPA_Search( lpnParent->hdpaKids,
(LPVOID)pidlKid,
0,
(PFNDPACOMPARE)OTCompareIDs,
(LPARAM)&info,
DPAS_SORTED | DPAS_INSERTAFTER
);
if (info.bFound)
{
return (LPOneTreeNode)DPA_FastGetPtr( lpnParent->hdpaKids, *pPos );
}
}
return NULL;
}
// lplpnKidAdded -- this is in/out. it holds the kid to be added coming in..
// it holds the kid added (or found) coming out
void AdoptKid(LPSHELLFOLDER lpsfParent, LPOneTreeNode* lplpnKidAdded, int iPos, DWORD dwLastChecked)
{
LPOneTreeNode lpnFind;
LPOneTreeNode lpnNewKid = *lplpnKidAdded;
LPOneTreeNode lpnParent = lpnNewKid->lpnParent;
ENTERCRITICAL;
if (lpnParent->hdpaKids == KIDSUNKNOWN) {
OTInvalidateNode(lpnParent);
lpnParent->hdpaKids = NOKIDS;
}
if (lpnParent->hdpaKids == NOKIDS)
{
lpnParent->hdpaKids = DPA_Create(0);
iPos = 0;
}
//
// has anyone added anything to the DPA since
// we did the initial find?
if ((iPos != -1 && dwLastChecked == lpnParent->dwLastChanged) ||
!(lpnFind = FindKid( lpsfParent, lpnParent, lpnNewKid->pidl, FALSE, &iPos))) {
OTAddRef(lpnNewKid);
lpnParent->dwLastChanged = GetTickCount();
DPA_InsertPtr( lpnParent->hdpaKids, iPos, lpnNewKid );
}
else
{
*lplpnKidAdded = lpnFind;
}
LEAVECRITICAL;
}
#ifdef FOR_GEORGEST
void DebugDumpNode(LPOneTreeNode lpn, LPTSTR lpsz)
{
Assert(lpn->lpText);
DebugMsg(DM_TRACE, TEXT("ONETREE: %s (%d) %s"), lpsz, lpn, lpn->lpText);
}
#endif
LPTSTR StrRetToStrPtr(STRRET * psr, LPCITEMIDLIST pidl)
{
LPTSTR psz;
#ifdef UNICODE
TCHAR szName[MAX_PATH];
int iLen;
switch(psr->uType)
{
case STRRET_OLESTR:
lstrcpyn(szName, psr->pOleStr, ARRAYSIZE(szName));
SHFree(psr->pOleStr);
break;
case STRRET_OFFSET:
{
LPSTR pszStr = (LPSTR)(((LPBYTE)&pidl->mkid) + psr->uOffset);
iLen = lstrlenA(pszStr) + 1;
MultiByteToWideChar(CP_ACP, 0,
pszStr, iLen,
szName, iLen);
break;
}
case STRRET_CSTR:
iLen = lstrlenA(psr->cStr) + 1;
MultiByteToWideChar(CP_ACP, 0,
psr->cStr, iLen,
szName, iLen);
break;
default:
DebugMsg(DM_ERROR, TEXT("exp - StrRetToStrPtr: Got a type we don't know! %d"), psr->uType);
return (LPTSTR)c_szNULL;
}
psz = LocalAlloc(LPTR, (lstrlen(szName) + 1) * sizeof(TCHAR));
if (psz) {
lstrcpy(psz, szName);
}
#else
switch (psr->uType)
{
case STRRET_OLESTR:
{
LPOLESTR pwszDisplayName;
// Convert from STRRET_OLESTR to STRRET_CSTR
pwszDisplayName = psr->pOleStr;
OleStrToStrN(psr->cStr, ARRAYSIZE(psr->cStr),
pwszDisplayName, (UINT)-1);
SHFree(pwszDisplayName);
psr->uType = STRRET_CSTR;
}
// fall through
case STRRET_CSTR:
psz = LocalAlloc(LPTR, (lstrlen(psr->cStr) + 1) * sizeof(TCHAR));
if (psz) {
lstrcpy(psz, psr->cStr);
}
break;
case STRRET_OFFSET:
// REVIEW: Do some validation here, instead of asserts!
Assert(psr->uOffset < pidl->mkid.cb);
Assert(psr->uOffset >= SIZEOF(pidl->mkid.cb));
psz = (LPSTR)(((LPBYTE)&pidl->mkid) + psr->uOffset);
break;
}
#endif
return psz;
}
UINT AddChildEx(IShellFolder *lpsfParent, LPOneTreeNode lpnParent,
LPCITEMIDLIST pidl, BOOL bAllowDup,
LPOneTreeNode *lplpnKid, LPCTSTR pszText)
{
LPOneTreeNode lpnKid = NULL;
INT iPos = 0;
UINT uReturn;
DWORD dwLastChecked = 0;
OTValidate(lpnParent);
// Is it already in the tree?
if (lpnParent)
{
dwLastChecked = lpnParent->dwLastChanged;
lpnKid = FindKid( lpsfParent, lpnParent, pidl, FALSE, &iPos );
}
if (!lpnKid || bAllowDup)
{
STRRET srDisplayName;
HRESULT hres;
LPITEMIDLIST pidlChild = ILClone(pidl);
uReturn = ADDCHILD_FAILED; // assume error
if (!pidlChild)
goto Error1;
// First, ask for the display name of this subfolder.
if (pszText && *pszText)
{
#ifdef UNICODE
UINT cchLen = lstrlen(pszText);
srDisplayName.uType = STRRET_OLESTR;
srDisplayName.pOleStr = SHAlloc((cchLen + 1) * sizeof(OLECHAR));
if (srDisplayName.pOleStr == NULL)
{
hres = E_OUTOFMEMORY;
}
else
{
lstrcpyn(srDisplayName.pOleStr, pszText, cchLen + 1);
}
#else
srDisplayName.uType = STRRET_CSTR;
lstrcpyn(srDisplayName.cStr, pszText, ARRAYSIZE(srDisplayName.cStr));
#endif
hres = S_OK;
}
else
{
if (lpsfParent)
{
//
// Note for M7 only:
// If we turn off "hide extension", we end up displaying
// .EXE extension for rooted thing.
//
hres = lpsfParent->lpVtbl->GetDisplayNameOf(lpsfParent, pidl,
SHGDN_INFOLDER | SHGDN_NORMAL, &srDisplayName);
}
else
{
#ifdef UNICODE
srDisplayName.uType = STRRET_OLESTR;
srDisplayName.pOleStr = SHAlloc(MAX_PATH*SIZEOF(TCHAR));
if (srDisplayName.pOleStr == NULL)
{
hres = E_OUTOFMEMORY;
}
else
{
LoadString(hinstCabinet, IDS_DESKTOP, srDisplayName.pOleStr,
MAX_PATH);
hres = S_OK;
}
#else
srDisplayName.uType = STRRET_CSTR;
LoadString(hinstCabinet, IDS_DESKTOP, srDisplayName.cStr, ARRAYSIZE(srDisplayName.cStr));
hres = S_OK;
#endif
}
}
if (SUCCEEDED(hres))
{
LPOneTreeNode lpnKid = LocalAlloc(LPTR, sizeof(OneTreeNode));
if (lpnKid)
{
// Note that we copy only a mkid. Because we allocate
// two extra bytes (with 0-init), we'll get a IDL.
Assert(ILIsEmpty(_ILNext(pidl)));
lpnKid->pidl = pidlChild;
lpnKid->lpText = StrRetToStrPtr(&srDisplayName, pidlChild);
#ifdef DEBUG
lpnKid->dwDebugSig = OTDEBUG_SIG;
#endif
lpnKid->lpnParent = lpnParent;
lpnKid->hdpaKids = KIDSUNKNOWN;
lpnKid->cChildren = (BYTE)I_CHILDRENCALLBACK;
lpnKid->iImage = (USHORT)I_IMAGECALLBACK;
lpnKid->iSelectedImage = (USHORT)I_IMAGECALLBACK;
lpnKid->cRef = 1;
lpnKid->dwLastChanged = 0;
DebugDumpNode(lpnKid, TEXT("AdoptingKid"));
OTInvalidateNode(lpnKid);
*lplpnKid = lpnKid;
if (lpnParent)
{
AdoptKid(lpsfParent, lplpnKid, iPos, dwLastChecked);
(*lplpnKid)->fMark = 0;
// release now.. if adopt kid took it, it did an addref to lpnKid
OTRelease( lpnKid );
if (lpnKid == *lplpnKid) {
uReturn = ADDCHILD_ADDED;
} else {
uReturn = ADDCHILD_EXISTED;
}
} else {
// this needs to return success if there's no parent.
// only onetreeinitialize does this, and it needs the return
uReturn = ADDCHILD_ADDED;
}
}
} else {
ILFree(pidlChild);
}
}
else
{
// this kid still exists, set fMark to 0 so
// that we don't kill the kid below
lpnKid->fMark = 0;
// didn't add
*lplpnKid = lpnKid;
uReturn = ADDCHILD_EXISTED;
}
Error1:;
#ifdef OT_DEBUG
DebugMsg(DM_TRACE, TEXT("OneTree: Addchild retuns %d"), uReturn);
#endif
return uReturn;
}
UINT AddChild(IShellFolder *lpsfParent, LPOneTreeNode lpnParent,
LPCITEMIDLIST pidl, BOOL bAllowDup,
LPOneTreeNode *lplpnKid)
{
return AddChildEx(lpsfParent, lpnParent, pidl, bAllowDup, lplpnKid, NULL);
}
// BUGBUG.. One tree reference counting isn't there nor is releasing
//
// Notes: We can't combine this function with _BindFromJunctoinPoint
// to simplify the code. We need to avoid allocating szPath[]
// each time we call OTBindToFolder recursively.
//
HRESULT OTBindToFolderEx(LPOneTreeNode lpNode, LPSHELLFOLDER *ppshf)
{
HRESULT hres = S_OK; // assume no error
*ppshf = NULL; // assume error
OTValidate(lpNode);
if (!IsWindow(s_hwndOT)) {
s_hwndOT = NULL;
}
if (!lpNode)
{
return E_FAIL;
}
//
// Special case for the desktop node
//
if (lpNode == s_lpnRoot)
{
IUnknown_AddRef(s_pshfRoot);
*ppshf = s_pshfRoot;
return S_OK;
}
return SFCBindToFolder(NULL, lpNode, ppshf);
}
LPSHELLFOLDER OTBindToFolder(LPOneTreeNode lpnd)
{
LPSHELLFOLDER pshf = NULL;
HRESULT hres = OTBindToFolderEx(lpnd, &pshf);
Assert((FAILED(hres) && pshf==NULL) || (SUCCEEDED(hres) && pshf));
return pshf;
}
HRESULT OTBindToParent(LPOneTreeNode lpnd, LPSHELLFOLDER *ppsf)
{
HRESULT hres;
lpnd = OTGetParent(lpnd);
if (lpnd)
{
hres = OTBindToFolderEx(lpnd, ppsf);
OTRelease(lpnd);
return(hres);
}
// This must be the root item
if (OTIsDesktopRoot())
{
// The desktop has no parent
return E_INVALIDARG;
}
if (!s_pshfRootParent)
{
return E_UNEXPECTED;
}
IUnknown_AddRef(s_pshfRootParent);
*ppsf = s_pshfRootParent;
return S_OK;
}
HRESULT OTRealBindToFolder(LPOneTreeNode lpNode, LPSHELLFOLDER *ppshf)
{
HRESULT hres;
LPITEMIDLIST pidl;
*ppshf = NULL; // assume error
pidl = OTCreateIDListFromNode(lpNode);
if (pidl)
{
hres = s_pshfRoot->lpVtbl->BindToObject(s_pshfRoot, pidl, NULL, &IID_IShellFolder, (LPVOID*)ppshf);
Assert((FAILED(hres) && *ppshf==NULL) || (SUCCEEDED(hres) && *ppshf));
ILFree(pidl);
}
else
{
hres = E_OUTOFMEMORY;
}
return hres;
}
BOOL OTUpdateNodeName(LPSHELLFOLDER psf, LPOneTreeNode lpNode)
{
STRRET sr;
HRESULT hres;
LPTSTR pszNewName;
LPCITEMIDLIST pidl;
BOOL fRet = FALSE;
pidl = lpNode->pidl;
if (!pidl)
goto Bail;
hres = psf->lpVtbl->GetDisplayNameOf(psf, pidl,
SHGDN_INFOLDER | SHGDN_NORMAL, &sr);
if (FAILED(hres))
goto Bail;
ENTERCRITICAL;
pszNewName = StrRetToStrPtr(&sr, pidl);
if (pszNewName) {
if (!lpNode->lpText || lstrcmp(pszNewName, lpNode->lpText)) {
OTSetNodeName(lpNode, pszNewName);
} else {
// if we didn't set it, we need to free pszNewName
if (!IsPtrInPidl((LPBYTE)pszNewName, lpNode->pidl)) {
LocalFree(pszNewName);
}
}
}
fRet = TRUE;
LEAVECRITICAL;
Bail:
return fRet;
}
void ForceNode(LPOneTreeNode lpNode)
{
LPSHELLFOLDER psfParent;
DWORD dwAttribs = SFGAO_HASSUBFOLDER | SFGAO_SHARE | SFGAO_REMOVABLE |SFGAO_FILESYSANCESTOR | SFGAO_FILESYSTEM | SFGAO_CAPABILITYMASK | SFGAO_COMPRESSED;
OTValidate(lpNode);
if (NULL != (psfParent = OTBindToFolder(lpNode->lpnParent)))
{
if (OTUpdateNodeName(psfParent, lpNode)) {
LPCITEMIDLIST pidl;
pidl = lpNode->pidl;
psfParent->lpVtbl->GetAttributesOf(psfParent, 1, &pidl, &dwAttribs);
lpNode->cChildren = (dwAttribs & SFGAO_HASSUBFOLDER) ? 1 : 0;
lpNode->dwAttribs = dwAttribs;
lpNode->fCompressed = (BYTE)(dwAttribs & SFGAO_COMPRESSED ? TRUE : FALSE);
}
_OTGetImageIndex(psfParent, lpNode);
IUnknown_Release(psfParent);
}
}
void CheckDestroyHDPAKids(LPOneTreeNode lpnParent)
{
OTValidate(lpnParent);
//
// do we have any kids left?
//
lpnParent->cChildren = GetKidCount(lpnParent);
if (!lpnParent->cChildren) {
lpnParent->cChildren = 0;
DPA_Destroy(lpnParent->hdpaKids);
lpnParent->hdpaKids = NOKIDS;
}
}
//
// nuke all the kids with the ref still set
//
void KillAbandonedKids(LPOneTreeNode lpnParent)
{
int i;
LPOneTreeNode lpnKid;
OTValidate(lpnParent);
for (i = GetKidCount(lpnParent) - 1; i >= 0; i--) {
lpnKid = GetNthKid(lpnParent, i);
if (lpnKid && lpnKid->fMark) {
// kill de wabbit
ENTERCRITICAL;
DPA_DeletePtr(lpnParent->hdpaKids, i);
lpnParent->dwLastChanged = GetTickCount();
LEAVECRITICAL;
OTRelease(lpnKid);
}
}
CheckDestroyHDPAKids(lpnParent);
}
#if 0
UINT OTPeekForAMessage(PFileCabinet pfc)
{
if (pfc->fShouldClose)
return PEEK_CLOSE;
else
return PeekForAMessage(pfc, pfc->hwndMain, FALSE);
}
#endif
BOOL SearchForKids(HWND hwndOwner, LPOneTreeNode lpnParent, PFileCabinet pfc, BOOL fInteractive)
{
IShellFolder *lpsfParent;
LPENUMIDLIST penum;
LPITEMIDLIST pidl;
LPOneTreeNode lpnKid;
BOOL fSuccess = TRUE;
ULONG celt;
int iDestroyCount = 0;
SHELLSTATE ss;
OTValidate(lpnParent);
// Have a seat, this could take a while
lpsfParent = OTBindToFolder(lpnParent);
if (!lpsfParent)
{
Assert(FALSE);
fSuccess = FALSE;
goto Error1;
}
SHGetSetSettings(&ss, SSF_SHOWALLOBJECTS, FALSE);
if (FAILED(lpsfParent->lpVtbl->EnumObjects(lpsfParent,
fInteractive ? hwndOwner : NULL,
ss.fShowAllObjects ? SHCONTF_FOLDERS|SHCONTF_INCLUDEHIDDEN : SHCONTF_FOLDERS,
&penum)))
{
// Notes: It means the enumeration is either canceled by the
// user, or something went wrong.
//
// Return the state of lpnParent->hdpaKids to KIDSUNKNOWN.
//
KillKids(lpnParent);
lpnParent->hdpaKids = KIDSUNKNOWN;
OTInvalidateNode(lpnParent);
DebugMsg(DM_ERROR, TEXT("ca TR - SearchForKids ISF::EnumObjects failed"));
fSuccess = FALSE;
goto Error2;
}
// after this point, we're gonna succeed enough that we can turn off the invalid flag.
lpnParent->fInvalid = 0;
if (NodeHasKids(lpnParent)) {
int i;
for (i = GetKidCount(lpnParent) - 1; i >= 0; i--) {
lpnKid = GetNthKid(lpnParent, i);
if (lpnKid) {
lpnKid->fMark = 1;
}
}
}
// Build list of all the items
// Enumerate over all the sub folders within this folder.
// BUGBUG: Call GetMaxIDSize!
//
if (lpnParent->hdpaKids == KIDSUNKNOWN)
lpnParent->hdpaKids = NOKIDS;
while (penum->lpVtbl->Next(penum, 1, &pidl, &celt)==S_OK && celt==1)
{
AddChild(lpsfParent, lpnParent, pidl, FALSE, &lpnKid);
SHFree(pidl);
#if 0
if (pfc) {
switch (OTPeekForAMessage(pfc)) {
case PEEK_QUIT:
Assert(0); // this should never happen
case PEEK_CLOSE:
// yes, we see a close message. Bail!
OTInvalidateNode(lpnParent);
PostMessage(pfc->hwndMain, WM_CLOSE, 0, 0);
goto Punt;
}
}
#endif
}
#if 0
Punt:
#endif
ENTERCRITICAL;
if (NodeHasKids(lpnParent))
KillAbandonedKids(lpnParent);
else
lpnParent->cChildren = 0;
LEAVECRITICAL;
// Tidyup.
IUnknown_Release(penum);
Error2:
IUnknown_Release(lpsfParent);
Error1:
return fSuccess;
}
LPOneTreeNode FindNearestNodeFromIDList(LPCITEMIDLIST pidl, UINT uFlags)
{
LPOneTreeNode lpnd = NULL;
LPITEMIDLIST pidlCopy = ILClone(pidl);
if (pidlCopy)
{
while (ILRemoveLastID(pidlCopy))
{
uFlags &= ~OTGNF_NEARESTMATCH; // so we won't recurse forever
lpnd = OTGetNodeFromIDList(pidlCopy, uFlags);
if (lpnd) {
OTRelease(lpnd);
break;
}
}
ILFree(pidlCopy);
}
return lpnd;
}
void RelayFileSysChange(LONG lEvent, LPITEMIDLIST pidl, LPITEMIDLIST pidlExtra)
{
int i;
HWND hwnd;
LPNMOTFSEINFO lpnm;
if (!s_hdpaAppHwnds)
return;
lpnm = (LPNMOTFSEINFO)Alloc(SIZEOF(NMOTFSEINFO));
if (!lpnm)
return;
lpnm->nmhdr.code = OTN_FSE;
lpnm->lEvent = lEvent;
lpnm->pidl = pidl;
lpnm->pidlExtra = pidlExtra;
for (i = DPA_GetPtrCount(s_hdpaAppHwnds) - 1; i >= 0; i--) {
hwnd = (HWND)DPA_GetPtr(s_hdpaAppHwnds, i);
if (hwnd) {
if (!IsWindow(hwnd)) {
//
// unregistering will shrink s_hdpaAppHwnds,
// but we're done with this one anyways
//
OTUnregister(hwnd);
} else {
// notify them.
SendMessage(hwnd, CWM_ONETREEFSE, 0, (LPARAM)(LPNMOTFSEINFO)lpnm);
}
}
}
Free(lpnm);
}
void OTInvalidateRoot()
{
OTSweepFolders(s_lpnRoot);
OTInvalidateNode(s_lpnRoot);
}
void OTAbandonKid(LPOneTreeNode lpnParent, LPOneTreeNode lpnKid)
{
LPSHELLFOLDER psfParent;
if (lpnParent && lpnKid && NodeHasKids(lpnParent)) {
LPOneTreeNode lpn;
int i;
// invalidate the new node's parent to get any children flags right
OTInvalidateNode(lpnParent);
// remove the node from it's parent's list
psfParent = OTBindToFolder(lpnParent);
if (psfParent)
{
lpn = FindKid( psfParent, lpnParent, lpnKid->pidl, FALSE, &i );
if (lpn) {
#ifdef OT_DEBUG
DebugMsg(DM_TRACE, TEXT("OneTree: QuickRename found old node to nuke"));
#endif
ENTERCRITICAL;
DPA_DeletePtr(lpnParent->hdpaKids, i);
lpnParent->dwLastChanged = GetTickCount();
LEAVECRITICAL;
OTRelease(lpn);
CheckDestroyHDPAKids(lpnParent);
}
IUnknown_Release(psfParent);
}
}
}
// if the RENAMEFOLDER event was a rename within the same
// if the RENAMEFOLDER event was a rename within the same
BOOL TryQuickRename(LPITEMIDLIST pidl, LPITEMIDLIST pidlExtra, BOOL *pfRet)
{
LPITEMIDLIST pidlClone;
LPOneTreeNode lpNode;
LPOneTreeNode lpNodeExtra;
LPOneTreeNode lpNodeNew;
BOOL fRet = FALSE;
// This can happen when a folder is moved from a "rooted" Explorer outside
// of the root
if (!pidl || !pidlExtra)
{
return(FALSE);
}
// this whole things should be in a critical section from the call of
// DoHandleFileSysChange
// this one was deleted
lpNode = _GetNodeFromIDList(pidl, 0, NULL);
if (lpNode == s_lpnRoot) {
OTInvalidateRoot();
fRet = TRUE;
} else if (lpNode) {
// this one was created
pidlClone = ILClone(pidlExtra);
if (pidlClone) {
ILRemoveLastID(pidlClone);
// if the parent isn't created yet, let's not bother
lpNodeExtra = _GetNodeFromIDList(pidlClone, 0, NULL);
ILFree(pidlClone);
if (lpNodeExtra) {
lpNodeNew = _GetNodeFromIDList(pidlExtra, OTGNF_TRYADD, NULL);
if (lpNode == lpNodeNew) {
// Same node, lets generate a new one to take care
// of simple renames that only change case or other
// such things, else the titles and the like will
// not be updated...
OTAddSubFolder(lpNode->lpnParent, ILFindLastID(pidlExtra), TRUE, &lpNodeNew);
}
if (lpNodeNew) {
int i;
Assert(OTGetParent(lpNodeNew) == lpNodeExtra);
Assert((lpNodeNew == lpNode) || (lpNodeNew->hdpaKids == KIDSUNKNOWN));
// invalidate the new node's parent to get any children flags right
OTInvalidateNode(lpNodeExtra);
// Everything's all set. copy info from lpNode to lpNodeNew
// and remove lpNode from it's parent
// kidnap lpNode's kids
lpNodeNew->cChildren = lpNode->cChildren;
lpNodeNew->hdpaKids = lpNode->hdpaKids;
lpNodeNew->iImage = lpNode->iImage;
lpNodeNew->iSelectedImage = lpNode->iSelectedImage;
if (NodeHasKids(lpNodeNew)) {
for (i = GetKidCount(lpNodeNew) - 1; i >= 0 ; i--) {
lpNodeExtra = GetNthKid(lpNodeNew, i);
if (lpNodeExtra) {
lpNodeExtra->lpnParent = lpNodeNew;
}
}
}
//
// We need to invalidate all the cached IShellFolder's.
//
OTSweepFolders(lpNodeNew);
SFCFreeNode(lpNodeNew);
// now clean up the old node
lpNode->cChildren = (BYTE)I_CHILDRENCALLBACK;
lpNode->hdpaKids = KIDSUNKNOWN;
SFCFreeNode(lpNode);
Assert(lpNode->lpnParent); // bad news if we're renaming the root
if (lpNode->lpnParent && NodeHasKids(lpNode->lpnParent)) {
LPSHELLFOLDER psfParent;
LPOneTreeNode lpnParent = lpNode->lpnParent;
// invalidate the new node's parent to get any children flags right
OTInvalidateNode(lpnParent);
// remove the node from it's parent's list
psfParent = OTBindToFolder(lpnParent);
if (psfParent)
{
lpNodeExtra = FindKid(psfParent, lpnParent, lpNode->pidl, FALSE, &i );
if (lpNodeExtra && lpNodeExtra == lpNode) {
#ifdef OT_DEBUG
DebugMsg(DM_TRACE, TEXT("OneTree: QuickRename found old node to nuke"));
#endif
ENTERCRITICAL;
DPA_DeletePtr(lpnParent->hdpaKids, i);
lpnParent->dwLastChanged = GetTickCount();
LEAVECRITICAL;
OTRelease(lpNode);
CheckDestroyHDPAKids(lpnParent);
}
IUnknown_Release(psfParent);
}
}
fRet = *pfRet = TRUE;
}
}
}
}
return fRet;
}
// avoids eating stack
BOOL DoHandleFileSysChange(LONG lNotification, LPITEMIDLIST pidl, LPITEMIDLIST pidlExtra)
{
LPOneTreeNode lpNode = NULL;
LPITEMIDLIST pidlClone;
BOOL fRet = FALSE;
switch(lNotification)
{
case SHCNE_UPDATEITEM:
{
// We will rename from ourselves to ourselves, which will force the
// node to be updated completely (rather than the previous method of
// just updating ourselves based on a possibly stale pidl)
BOOL fRenRet;
if (TryQuickRename(pidl, pidl, &fRenRet))
{
fRet = fRenRet;
break;
}
fRet = FALSE;
break;
}
case SHCNE_RENAMEFOLDER:
{
BOOL fRenRet;
// first try to just swap the nodes if it's true rename (not a move)
if (TryQuickRename(pidl, pidlExtra, &fRenRet))
{
fRet = fRenRet;
break;
}
// Rename is special. We need to invalidate both
// the pidl and the pidlExtra. so we call ourselves
fRet = DoHandleFileSysChange(0, pidlExtra, NULL);
// Handle the rooted case where this pidl may be NULL!
if (!pidl)
break;
}
case SHCNE_RMDIR:
if (ILIsEmpty(pidl)) {
// we've deleted the desktop dir.
lpNode = s_lpnRoot;
OTInvalidateRoot();
break;
}
case 0:
case SHCNE_MKDIR:
case SHCNE_DRIVEADD:
case SHCNE_DRIVEREMOVED:
if (!pidl)
{
break;
}
pidlClone = ILClone(pidl);
if (!pidlClone)
{
break;
}
ILRemoveLastID(pidlClone);
lpNode = _GetNodeFromIDList(pidlClone, 0, NULL);
ILFree(pidlClone);
break;
case SHCNE_MEDIAINSERTED:
case SHCNE_MEDIAREMOVED:
fRet = TRUE;
lpNode = _GetNodeFromIDList(pidl, 0, NULL);
if (lpNode)
lpNode = lpNode->lpnParent;
break;
case SHCNE_DRIVEADDGUI:
fRet = TRUE;
case SHCNE_NETSHARE:
case SHCNE_NETUNSHARE:
case SHCNE_UPDATEDIR:
lpNode = _GetNodeFromIDList(pidl, 0, NULL);
break;
case SHCNE_SERVERDISCONNECT:
// nuke all our kids and mark ourselves invalid
lpNode = _GetNodeFromIDList(pidl, 0, NULL);
if (lpNode && NodeHasKids(lpNode))
{
int i;
for (i = GetKidCount(lpNode) -1; i >= 0; i--) {
OTRelease(GetNthKid(lpNode, i));
}
DPA_Destroy(lpNode->hdpaKids);
lpNode->hdpaKids = KIDSUNKNOWN;
OTInvalidateNode(lpNode);
SFCFreeNode(lpNode);
} else {
lpNode = NULL;
fRet = TRUE;
}
case SHCNE_ASSOCCHANGED:
fRet = TRUE;
break;
case SHCNE_UPDATEIMAGE:
if (pidl) {
InvalidateImageIndices();
DoInvalidateAll(s_lpnRoot, *(int UNALIGNED *)((BYTE*)pidl + 2));
DebugMsg(DM_TRACE, TEXT(" SHCNE_UPDATEIMAGE : %d "), *(int UNALIGNED *)((BYTE*)pidl + 2));
fRet = TRUE;
}
break;
}
if (lpNode)
{
OTInvalidateNode(lpNode);
fRet = TRUE;
}
return fRet;
}
void HandleFileSysChange(LONG lNotification, LPITEMIDLIST*lplpidl)
{
BOOL fRelay;
LPITEMIDLIST pidl1 = lplpidl[0];
LPITEMIDLIST pidl2 = lplpidl[1];
if (!s_bDesktopRoot)
{
// Note that we still call HandleFSChange even if these fail in case
// we got a rename from our root to somewhere outside our root
OTTranslateIDList(pidl1, &pidl1);
if (pidl2)
{
OTTranslateIDList(pidl2, &pidl2);
}
}
ENTERCRITICAL;
fRelay = DoHandleFileSysChange(lNotification, pidl1, pidl2);
LEAVECRITICAL;
if (fRelay)
RelayFileSysChange(lNotification, pidl1, pidl2);
if (!s_bDesktopRoot)
{
if (pidl2)
{
ILFree(pidl2);
}
if (pidl1)
{
ILFree(pidl1);
}
}
}
void _OTGetImageIndex(LPSHELLFOLDER psfParent, LPOneTreeNode lpNode)
{
LPITEMIDLIST pidl;
OTValidate(lpNode);
Assert(psfParent);
pidl = OTCloneFolderID(lpNode);
if (pidl)
{
int iSelectedImage;
lpNode->iImage = SHMapPIDLToSystemImageListIndex(psfParent,
pidl, &iSelectedImage);
lpNode->iSelectedImage = iSelectedImage;
ILFree(pidl);
}
}
BOOL OTSubNodeCount(HWND hwndOwner, LPOneTreeNode lpNode, PFileCabinet pfc, UINT* pcnd, BOOL fInteractive)
{
BOOL fSuccess = TRUE;
OTValidate(lpNode);
*pcnd=0;
if (lpNode)
{
if (lpNode->hdpaKids == KIDSUNKNOWN || lpNode->fInvalid)
{
fSuccess=SearchForKids(hwndOwner, lpNode, pfc, fInteractive);
}
if (NodeHasKids(lpNode))
{
*pcnd = GetKidCount(lpNode);
}
}
else
{
fSuccess = FALSE;
}
return fSuccess;
}
LPOneTreeNode OTGetNthSubNode(HWND hwndOwner, LPOneTreeNode lpnd, UINT i)
{
UINT cnd;
OTValidate(lpnd);
// OTSubNodeCount will validate
OTSubNodeCount(hwndOwner, lpnd, NULL, &cnd, FALSE);
if (i >= cnd) {
return NULL;
} else {
LPOneTreeNode lpNode;
ENTERCRITICAL;
lpNode = GetNthKid(lpnd, i);
if (lpNode) OTAddRef(lpNode);
LEAVECRITICAL;
return lpNode;
}
}
BOOL OTIsCompressed(LPOneTreeNode lpNode)
{
if (lpNode->fInvalid)
ForceNode(lpNode);
return lpNode->fCompressed;
}
void OTNodeFillTV_ITEM(LPOneTreeNode lpNode, LPTV_ITEM lpItem)
{
Assert(lpNode);
OTValidate(lpNode);
if (lpNode->fInvalid ||
lpNode->iImage == (USHORT)I_IMAGECALLBACK ||
lpNode->iSelectedImage == (USHORT)I_IMAGECALLBACK)
ForceNode(lpNode);
if (lpItem->mask & TVIF_TEXT) {
OTGetNodeName(lpNode, lpItem->pszText, lpItem->cchTextMax);
}
if (lpItem->mask & TVIF_CHILDREN) {
if (lpNode->fInvalid || lpNode->hdpaKids == KIDSUNKNOWN)
lpItem->cChildren = lpNode->cChildren;
else if (lpNode->hdpaKids == NOKIDS)
lpItem->cChildren = 0;
else
lpItem->cChildren = GetKidCount(lpNode);
}
if (lpItem->mask & TVIF_IMAGE) {
lpItem->iImage = lpNode->iImage;
}
if (lpItem->mask & TVIF_SELECTEDIMAGE) {
lpItem->iSelectedImage = lpNode->iSelectedImage;
}
}
BOOL OTHasSubFolders(LPOneTreeNode lpNode)
{
Assert(lpNode);
OTValidate(lpNode);
if (lpNode->fInvalid) {
ForceNode(lpNode);
}
return lpNode->cChildren;
}
void OTRegister(HWND hwnd)
{
if (!s_hdpaAppHwnds)
s_hdpaAppHwnds = DPA_Create(0);
// BUGBUG (DavePl) Check return value of DPA_Create()
DPA_InsertPtr(s_hdpaAppHwnds, 0, (void *)hwnd);
}
void OTUnregister(HWND hwnd)
{
int i;
int iCount;
if (!s_hdpaAppHwnds)
return;
for (iCount = i = DPA_GetPtrCount(s_hdpaAppHwnds) - 1; i >= 0; i--) {
if (hwnd == (HWND)DPA_GetPtr(s_hdpaAppHwnds, i))
{
DPA_DeletePtr(s_hdpaAppHwnds, i);
return;
}
}
}
// this validates that the subpidl is a folder pidl and returns the full (non-simple)
// pidl for it
LPITEMIDLIST OTGetRealFolderIDL(LPOneTreeNode lpnParent, LPCITEMIDLIST pidlSimple)
{
IShellFolder *lpsfParent;
HRESULT hres;
LPITEMIDLIST pidlReal = NULL;
Assert(_ILNext(pidlSimple)->mkid.cb==0);
Assert(lpnParent);
OTValidate(lpnParent);
hres = OTBindToFolderEx(lpnParent, &lpsfParent);
// get the pidlReal because we might have a simple pidl
if (lpsfParent)
{
hres = SHGetRealIDL(lpsfParent, pidlSimple, &pidlReal);
if (SUCCEEDED(hres) && pidlReal)
{
DWORD dwAttribs = SFGAO_FOLDER | SFGAO_BROWSABLE;
hres=lpsfParent->lpVtbl->GetAttributesOf(lpsfParent, 1, &pidlReal, &dwAttribs);
if (FAILED(hres) || !(dwAttribs & (SFGAO_FOLDER | SFGAO_BROWSABLE)))
{
ILFree(pidlReal);
pidlReal = NULL;
}
}
IUnknown_Release(lpsfParent);
} else {
DebugMsg(DM_ERROR, TEXT("Unable to bind to folder %s in OTAddSubFolder"), TEXT("?"));
}
return pidlReal;
}
HRESULT OTAddSubFolder(LPOneTreeNode lpNode, LPCITEMIDLIST pidl,
BOOL fAllowDup, LPOneTreeNode *ppndOut)
{
LPSHELLFOLDER lpsfParent;
LPOneTreeNode lpNewNode = NULL;
HRESULT hres;
hres = OTBindToFolderEx(lpNode, &lpsfParent);
if (SUCCEEDED(hres)) {
LPITEMIDLIST pidlReal = OTGetRealFolderIDL(lpNode, pidl);
hres = E_FAIL;
if (pidlReal) {
if (AddChild(lpsfParent, lpNode, pidlReal, fAllowDup, &lpNewNode) != ADDCHILD_FAILED) {
hres = NOERROR;
}
}
IUnknown_Release(lpsfParent);
}
if (ppndOut) {
*ppndOut = lpNewNode;
if (lpNewNode)
OTAddRef(lpNewNode);
}
return hres;
}
void OTGetImageIndex(LPOneTreeNode lpNode, int *lpiImage, int *lpiSelectedImage)
{
Assert(lpNode);
OTValidate(lpNode);
if (lpNode->fInvalid ||
lpNode->iImage == (USHORT)I_IMAGECALLBACK ||
lpNode->iSelectedImage == (USHORT)I_IMAGECALLBACK)
{
// this gets the sharing info aswell
ForceNode(lpNode);
}
if (lpiImage)
*lpiImage = lpNode->iImage;
if (lpiSelectedImage)
*lpiSelectedImage = lpNode->iSelectedImage;
}
LPOneTreeNode OTGetNodeFromIDListEx(LPCITEMIDLIST pidl, UINT uFlags, HRESULT* phresOut)
{
LPOneTreeNode lpNode = _GetNodeFromIDList(pidl, uFlags, phresOut);
if (!lpNode && (uFlags & OTGNF_NEARESTMATCH))
{
lpNode = FindNearestNodeFromIDList(pidl, uFlags);
}
if (lpNode)
OTAddRef(lpNode);
return lpNode;
}
//
// Replacement of OTGetPathFromNode
//
LPITEMIDLIST OTCreateIDListFromNode(LPOneTreeNode lpn)
{
USHORT uID[1] = { 0 };
LPITEMIDLIST pidl = ILClone((LPITEMIDLIST)&uID[0]);
Assert(lpn);
// Walk backwards to construct the path.
for (; pidl && (lpn != s_lpnRoot); lpn = lpn->lpnParent)
{
LPCITEMIDLIST pidlNext;
ENTERCRITICAL;
pidlNext = lpn->pidl;
if (pidlNext == NULL) {
LEAVECRITICAL;
break;
}
pidl = ILAppendID(pidl, &pidlNext->mkid, FALSE);
LEAVECRITICAL;
}
return pidl;
}
//
// Replacement of GetNodeFromPath
//
LPOneTreeNode _GetNodeFromIDList(LPCITEMIDLIST pidlFull, UINT uFlags, HRESULT *phresOut)
{
LPCITEMIDLIST pidl;
LPOneTreeNode lpNode = s_lpnRoot;
HRESULT hres = S_OK;
if (!pidlFull)
{
return(NULL);
}
//
// REVIEW: We can eliminate ILCloneFirst by copying each mkid to pidlT.
//
for (pidl = pidlFull; !ILIsEmpty(pidl) && lpNode; pidl = _ILNext(pidl))
{
LPOneTreeNode lpnParent = lpNode;
LPITEMIDLIST pidlFirst = ILCloneFirst(pidl);
OTValidate(lpnParent);
lpNode = NULL; // assume error
if (pidlFirst)
{
LPSHELLFOLDER psfParent = OTBindToFolder(lpnParent);
if (psfParent)
{
INT i;
lpNode = FindKid(psfParent, lpnParent, pidlFirst, uFlags & OTGNF_VALIDATE, &i);
if (!lpNode && (uFlags & OTGNF_TRYADD))
{
DebugMsg(DM_TRACE, TEXT("Onetree: Unable to Find %s"), (LPTSTR)pidl->mkid.abID);
// doing no notify for right now because
// several notify handlers call this.
// just an efficiency thing
hres = OTAddSubFolder(lpnParent, pidlFirst, FALSE, &lpNode);
OTRelease(lpNode);
}
IUnknown_Release(psfParent);
}
ILFree(pidlFirst);
}
}
if (phresOut) {
DebugMsg(DM_TRACE, TEXT("ca TR - _GetNodeFromIDList returning %x as *phresOut"), hres);
*phresOut = hres;
}
return lpNode;
}
BOOL OTGetDisplayName(LPCITEMIDLIST pidl, LPTSTR pszName)
{
if (pidl)
{
if (s_bDesktopRoot)
{
return ILGetDisplayName(pidl, pszName);
}
else
{
STRRET srName;
// Not everybody can handle an empty IDList to talk about
// themselves
if (ILIsEmpty(pidl))
{
return ILGetDisplayName(s_pidlRoot, pszName);
}
if (SUCCEEDED(s_pshfRoot->lpVtbl->GetDisplayNameOf(s_pshfRoot, pidl,
SHGDN_FORPARSING, &srName)))
{
StrRetToStrN(pszName, MAX_PATH, &srName, pidl);
return(TRUE);
}
}
}
*pszName = 0;
return FALSE;
}
int CALLBACK OTTreeViewCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
LPOneTreeNode lpn1 = (LPOneTreeNode)lParam1;
LPOneTreeNode lpn2 = (LPOneTreeNode)lParam2;
IShellFolder *psfParent = (IShellFolder *)lParamSort;
HRESULT hres;
OTValidate(lpn1);
OTValidate(lpn2);
ENTERCRITICAL;
hres = psfParent->lpVtbl->CompareIDs(psfParent,
0, OTGetFolderID(lpn1), OTGetFolderID(lpn2));
LEAVECRITICAL;
if (FAILED(hres))
{
return(0);
}
return (short)SCODE_CODE(GetScode(hres));
}
LPITEMIDLIST OTCloneAbsIDList(LPCITEMIDLIST pidlRelToRoot)
{
if (s_bDesktopRoot)
return ILClone(pidlRelToRoot);
return ILCombine(s_pidlRoot, pidlRelToRoot);
}