SHLFLDR.CPP
/************************************************************************** 
   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 
   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 
   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 
   PARTICULAR PURPOSE. 
 
   Copyright 1997 Microsoft Corporation.  All Rights Reserved. 
**************************************************************************/ 
 
/************************************************************************** 
 
   File:          ShlFldr.cpp 
    
   Description:   Implements CShellFolder. 
 
**************************************************************************/ 
 
/************************************************************************** 
   #include statements 
**************************************************************************/ 
 
#include "ShlFldr.h" 
#include "ShlView.h" 
#include "InfoTip.h" 
#include "ExtrIcon.h" 
#include "ContMenu.h" 
#include "Guid.h" 
#include "resource.h" 
 
/************************************************************************** 
   global variables 
**************************************************************************/ 
 
HKEY  g_ahKey[] = {  HKEY_CLASSES_ROOT, 
                     HKEY_CURRENT_USER, 
                     HKEY_LOCAL_MACHINE, 
                     HKEY_USERS, 
                     HKEY_PERFORMANCE_DATA, 
                     HKEY_CURRENT_CONFIG, 
                     HKEY_DYN_DATA, 
                     (HKEY)-1}; 
       
/************************************************************************** 
 
   CShellFolder::CShellFolder() 
 
**************************************************************************/ 
 
CShellFolder::CShellFolder(CShellFolder *pParent, LPCITEMIDLIST pidl) 
{ 
m_hKeyRoot = NULL; 
m_lpszSubKey = NULL; 
 
m_pSFParent = pParent; 
if(m_pSFParent) 
   m_pSFParent->AddRef(); 
 
m_pPidlMgr = new CPidlMgr(); 
if(!m_pPidlMgr) 
   { 
   delete this; 
   return; 
   } 
 
//get the shell's IMalloc pointer 
//we'll keep this until we get destroyed 
if(FAILED(SHGetMalloc(&m_pMalloc))) 
   { 
   delete this; 
   return; 
   } 
 
m_pidlRel = m_pPidlMgr->Copy(pidl); 
m_pidlFQ = NULL; 
 
if(m_pidlRel) 
   { 
   //determine if this PIDL has an HKEY or a sub key string 
   if(m_pPidlMgr->IsRootKey(m_pidlRel)) 
      { 
      //this is a root key 
      m_pPidlMgr->GetRootKey(m_pidlRel, &m_hKeyRoot); 
      } 
 
   DWORD dwSize = m_pPidlMgr->GetSubKeyText(m_pidlRel, NULL, 0); 
   m_lpszSubKey = new TCHAR[dwSize]; 
   if(m_lpszSubKey) 
      { 
      m_pPidlMgr->GetSubKeyText(m_pidlRel, m_lpszSubKey, dwSize); 
      } 
   } 
 
m_ObjRefCount = 1; 
 
g_DllRefCount++; 
} 
 
/************************************************************************** 
 
   CShellFolder::~CShellFolder() 
 
**************************************************************************/ 
 
CShellFolder::~CShellFolder() 
{ 
if(m_pidlRel) 
   { 
   m_pPidlMgr->Delete(m_pidlRel); 
   m_pidlRel = NULL; 
   } 
 
if(m_pidlFQ) 
   { 
   m_pPidlMgr->Delete(m_pidlFQ); 
   m_pidlFQ = NULL; 
   } 
 
if(m_lpszSubKey) 
   delete m_lpszSubKey; 
 
if(m_pSFParent) 
   m_pSFParent->Release(); 
 
if(m_pMalloc) 
   { 
   m_pMalloc->Release(); 
   } 
 
if(m_pPidlMgr) 
   { 
   delete m_pPidlMgr; 
   } 
 
g_DllRefCount--; 
} 
 
/////////////////////////////////////////////////////////////////////////// 
// 
// IUnknown Implementation 
// 
 
/************************************************************************** 
 
   CShellFolder::QueryInterface 
 
**************************************************************************/ 
 
STDMETHODIMP CShellFolder::QueryInterface(REFIID riid, LPVOID *ppReturn) 
{ 
*ppReturn = NULL; 
 
//IUnknown 
if(IsEqualIID(riid, IID_IUnknown)) 
   { 
   *ppReturn = this; 
   } 
 
//IPersistFolder 
else if(IsEqualIID(riid, IID_IPersistFolder)) 
   { 
   *ppReturn = (IPersistFolder*)this; 
   } 
 
//IShellFolder 
else if(IsEqualIID(riid, IID_IShellFolder)) 
   { 
   *ppReturn = (IShellFolder*)this; 
   } 
 
if(*ppReturn) 
   { 
   (*(LPUNKNOWN*)ppReturn)->AddRef(); 
   return S_OK; 
   } 
 
return E_NOINTERFACE; 
}                                              
 
/************************************************************************** 
 
   CShellFolder::AddRef 
 
**************************************************************************/ 
 
STDMETHODIMP_(DWORD) CShellFolder::AddRef() 
{ 
return ++m_ObjRefCount; 
} 
 
/************************************************************************** 
 
   CShellFolder::Release 
 
**************************************************************************/ 
 
STDMETHODIMP_(DWORD) CShellFolder::Release() 
{ 
if(--m_ObjRefCount == 0) 
   { 
   delete this; 
   return 0; 
   } 
    
return m_ObjRefCount; 
} 
 
/////////////////////////////////////////////////////////////////////////// 
// 
// IPersist Implementation 
// 
 
/************************************************************************** 
 
   CShellView::GetClassID() 
    
**************************************************************************/ 
 
STDMETHODIMP CShellFolder::GetClassID(LPCLSID lpClassID) 
{ 
*lpClassID = CLSID_RegViewNameSpace; 
 
return S_OK; 
} 
 
/////////////////////////////////////////////////////////////////////////// 
// 
// IPersistFolder Implementation 
// 
 
/************************************************************************** 
 
   CShellView::Initialize() 
    
**************************************************************************/ 
 
STDMETHODIMP CShellFolder::Initialize(LPCITEMIDLIST pidl) 
{ 
if(m_pidlFQ) 
   { 
   m_pPidlMgr->Delete(m_pidlFQ); 
   m_pidlFQ = NULL; 
   } 
 
m_pidlFQ = m_pPidlMgr->Copy(pidl); 
 
return S_OK; 
} 
 
/////////////////////////////////////////////////////////////////////////// 
// 
// IShellFolder Implementation 
// 
 
/************************************************************************** 
 
   CShellFolder::BindToObject() 
    
**************************************************************************/ 
 
STDMETHODIMP CShellFolder::BindToObject(  LPCITEMIDLIST pidl,  
                                          LPBC pbcReserved,  
                                          REFIID riid,  
                                          LPVOID *ppvOut) 
{ 
*ppvOut = NULL; 
 
CShellFolder   *pShellFolder = new CShellFolder(this, pidl); 
if(!pShellFolder) 
   return E_OUTOFMEMORY; 
 
LPITEMIDLIST   pidlFQ; 
 
pidlFQ = m_pPidlMgr->Concatenate(m_pidlFQ, pidl); 
pShellFolder->Initialize(pidlFQ); 
m_pPidlMgr->Delete(pidlFQ); 
 
HRESULT  hr = pShellFolder->QueryInterface(riid, ppvOut); 
 
pShellFolder->Release(); 
 
return hr; 
} 
 
/************************************************************************** 
 
   CShellFolder::BindToStorage() 
    
**************************************************************************/ 
 
STDMETHODIMP CShellFolder::BindToStorage( LPCITEMIDLIST pidl,  
                                          LPBC pbcReserved,  
                                          REFIID riid,  
                                          LPVOID *ppvOut) 
{ 
*ppvOut = NULL; 
 
return E_NOTIMPL; 
} 
 
/************************************************************************** 
 
   CShellFolder::CompareIDs() 
    
**************************************************************************/ 
 
STDMETHODIMP CShellFolder::CompareIDs( LPARAM lParam,  
                                       LPCITEMIDLIST pidl1,  
                                       LPCITEMIDLIST pidl2) 
{ 
TCHAR szString1[MAX_PATH] = TEXT(""); 
TCHAR szString2[MAX_PATH] = TEXT(""); 
HKEY  hkey1 = NULL, 
      hkey2 = NULL; 
int   nReturn; 
 
/* 
Special case - If one of the items is a key and the other is a value, always  
make the key come before the value. 
*/ 
LPCITEMIDLIST  pidlTemp1 = pidl1, 
               pidlTemp2 = pidl2; 
 
//get the last item in each list 
while((m_pPidlMgr->GetNextItem(pidlTemp1))->mkid.cb) 
   pidlTemp1 = m_pPidlMgr->GetNextItem(pidlTemp1); 
while((m_pPidlMgr->GetNextItem(pidlTemp2))->mkid.cb) 
   pidlTemp2 = m_pPidlMgr->GetNextItem(pidlTemp2); 
 
//at this point, both pidlTemp1 and pidlTemp2 point to the last item in the list 
if(m_pPidlMgr->IsValue(pidlTemp1) != m_pPidlMgr->IsValue(pidlTemp2)) 
   { 
   if(m_pPidlMgr->IsValue(pidlTemp1)) 
      return 1; 
 
   return -1; 
   } 
 
//get the numeric value of the root key 
m_pPidlMgr->GetRootKey(pidl1, &hkey1); 
m_pPidlMgr->GetRootKey(pidl2, &hkey2); 
 
//compare the root keys 
if(hkey1 != hkey2) 
   { 
   return (int)((DWORD)hkey1 - (DWORD)hkey2); 
   } 
 
//now compare the subkey strings 
m_pPidlMgr->GetSubKeyText( pidl1,  
                           szString1,  
                           sizeof(szString1)); 
m_pPidlMgr->GetSubKeyText( pidl2,  
                           szString2,  
                           sizeof(szString2)); 
nReturn = lstrcmpi(szString1, szString2); 
if(nReturn) 
   return nReturn; 
 
//now compare the value strings 
m_pPidlMgr->GetValueText(  pidl1,  
                           szString1,  
                           sizeof(szString1)); 
m_pPidlMgr->GetValueText(  pidl2,  
                           szString2,  
                           sizeof(szString2)); 
return lstrcmpi(szString1, szString2); 
} 
 
/************************************************************************** 
 
   CShellFolder::CreateViewObject() 
    
**************************************************************************/ 
 
STDMETHODIMP CShellFolder::CreateViewObject( HWND hwndOwner,  
                                             REFIID riid,  
                                             LPVOID *ppvOut) 
{ 
HRESULT     hr; 
CShellView  *pShellView; 
 
*ppvOut = NULL; 
 
pShellView = new CShellView(this, m_pidlRel); 
if(!pShellView) 
   return E_OUTOFMEMORY; 
 
hr = pShellView->QueryInterface(riid, ppvOut); 
 
pShellView->Release(); 
 
return hr; 
} 
 
/************************************************************************** 
 
   CShellFolder::EnumObjects() 
    
**************************************************************************/ 
 
STDMETHODIMP CShellFolder::EnumObjects(   HWND hwndOwner,  
                                          DWORD dwFlags,  
                                          LPENUMIDLIST *ppEnumIDList) 
{ 
HRESULT  hr; 
 
*ppEnumIDList = NULL; 
 
*ppEnumIDList = new CEnumIDList(GetRootKey(), m_lpszSubKey, dwFlags, &hr); 
if(!*ppEnumIDList) 
   return hr; 
 
return S_OK; 
} 
 
/************************************************************************** 
 
   CShellFolder::GetAttributesOf() 
    
**************************************************************************/ 
 
STDMETHODIMP CShellFolder::GetAttributesOf(  UINT uCount,  
                                             LPCITEMIDLIST aPidls[],  
                                             LPDWORD pdwAttribs) 
{ 
UINT  i; 
 
*pdwAttribs = (DWORD)-1; 
 
for(i = 0; i < uCount; i++) 
   { 
   DWORD dwAttribs = 0; 
 
   //flags common to all items 
   dwAttribs |= 0; 
 
   //is this item a key? 
   if(!m_pPidlMgr->IsValue(m_pPidlMgr->GetLastItem(aPidls[i]))) 
      { 
      dwAttribs |= SFGAO_FOLDER; 
 
      //does this item have a sub folder? 
      if(m_pPidlMgr->HasSubKeys(GetRootKey(), m_lpszSubKey, aPidls[i])) 
         dwAttribs |= SFGAO_HASSUBFOLDER; 
      } 
    
   *pdwAttribs &= dwAttribs; 
   } 
 
return S_OK; 
} 
 
/************************************************************************** 
 
   CShellFolder::GetUIObjectOf() 
    
**************************************************************************/ 
 
STDMETHODIMP CShellFolder::GetUIObjectOf( HWND hwndOwner,  
                                          UINT uCount,  
                                          LPCITEMIDLIST *pPidl,  
                                          REFIID riid,  
                                          LPUINT puReserved,  
                                          LPVOID *ppvReturn) 
{ 
*ppvReturn = NULL; 
 
if(IsEqualIID(riid, IID_IContextMenu)) 
   { 
   CContextMenu   *pcm = new CContextMenu(this, pPidl, uCount); 
 
   if(pcm) 
      { 
      *ppvReturn = pcm; 
      return S_OK; 
      } 
   } 
 
if(uCount != 1) 
   return E_FAIL; 
 
if(IsEqualIID(riid, IID_IExtractIcon)) 
   { 
   CExtractIcon   *pei; 
   LPITEMIDLIST   pidl; 
 
   pidl = m_pPidlMgr->Concatenate(m_pidlRel, pPidl[0]); 
 
   pei = new CExtractIcon(pidl); 
 
   /* 
   The temp PIDL can be deleted because the new CExtractIcon either failed or  
   made its own copy of it. 
   */ 
   m_pPidlMgr->Delete(pidl); 
 
   if(pei) 
      { 
      *ppvReturn = pei; 
      return S_OK; 
      } 
    
   return E_OUTOFMEMORY; 
   } 
 
#if (_WIN32_IE >= 0x0400) 
if(IsEqualIID(riid, IID_IQueryInfo)) 
   { 
   CQueryInfo     *pqit; 
   LPITEMIDLIST   pidl; 
 
   pidl = m_pPidlMgr->Concatenate(m_pidlRel, pPidl[0]); 
 
   pqit = new CQueryInfo(pidl); 
 
   /* 
   The temp PIDL can be deleted because the new CQueryInfo either failed or  
   made its own copy of it. 
   */ 
   m_pPidlMgr->Delete(pidl); 
 
   if(pqit) 
      { 
      *ppvReturn = pqit; 
      return S_OK; 
      } 
    
   return E_OUTOFMEMORY; 
   } 
#endif   //#if (_WIN32_IE >= 0x0400) 
 
return E_NOINTERFACE; 
} 
 
/************************************************************************** 
 
   CShellFolder::GetDisplayNameOf() 
    
**************************************************************************/ 
 
#define GET_SHGDN_FOR(dwFlags)         ((DWORD)dwFlags & (DWORD)0x0000FF00) 
#define GET_SHGDN_RELATION(dwFlags)    ((DWORD)dwFlags & (DWORD)0x000000FF) 
 
STDMETHODIMP CShellFolder::GetDisplayNameOf( LPCITEMIDLIST pidl,  
                                             DWORD dwFlags,  
                                             LPSTRRET lpName) 
{ 
TCHAR szText[MAX_PATH]; 
int   cchOleStr; 
 
switch(GET_SHGDN_RELATION(dwFlags)) 
   { 
   case SHGDN_NORMAL: 
      //get the full name 
      m_pPidlMgr->GetPidlPath(pidl, szText, sizeof(szText)); 
 
      //If the text is NULL and this is a value, get the default item name. 
      if(!*szText && m_pPidlMgr->IsValue(m_pPidlMgr->GetLastItem(pidl))) 
         { 
         LoadString(g_hInst, IDS_DEFAULT, szText, sizeof(szText)); 
         } 
      break; 
 
   case SHGDN_INFOLDER: 
      { 
      LPITEMIDLIST   pidlTemp = m_pPidlMgr->GetLastItem(pidl); 
 
      //get the relative name 
      m_pPidlMgr->GetItemText(pidlTemp, szText, sizeof(szText)); 
 
      //If the text is NULL and this is a value, get the default item name. 
      if(!*szText && m_pPidlMgr->IsValue(pidlTemp)) 
         { 
         LoadString(g_hInst, IDS_DEFAULT, szText, sizeof(szText)); 
         } 
      } 
      break; 
 
   default: 
      return E_INVALIDARG; 
   } 
 
//get the number of characters required 
cchOleStr = lstrlen(szText) + 1; 
 
//allocate the wide character string 
lpName->pOleStr = (LPWSTR)m_pMalloc->Alloc(cchOleStr * sizeof(WCHAR)); 
if(!lpName->pOleStr) 
   return E_OUTOFMEMORY; 
 
lpName->uType = STRRET_WSTR; 
 
LocalToWideChar(lpName->pOleStr, szText, cchOleStr); 
 
return S_OK; 
} 
 
/************************************************************************** 
 
   CShellFolder::ParseDisplayName() 
    
**************************************************************************/ 
 
STDMETHODIMP CShellFolder::ParseDisplayName( HWND hwndOwner,  
                                             LPBC pbcReserved,  
                                             LPOLESTR lpDisplayName,  
                                             LPDWORD pdwEaten,  
                                             LPITEMIDLIST *pPidlNew,  
                                             LPDWORD pdwAttributes) 
{ 
HRESULT        hr = E_OUTOFMEMORY; 
LPITEMIDLIST   pidlFull = NULL; 
DWORD          dwChars = lstrlenW(lpDisplayName) + 1; 
LPTSTR         pszTemp = (LPTSTR)GlobalAlloc(GPTR, dwChars * sizeof(TCHAR)); 
 
if(pszTemp) 
   { 
   hr = E_FAIL; 
 
   WideCharToLocal(pszTemp, lpDisplayName, dwChars); 
 
   if(*pszTemp) 
      { 
      LPTSTR         pszNext; 
      TCHAR          szElement[MAX_PATH]; 
      int            i; 
      BOOL           fValue; 
 
      //get the root key 
 
      pszNext = GetNextRegElement(pszTemp, szElement, ARRAYSIZE(szElement)); 
 
      for(i = 0; (-1 != (int)g_ahKey[i]) && !pidlFull; i++) 
         { 
         TCHAR szKeyText[MAX_PATH]; 
 
         GetRootKeyText(g_ahKey[i], szKeyText, ARRAYSIZE(szKeyText)); 
 
         if(0 == lstrcmpi(szElement, szKeyText)) 
            { 
            pidlFull = m_pPidlMgr->CreateRootKey(g_ahKey[i]); 
            } 
         } 
       
      if(pidlFull) 
         { 
         LPITEMIDLIST   pidlTemp = NULL; 
         LPITEMIDLIST   pidlOld = NULL; 
         HKEY           hKey; 
 
         //check to see if the last item is a value or a key 
         if(NOERROR == RegOpenKeyEx(   g_ahKey[i], 
                                       pszNext, 
                                       0, 
                                       KEY_READ, 
                                       &hKey)) 
            { 
            fValue = FALSE; 
            RegCloseKey(hKey); 
            } 
         else 
            fValue = TRUE; 
          
         //get the remaining keys 
         while(pszNext = GetNextRegElement(pszNext, szElement, ARRAYSIZE(szElement))) 
            { 
            //if this is the last element, check to see if it is a value or a key 
            if(!*pszNext && fValue) 
               pidlTemp = m_pPidlMgr->CreateValue(szElement); 
            else 
               pidlTemp = m_pPidlMgr->CreateSubKey(szElement); 
             
            pidlOld = pidlFull; 
            pidlFull = m_pPidlMgr->Concatenate(pidlFull, pidlTemp); 
            m_pPidlMgr->Delete(pidlOld); 
            } 
          
         hr = S_OK; 
         } 
      } 
    
   GlobalFree((HGLOBAL)pszTemp); 
   } 
 
*pPidlNew = pidlFull; 
 
return hr; 
} 
 
/************************************************************************** 
 
   CShellFolder::SetNameOf() 
    
**************************************************************************/ 
 
STDMETHODIMP CShellFolder::SetNameOf(  HWND hwndOwner,  
                                       LPCITEMIDLIST pidl,  
                                       LPCOLESTR lpName,  
                                       DWORD dw,  
                                       LPITEMIDLIST *pPidlOut) 
{ 
return E_NOTIMPL; 
} 
 
/************************************************************************** 
 
   CShellFolder::GetFolderPath() 
    
**************************************************************************/ 
 
BOOL CShellFolder::GetFolderPath(LPTSTR lpszOut, DWORD dwOutSize) 
{ 
TCHAR    szTemp[MAX_PATH] = TEXT(""); 
DWORD    dwSize; 
 
if(!lpszOut) 
   return FALSE; 
 
*lpszOut = 0; 
 
//get the text for the root key 
GetRootKeyText(m_hKeyRoot, szTemp, sizeof(szTemp)); 
 
dwSize = lstrlen(szTemp); 
if(m_lpszSubKey) 
   dwSize += lstrlen(m_lpszSubKey) + 1; 
 
//is the output buffer big enough? 
if(dwSize > dwOutSize) 
   return FALSE; 
 
//add the text we already have 
lstrcpy(lpszOut, szTemp); 
 
//add this folder's text if present 
if(m_lpszSubKey && *m_lpszSubKey) 
   { 
   lstrcat(lpszOut, TEXT("\\")); 
   lstrcat(lpszOut, m_lpszSubKey); 
   } 
 
return TRUE; 
} 
 
/************************************************************************** 
 
   CShellFolder::GetFolderText() 
    
**************************************************************************/ 
 
BOOL CShellFolder::GetFolderText(LPTSTR lpszOut, DWORD dwOutSize) 
{ 
if(!lpszOut) 
   return FALSE; 
 
*lpszOut = 0; 
 
//add this folder's text if present 
if(m_lpszSubKey && *m_lpszSubKey) 
   { 
   lstrcpyn(lpszOut, m_lpszSubKey, dwOutSize); 
   } 
 
return TRUE; 
} 
 
/************************************************************************** 
 
   CShellFolder::GetRootKey() 
    
**************************************************************************/ 
 
HKEY CShellFolder::GetRootKey(void) 
{ 
HKEY  hkReturn = NULL; 
CShellFolder   *pTemp = this; 
 
while(pTemp) 
   { 
   if(pTemp->m_hKeyRoot) 
      { 
      hkReturn = pTemp->m_hKeyRoot; 
      break; 
      } 
    
   pTemp = pTemp->m_pSFParent; 
   } 
 
return hkReturn; 
}