treewalk.cpp
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 21k
Category:

Windows Kernel

Development Platform:

Visual C++

  1. //---------------------------------------------------------------------------
  2. //
  3. // Copyright (c) Microsoft Corporation 
  4. //
  5. // File: TreeWalk.cpp
  6. //
  7. // This file contains the implementation of CShellTreeWalker, a COM object
  8. // that inherits IShellTreeWalker, and it will recursively enumerate all the
  9. // files (or directories or both) starting from a root directory that match a
  10. // certain spec. 
  11. // 1. The tree walker is reparse point aware, it does not traverse into reparse 
  12. // point folders by default, but will if specified
  13. // 2. It keeps track of the number of files, directories, depth, and total
  14. // size of all files encountered.
  15. // 3. It will stop the traversal right away if any error message is returned
  16. // from the callback functions except for E_NOTIMPL
  17. // 4. It will jump out of the current working directory if S_FALSE is returned from callback
  18. // functions.
  19. //
  20. // History:
  21. //         12-5-97  by dli
  22. //------------------------------------------------------------------------
  23. #include "shellprv.h"
  24. #include "validate.h"
  25. #define MAX_TREE_DEPTH    0xFFFFFFFF// maximum file system tree depth we walk into 
  26. #define IS_FILE_DIRECTORY(pwfd)     ((pwfd)->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  27. #define IS_FILE_REPARSE_POINT(pwfd) ((pwfd)->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
  28. // Call back flags for _CallCallBack
  29. #define STWCB_FILE     1
  30. #define STWCB_ERROR    2
  31. #define STWCB_ENTERDIR 3
  32. #define STWCB_LEAVEDIR 4
  33. #define TF_TREEWALKER 0
  34. extern "C" {
  35.     DWORD PathGetClusterSize(LPCTSTR pszPath);
  36. }
  37. class CShellTreeWalker : public IShellTreeWalker
  38. {
  39. public:
  40.     CShellTreeWalker();
  41.     
  42.     // *** IUnknown Methods
  43.     virtual STDMETHODIMP QueryInterface(REFIID riid, LPVOID * ppvObj);
  44.     virtual STDMETHODIMP_(ULONG) AddRef(void) ;
  45.     virtual STDMETHODIMP_(ULONG) Release(void);
  46.     // *** IShellTreeWalker Methods
  47.     virtual STDMETHODIMP WalkTree(DWORD dwFlags, LPCWSTR pwszWalkRoot, LPCWSTR pwszWalkSpec, int iMaxPath, IShellTreeWalkerCallBack * pstwcb);
  48. protected:
  49.     
  50.     UINT _cRef;
  51.     
  52.     DWORD _dwFlags;     // Flags indicating the search status
  53.     UINT _nMaxDepth;    // Maximum depth we walk into
  54.     UINT _nDepth;       // Current depth
  55.     UINT _nFiles;       // Number of files we have seen so far
  56.     UINT _nDirs;        // Number of directories we have seen
  57.     BOOL _bFolderFirst; // Do the folders first
  58.     DWORD _dwClusterSize;    // the size of a cluster
  59.     ULONGLONG _ulTotalSize;  // total size of all files we have seen
  60.     ULONGLONG _ulActualSize; // total size on disk, taking into account compression, sparse files, and cluster slop
  61.     TCHAR _szWalkBuf[MAX_PATH];         // The path buffer used in the walk
  62.     LPCTSTR _pszWalkSpec;               // The spec we use in FindFirstFile and FindNextFile
  63.     IShellTreeWalkerCallBack * _pstwcb; // The call back interface pointer
  64.     
  65.     WIN32_FIND_DATA  _wfd;              // The temp storage of WIN32_FIND_DATA 
  66.     WIN32_FIND_DATA _fdTopLevelFolder;  // The top level folder info
  67.     
  68.     HRESULT _CallCallBacks(DWORD dwCallReason, WIN32_FIND_DATA * pwfd);
  69.     HRESULT _ProcessAndRecurse(WIN32_FIND_DATA * pwfd);
  70.     HRESULT _TreeWalkerHelper();
  71. }; 
  72. //
  73. // default constructor
  74. //
  75. CShellTreeWalker::CShellTreeWalker() : _cRef(1) 
  76. {
  77.     ASSERT(_dwFlags == 0);
  78.     ASSERT(_bFolderFirst == FALSE);
  79.     ASSERT(_nMaxDepth == 0);
  80.     ASSERT(_nDepth == 0);
  81.     ASSERT(_nDirs == 0);
  82.     ASSERT(_ulTotalSize == 0);
  83.     ASSERT(_ulActualSize == 0);
  84.     ASSERT(_pszWalkSpec == NULL);
  85.     ASSERT(_pstwcb == NULL);
  86.     ASSERT(_szWalkBuf[0] == 0);
  87. }
  88. // _CallCallBack: convert the TCHARs to WCHARs and call the callback functions
  89. HRESULT CShellTreeWalker::_CallCallBacks(DWORD dwReason, WIN32_FIND_DATA * pwfd)
  90. {
  91.     HRESULT hres;
  92.     WCHAR wszDir[MAX_PATH];
  93.     WCHAR wszFileName[MAX_PATH];
  94. #ifndef UNICODE
  95.     CHAR szTemp[MAX_PATH];
  96. #endif
  97.     WIN32_FIND_DATAW wfdw = {0};
  98.     WIN32_FIND_DATAW* pwfdw = NULL;
  99.     TREEWALKERSTATS tws = {0};
  100.     tws.nFiles = _nFiles;
  101.     tws.nFolders     = _nDirs;
  102.     tws.nDepth       = _nDepth;
  103.     tws.ulTotalSize  = _ulTotalSize;
  104.     tws.ulActualSize = _ulActualSize;
  105.     tws.dwClusterSize = _dwClusterSize;
  106.     // _szWalkBuf to wszDir
  107. #ifdef UNICODE
  108.     lstrcpy(wszDir, _szWalkBuf);
  109.     lstrcpy(wszFileName, wszDir);
  110.     PathCombine(wszFileName, wszFileName, pwfd->cFileName);
  111. #else
  112.     SHAnsiToUnicode(_szWalkBuf, wszDir, ARRAYSIZE(wszDir));
  113.     lstrcpy(szTemp, _szWalkBuf);
  114.     PathCombine(szTemp, szTemp, pwfd->cFileName);
  115.     SHAnsiToUnicode(szTemp, wszFileName, ARRAYSIZE(wszFileName));
  116. #endif
  117.     if (pwfd && ((dwReason == STWCB_FILE) || (dwReason == STWCB_ENTERDIR)))
  118.     {
  119.         // WIN32_FIND_DATAA to WIN32_FIND_DATAW
  120.         hmemcpy(&wfdw, pwfd, SIZEOF(WIN32_FIND_DATA));
  121. #ifndef UNICODE
  122.         SHAnsiToUnicode(pwfd->cFileName, wfdw.cFileName, ARRAYSIZE(wfdw.cFileName));
  123.         SHAnsiToUnicode(pwfd->cAlternateFileName, wfdw.cAlternateFileName, ARRAYSIZE(wfdw.cAlternateFileName));
  124. #endif
  125.         pwfdw = &wfdw;
  126.     }
  127.     switch (dwReason) {
  128.         case STWCB_FILE:
  129.             hres = _pstwcb->FoundFile(wszFileName, &tws, pwfdw);
  130.             TraceMsg(TF_TREEWALKER, "TreeWalker Callback FoundFile: %s\%s dwReason: %x  nFiles: %d  nDepth: %d  nDirs: %d",
  131.                      _szWalkBuf, pwfd->cFileName, dwReason, _nFiles, _nDepth, _nDirs);
  132.             break;
  133.         case STWCB_ENTERDIR:
  134.             hres = _pstwcb->EnterFolder(wszDir, &tws, pwfdw);
  135.             TraceMsg(TF_TREEWALKER, "TreeWalker Callback EnterFolder: %s dwReason: %x  nFiles: %d  nDepth: %d  nDirs: %d",
  136.                      _szWalkBuf, dwReason, _nFiles, _nDepth, _nDirs);
  137.             break;
  138.         case STWCB_LEAVEDIR:
  139.             hres = _pstwcb->LeaveFolder(wszDir, &tws);
  140.             break;
  141. //        case STWCB_ERROR:
  142. //            hres = _pstwcb->HandleError(S_OK, wszDir, &tws);
  143. //            break;
  144.         default:
  145.             hres = S_OK;
  146.             break;
  147.     }
  148.     // Error messages are significant to us, all E_ messages are interpreted as "Stop right now!!"
  149.     if (hres == E_NOTIMPL)
  150.         hres = S_OK;
  151.     
  152.     return hres;
  153. }
  154. // Call call back funtions on directories and files, recurse on directories if there is no objection
  155. // from the callback object
  156. HRESULT CShellTreeWalker::_ProcessAndRecurse(WIN32_FIND_DATA * pwfd)
  157. {
  158.     HRESULT hres = S_OK;
  159.     // Don't recurse on reparse points by default
  160.     if (IS_FILE_DIRECTORY(pwfd) && (!IS_FILE_REPARSE_POINT(pwfd) || (_dwFlags & WT_GOINTOREPARSEPOINT)))
  161.     {
  162.         // BUGBUG: If we are in a symbolic link, we need to detect cycles, 
  163.         // the common prefix method in BeenThereDoneThat will work as long as we
  164.         // keep track of all junction point targets we ran into. 
  165.         // use _szWalkBuf since we dont want any stack variables, because we are a recursive function
  166.         if (PathCombine(_szWalkBuf, _szWalkBuf, pwfd->cFileName))
  167.         {
  168.             // We remember the total number of sub directories we have seen
  169.             // doesn't matter if the client approves or not(call back returns S_OK or S_FALSE or E_FAIL)
  170.             _nDirs++;
  171.             // Let the CallBack object know that we are about to enter a directory 
  172.             if (_dwFlags & WT_NOTIFYFOLDERENTER)
  173.                 hres = _CallCallBacks(STWCB_ENTERDIR, pwfd);
  174.             if ((hres == S_OK) && (_nDepth < _nMaxDepth))
  175.             {
  176.                 _nDepth++;
  177.                 hres = _TreeWalkerHelper();
  178.                 _nDepth--;
  179.             }
  180.             else if (hres == S_FALSE)
  181.                 hres = S_OK;
  182.             // Let the CallBack object know that we are about to leave a directory 
  183.             if (_dwFlags & WT_NOTIFYFOLDERLEAVE)
  184.                 _CallCallBacks(STWCB_LEAVEDIR, NULL);
  185.             
  186.             // Peel off the subdirectory we tagged on in the above PathCombine Ex:"c:binfun --> c:bin" 
  187.             PathRemoveFileSpec(_szWalkBuf);
  188.         }
  189.     }
  190.     else
  191.     {
  192.         // Count the number of files and compute the total size before callling the
  193.         // call back object. 
  194.         ULARGE_INTEGER ulTemp;
  195.         _nFiles++;
  196.         ulTemp.LowPart  = pwfd->nFileSizeLow;
  197.         ulTemp.HighPart = pwfd->nFileSizeHigh;
  198.         _ulTotalSize += ulTemp.QuadPart;
  199. #ifdef WINNT
  200.         // when calculating the total size, we need to find out if the file is compressed or sparse (NTFS only case)
  201.         if (pwfd->dwFileAttributes & (FILE_ATTRIBUTE_COMPRESSED | FILE_ATTRIBUTE_SPARSE_FILE))
  202.         {
  203.             // use _szWalkBuf since we dont want any stack variables, because we are a recursive function
  204.             PathCombine(_szWalkBuf, _szWalkBuf, pwfd->cFileName);
  205.             // eithe the file is compressed or sparse, we need to call GetCompressedFileSize to get the real
  206.             // size on disk for this file (NOTE: GetCompressedFileSize takes into account cluster slop, except
  207.             // for files < 1 cluster, and we take care of that below)
  208.             ulTemp.LowPart = SHGetCompressedFileSize(_szWalkBuf, &ulTemp.HighPart);
  209.             _ulActualSize += ulTemp.QuadPart;
  210.             // Peel off the filename we tagged on above
  211.             PathRemoveFileSpec(_szWalkBuf);
  212.         }
  213.         else
  214. #endif
  215.         {
  216.             // BUGBUG (reinerf) the cluster size could change if we started on one volume and have now
  217.             // walked onto another mounted volume
  218.             // if its not compressed, we just round up to the drive's cluster size. ulTemp was setup
  219.             // already for us above, so just round it to the cluster and add it in
  220.             _ulActualSize += ROUND_TO_CLUSTER(ulTemp.QuadPart, _dwClusterSize);
  221.         }
  222.         
  223.         hres = _CallCallBacks(STWCB_FILE, pwfd);
  224.     }
  225.     return hres;
  226. }
  227. #define DELAY_ARRAY_GROW  32
  228. // Recursive function that does the real work on the traversal,
  229. HRESULT CShellTreeWalker::_TreeWalkerHelper()
  230. {
  231.     HRESULT hres = S_OK;
  232.     TraceMsg(TF_TREEWALKER, "TreeWalkerHelper started on: %s flags: %x  nFiles: %d  nDepth: %d  nDirs: %d",
  233.              _szWalkBuf, _dwFlags, _nFiles, _nDepth, _nDirs);
  234.     // Let the CallBack object know that we are about to start the walk
  235.     // provided he cares about the root
  236.     if (_nDepth == 0 && !(_dwFlags & WT_EXCLUDEWALKROOT) &&
  237.         (_dwFlags & WT_NOTIFYFOLDERENTER))
  238.     {
  239.         HANDLE hTopLevelFolder;
  240.         // Get the info for the TopLevelFolder
  241.         hTopLevelFolder = FindFirstFile(_szWalkBuf, &_fdTopLevelFolder);
  242.         if (hTopLevelFolder == INVALID_HANDLE_VALUE)
  243.         {
  244.             LPTSTR szFileName;
  245.             DWORD dwAttribs = -1; // assume failure
  246.             // We could have failed if we tried to do a FindFirstFile on the root (c:)
  247.             // or if something is really wrong, to test for this we do a GetFileAttributes (GetFileAttributesEx on NT)
  248. #ifdef WINNT
  249.             // on NT we can use GetFileAttributesEx to get both the attribs and part of the win32fd
  250.             if (GetFileAttributesEx(_szWalkBuf, GetFileExInfoStandard, (LPVOID)&_fdTopLevelFolder))
  251.             {
  252.                 // success!
  253.                 dwAttribs = _fdTopLevelFolder.dwFileAttributes;
  254.                 szFileName = PathFindFileName(_szWalkBuf);
  255.                 lstrcpyn(_fdTopLevelFolder.cFileName, szFileName, ARRAYSIZE(_fdTopLevelFolder.cFileName));
  256.                 lstrcpyn(_fdTopLevelFolder.cAlternateFileName, szFileName, ARRAYSIZE(_fdTopLevelFolder.cAlternateFileName));
  257.             }
  258. #else
  259.             // on win9x we call GetFileAttrbutes, since we dont have GetFileAttributesEx
  260.             dwAttribs = GetFileAttributes(_szWalkBuf);
  261.             if (dwAttribs != -1)
  262.             {
  263.                 // success!
  264.                 // On win95 we steal a bunch of the find data from our first child, and fake the rest. Its lame,
  265.                 // but its the best we can do.
  266.                 memcpy(&_fdTopLevelFolder, &_wfd, SIZEOF(_fdTopLevelFolder));
  267.                 _fdTopLevelFolder.dwFileAttributes = dwAttribs;
  268.                 szFileName = PathFindFileName(_szWalkBuf);
  269.                 lstrcpyn(_fdTopLevelFolder.cFileName, szFileName, ARRAYSIZE(_fdTopLevelFolder.cFileName));
  270.                 lstrcpyn(_fdTopLevelFolder.cAlternateFileName, szFileName, ARRAYSIZE(_fdTopLevelFolder.cAlternateFileName));
  271.             }
  272. #endif
  273.             if (dwAttribs == -1)
  274.             {
  275.                 // this is very bad, so we bail
  276.                 TraceMsg(TF_ERROR, "Tree Walker: GetFileAttributes/Ex(%s) failed. Stopping the walk.", _szWalkBuf);
  277.                 return E_FAIL;
  278.             }
  279.         }
  280.         else
  281.         {
  282.             // We sucessfully got the find data, good.
  283.             FindClose(hTopLevelFolder);
  284.         }
  285.         // call the callback for the first enterdir
  286.         hres = _CallCallBacks(STWCB_ENTERDIR, &_fdTopLevelFolder);
  287.     }
  288.     // Do the real tree walk here
  289.     if (hres == S_OK)
  290.     {
  291.         // always use *.* to search when we are not at our maximum level because
  292.         // we need the sub directories
  293.         // BUGBUG: this can be changed on NT by using FindFirstFileEx if WT_FOLDERONLY
  294.         LPCTSTR pszSpec = (_pszWalkSpec && (_nDepth == _nMaxDepth)) ? _pszWalkSpec : c_szStarDotStar;
  295.         if (PathCombine(_szWalkBuf, _szWalkBuf, pszSpec))
  296.         {
  297.             HDSA hdsaDelayed = NULL;  // array of found items that will be delayed and processed later
  298.             HANDLE hFind;
  299.             // Start finding the sub folders and files 
  300.             hFind = FindFirstFile(_szWalkBuf, &_wfd);
  301.             // Peel off the find spec Ex:"c:bin*.* --> c:bin" 
  302.             PathRemoveFileSpec(_szWalkBuf);
  303.             if (hFind != INVALID_HANDLE_VALUE)
  304.             {
  305.                 BOOL bDir = FALSE;
  306.                 do
  307.                 {
  308.                     //  Skip over the . and .. entries.
  309.                     if (PathIsDotOrDotDot(_wfd.cFileName))
  310.                         continue;
  311.                     bDir = BOOLIFY(IS_FILE_DIRECTORY(&_wfd));
  312.                     // If this is a file, and we are not interested in files or this file spec does not match the one we were
  313.                     // looking for. 
  314.                     if ((!bDir) && ((_dwFlags & WT_FOLDERONLY) ||
  315.                                     (_pszWalkSpec && (_nDepth < _nMaxDepth) && !PathMatchSpec(_wfd.cFileName, _pszWalkSpec))))
  316.                         continue;
  317.                     //  The following EQUAL determines whether we want to process
  318.                     //  the data found or save it in the HDSA array and process them later. 
  319.                     // Enumerate the folder or file now? (determined by the WT_FOLDERFIRST flag)
  320.                     if (bDir == BOOLIFY(_bFolderFirst))
  321.                     {
  322.                         // Yes
  323.                         hres = _ProcessAndRecurse(&_wfd);
  324.                         // if hres is failure code someone said "stop right now"
  325.                         // if hres is S_FALSE some one said "quit this directory and start with next one"
  326.                         if (hres != S_OK)
  327.                             break;
  328.                     }
  329.                     else 
  330.                     {
  331.                         // No; enumerate it once we're finished with the opposite.
  332.                         if (!hdsaDelayed)
  333.                             hdsaDelayed = DSA_Create(SIZEOF(WIN32_FIND_DATA), DELAY_ARRAY_GROW);
  334.                         if (!hdsaDelayed)
  335.                         {
  336.                             hres = E_OUTOFMEMORY;
  337.                             break;
  338.                         }
  339.                         DSA_AppendItem(hdsaDelayed, &_wfd);
  340.                     }
  341.                 } while (FindNextFile(hFind, &_wfd));
  342.                 FindClose(hFind);
  343.             }
  344.             else
  345.             {
  346.                // find first file failed, this is a good place to report error 
  347.                DWORD dwErr = GetLastError();
  348.                TraceMsg(TF_TREEWALKER, "***WARNING***: FindFirstFile faied on %s%s with error = %d", _szWalkBuf, _pszWalkSpec, dwErr);
  349.             }
  350.             // Process the delayed items, these are either directories or files
  351.             if (hdsaDelayed)
  352.             {
  353.                 // we should have finished everything in the above do while loop in folderonly case
  354.                 ASSERT(!(_dwFlags & WT_FOLDERONLY));
  355.                 // if hres is failure code someone said "stop right now"
  356.                 // if hres is S_FALSE some one said "quit this directory and start with next one"
  357.                 if (hres == S_OK)   
  358.                 {
  359.                     int ihdsa;
  360.                     for (ihdsa = 0; ihdsa < DSA_GetItemCount(hdsaDelayed); ihdsa++)
  361.                     {
  362.                         WIN32_FIND_DATA * pwfd = (WIN32_FIND_DATA *)DSA_GetItemPtr(hdsaDelayed, ihdsa);
  363.                         hres = _ProcessAndRecurse(pwfd);
  364.                         if (hres != S_OK)
  365.                             break;
  366.                     }
  367.                 }
  368.                 DSA_Destroy(hdsaDelayed);
  369.             }
  370.             // Let the CallBack object know that we are finishing the walk
  371.             if (_nDepth == 0 && !(_dwFlags & WT_EXCLUDEWALKROOT) &&
  372.                 (_dwFlags & WT_NOTIFYFOLDERLEAVE) && (S_OK == hres))
  373.                 hres = _CallCallBacks(STWCB_LEAVEDIR, &_fdTopLevelFolder);
  374.             // hres was S_FALSE because someone wanted us to jump out of this current directory
  375.             // but don't pass it back to our parent directory  
  376.             if (hres == S_FALSE)
  377.                 hres = S_OK;
  378.         }
  379.         else
  380.             TraceMsg(TF_TREEWALKER, "***WARNING***: PathCombine failed!!!!");
  381.     }
  382.     
  383.     return hres;
  384. }
  385. // IShellTreeWalker::WalkTree is the main function for the IShellTreeWalker interface 
  386. HRESULT CShellTreeWalker::WalkTree(DWORD dwFlags, LPCWSTR pwszWalkRoot, LPCWSTR pwszWalkSpec, int iMaxDepth, IShellTreeWalkerCallBack * pstwcb)
  387. {
  388.     HRESULT hres = E_FAIL;
  389.     TCHAR szWalkSpec[64];
  390.     // must have a call back object to talk to
  391.     ASSERT(IS_VALID_CODE_PTR(pstwcb, IShellTreeWalkerCackBack));
  392.     if (pstwcb == NULL)
  393.         return E_INVALIDARG;
  394.     // make sure we have a valid directory to start with
  395.     ASSERT(IS_VALID_STRING_PTRW(pwszWalkRoot, -1));
  396.     if ((pwszWalkRoot != NULL) && (pwszWalkRoot[0] != L''))
  397.     {
  398. #ifdef UNICODE  
  399.         lstrcpy(_szWalkBuf, pwszWalkRoot);
  400. #else
  401.         WideCharToMultiByte(CP_ACP, 0,
  402.                             pwszWalkRoot, -1,
  403.                             _szWalkBuf, ARRAYSIZE(_szWalkBuf), NULL, NULL);
  404. #endif
  405.         // call back 
  406.         _pstwcb = pstwcb;
  407.         // copy the search flags and fix it up  
  408.         _dwFlags = dwFlags & WT_ALL;
  409.         
  410.         // this will save us from using the hdsa array to hold the directories
  411.         if (_dwFlags & WT_FOLDERONLY)
  412.         {
  413.             _dwFlags |= WT_FOLDERFIRST;
  414.             // It will be pretty meanless if the below flags are not set, because
  415.             // we don't call FoundFile in the FolderOnly case. 
  416.             ASSERT(_dwFlags & (WT_NOTIFYFOLDERENTER | WT_NOTIFYFOLDERLEAVE));
  417.         }
  418.         if (_dwFlags & WT_FOLDERFIRST)
  419.             _bFolderFirst = TRUE;
  420.         
  421.         if ((pwszWalkSpec != NULL) && (pwszWalkSpec[0] != L''))
  422.         {
  423. #ifdef UNICODE
  424.             lstrcpyn(szWalkSpec, pwszWalkSpec, ARRAYSIZE(szWalkSpec));
  425. #else   
  426.             WideCharToMultiByte(CP_ACP, 0,
  427.                                 pwszWalkSpec, -1,
  428.                                 szWalkSpec, ARRAYSIZE(szWalkSpec), NULL, NULL);
  429. #endif      
  430.             _pszWalkSpec = szWalkSpec;
  431.         }
  432.         
  433.         _nMaxDepth = (dwFlags & WT_MAXDEPTH) ? iMaxDepth : MAX_TREE_DEPTH;
  434.         _dwClusterSize = PathGetClusterSize(_szWalkBuf);
  435.         hres = _TreeWalkerHelper();
  436.     }
  437.     else
  438.         TraceMsg(TF_WARNING, "CShellTreeWalker::WalkTree Failed! due to bad _szWalkBuf");
  439.     
  440.     return hres;
  441. }
  442. // IShellTreeWalker::QueryInterface
  443. HRESULT CShellTreeWalker::QueryInterface(REFIID riid, LPVOID * ppvObj)
  444.     ASSERT(ppvObj != NULL);
  445.     
  446.     if (IsEqualIID(riid, IID_IUnknown))
  447.     {    
  448.         *ppvObj = SAFECAST(this, IUnknown *);
  449.         TraceMsg(TF_TREEWALKER, "CShellTreeWalker::QI IUnknown succeeded");
  450.     }
  451.     else if (IsEqualIID(riid, IID_IShellTreeWalker))
  452.     {
  453.         *ppvObj = SAFECAST(this, IShellTreeWalker*);
  454.         TraceMsg(TF_TREEWALKER, "CShellTreeWalker::QI IShellTreeWalker succeeded");
  455.     } 
  456.     else
  457.     {
  458.         *ppvObj = NULL;
  459.         return E_NOINTERFACE;  
  460.     }
  461.     
  462.     AddRef();
  463.     return S_OK;
  464. }
  465. // IShellTreeWalker::AddRef
  466. ULONG CShellTreeWalker::AddRef()
  467. {
  468.     _cRef++;
  469.     TraceMsg(TF_TREEWALKER, "CShellTreeWalker()::AddRef called, new _cRef=%lX", _cRef);
  470.     return _cRef;
  471. }
  472. // IShellTreeWalker::Release
  473. ULONG CShellTreeWalker::Release()
  474. {
  475.     _cRef--;
  476.     TraceMsg(TF_TREEWALKER, "CShellTreeWalker()::Release called, new _cRef=%lX", _cRef);
  477.     if (_cRef > 0)
  478.         return _cRef;
  479.     delete this;
  480.     return 0;
  481. }
  482. STDAPI CShellTreeWalker_CreateInstance(IUnknown* pUnkOuter, REFIID riid, OUT LPVOID *  ppvOut)
  483. {
  484.     HRESULT hr = E_FAIL;
  485.     
  486.     TraceMsg(TF_TREEWALKER, "CShellTreeWalker_CreateInstance()");
  487.     *ppvOut = NULL;                     
  488.     CShellTreeWalker *pstw = new CShellTreeWalker;
  489.     if (!pstw)
  490.         return E_OUTOFMEMORY;
  491.     
  492.     hr = pstw->QueryInterface(riid, (LPVOID *)ppvOut);
  493.     pstw->Release();
  494.     
  495.     return hr;
  496. }