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

Windows Kernel

Development Platform:

Visual C++

  1. /*****************************************************************************
  2.  *
  3.  *    ftppl.cpp - FTP LPITEMIDLIST List object
  4.  *
  5.  *****************************************************************************/
  6. #include "priv.h"
  7. #include "ftppl.h"
  8. #include "ftpurl.h"
  9. typedef struct tagINETENUM
  10. {
  11.     HINTERNET               hint;
  12.     BOOL *                  pfValidhinst;
  13.     LPVOID                  pvData;
  14.     LPFNPROCESSITEMCB       pfnProcessItemCB;
  15.     LPCITEMIDLIST           pidlRoot;
  16.     HRESULT                 hr;
  17. } INETENUM;
  18. /*****************************************************************************
  19.      FUNCTION: RecursiveEnum
  20.  
  21.     DESCRIPTION:
  22.         This function will pack the parameters needed during the enum.
  23. *****************************************************************************/
  24. HRESULT CFtpPidlList::RecursiveEnum(LPCITEMIDLIST pidlRoot, LPFNPROCESSITEMCB pfnProcessItemCB, HINTERNET hint, LPVOID pvData)
  25. {
  26.     INETENUM inetEnum = {hint, NULL, pvData, pfnProcessItemCB, pidlRoot, S_OK};
  27.     Enum(RecursiveProcessPidl, (LPVOID) &inetEnum);
  28.     return inetEnum.hr;
  29. }
  30. // lParam can be: 0 == do a case sensitive search.  1 == do a case insensitive search.
  31. int CFtpPidlList::ComparePidlName(LPVOID pvPidl1, LPVOID pvPidl2, LPARAM lParam)
  32. {
  33.     DWORD dwFlags = FCMP_NORMAL;
  34.     if (lParam)
  35.         dwFlags |= FCMP_CASEINSENSE;
  36.     // return < 0 for pvPidl1 before pvPidl2.
  37.     // return == 0 for pvPidl1 equals pvPidl2.
  38.     // return > 0 for pvPidl1 after pvPidl2.
  39.     return FtpItemID_CompareIDsInt(COL_NAME, (LPCITEMIDLIST)pvPidl1, (LPCITEMIDLIST)pvPidl2, dwFlags);
  40. }
  41. HRESULT CFtpPidlList::InsertSorted(LPCITEMIDLIST pidl)
  42. {
  43.     m_pfl->InsertSorted(ILClone(pidl), CFtpPidlList::ComparePidlName, FALSE /*Case Insensitive*/);
  44.     return S_OK;
  45. };
  46. int CFtpPidlList::FindPidlIndex(LPCITEMIDLIST pidlToFind, BOOL fCaseInsensitive)
  47. {
  48.     return m_pfl->SortedSearch((LPVOID) pidlToFind, CFtpPidlList::ComparePidlName, (LPARAM)fCaseInsensitive, DPAS_SORTED);
  49. }
  50. LPITEMIDLIST CFtpPidlList::FindPidl(LPCITEMIDLIST pidlToFind, BOOL fCaseInsensitive)
  51. {
  52.     LPITEMIDLIST pidlFound = NULL;
  53.     int nIndex = FindPidlIndex(pidlToFind, fCaseInsensitive);
  54.     if (-1 != nIndex)
  55.     {
  56.         pidlFound = ILClone(GetPidl(nIndex));
  57.     }
  58.     return pidlFound;
  59. }
  60. HRESULT CFtpPidlList::CompareAndDeletePidl(LPCITEMIDLIST pidlToDelete)
  61. {
  62.     HRESULT hr = S_FALSE;
  63.     int nIndex = FindPidlIndex(pidlToDelete, FALSE /*Case Insensitive*/);
  64.     if (-1 != nIndex)
  65.     {
  66.         LPITEMIDLIST pidlCurrent = GetPidl((UINT)nIndex);
  67.         if (EVAL(pidlCurrent))
  68.         {
  69.             ASSERT(0 == FtpItemID_CompareIDsInt(COL_NAME, pidlCurrent, pidlToDelete, FCMP_NORMAL));
  70.             m_pfl->DeletePtrByIndex(nIndex);
  71.             ILFree(pidlCurrent);    // Deallocate the memory
  72.             hr = S_OK;  // Found and deleted.
  73.         }
  74.     }
  75.     return hr;
  76. }
  77. void CFtpPidlList::Delete(int nIndex)
  78. {
  79.     LPITEMIDLIST pidlToDelete = GetPidl(nIndex);
  80.     ILFree(pidlToDelete);   // Free the memory.
  81.     m_pfl->DeletePtrByIndex(nIndex);
  82. }
  83. HRESULT CFtpPidlList::ReplacePidl(LPCITEMIDLIST pidlSrc, LPCITEMIDLIST pidlDest)
  84. {
  85.     HRESULT hr = S_FALSE;
  86.     int nIndex = FindPidlIndex(pidlSrc, FALSE);
  87.     if (-1 != nIndex)
  88.     {
  89.         LPITEMIDLIST pidlCurrent = GetPidl((UINT)nIndex);
  90.         if (EVAL(pidlCurrent))
  91.         {
  92.             ASSERT(0 == FtpItemID_CompareIDsInt(COL_NAME, pidlCurrent, pidlSrc, FCMP_NORMAL));
  93.             ILFree(pidlCurrent);    // Deallocate the memory
  94.             m_pfl->DeletePtrByIndex(nIndex);
  95.             InsertSorted(pidlDest);         // This function does the ILClone()
  96.             hr = S_OK;  // Found and deleted.
  97.         }
  98.     }
  99.     return hr;
  100. }
  101. void CFtpPidlList::AssertSorted(void)
  102. {
  103. #ifdef DEBUG
  104.     // For perf reasons, we need to keep this list in order.
  105.     // This is mainly because parse display name looks thru
  106.     // the list, so we want that to be fast.
  107.     for (int nIndex = (GetCount() - 2); (nIndex >= 0); nIndex--)
  108.     {
  109.         LPITEMIDLIST pidl1 = GetPidl((UINT)nIndex);
  110.         LPITEMIDLIST pidl2 = GetPidl((UINT)nIndex + 1);
  111.         // Assert that pidl1 comes before pidl2.
  112.         if (!EVAL(0 >= FtpItemID_CompareIDsInt(COL_NAME, pidl1, pidl2, FCMP_NORMAL)))
  113.         {
  114.             TCHAR szPidl1[MAX_PATH];
  115.             TCHAR szPidl2[MAX_PATH];
  116.             if (FtpID_IsServerItemID(pidl1))
  117.                 FtpPidl_GetServer(pidl1, szPidl1, ARRAYSIZE(szPidl1));
  118.             else
  119.                 FtpPidl_GetDisplayName(pidl1, szPidl1, ARRAYSIZE(szPidl1));
  120.             if (FtpID_IsServerItemID(pidl2))
  121.                 FtpPidl_GetServer(pidl2, szPidl2, ARRAYSIZE(szPidl2));
  122.             else
  123.                 FtpPidl_GetDisplayName(pidl2, szPidl2, ARRAYSIZE(szPidl2));
  124.             TraceMsg(TF_ERROR, "CFtpPidlList::AssertSorted() '%s' & '%s' where found out of order", szPidl1, szPidl2);
  125.         }
  126.         // We do NOT need to free pidl1 or pidl2 because we get a pointer to someone else's copy.
  127.     }
  128. #endif // DEBUG
  129. }
  130. void CFtpPidlList::TraceDump(LPCITEMIDLIST pidl, LPCTSTR pszCaller)
  131. {
  132. #ifdef DEBUG
  133. /*
  134.     TCHAR szUrl[MAX_URL_STRING];
  135.     UrlCreateFromPidl(pidl, SHGDN_FORPARSING, szUrl, ARRAYSIZE(szUrl), ICU_USERNAME, FALSE);
  136.     TraceMsg(TF_PIDLLIST_DUMP, "CFtpPidlList::TraceDump() root is '%s', called from '%s'", szUrl, pszCaller);
  137.     // Let's look at the contents.
  138.     for (int nIndex = (GetCount() - 1); (nIndex >= 0); nIndex--)
  139.     {
  140.         LPITEMIDLIST pidlFull = ILCombine(pidl, GetPidl((UINT)nIndex));
  141.         if (pidlFull)
  142.         {
  143.             UrlCreateFromPidl(pidlFull, SHGDN_FORPARSING, szUrl, ARRAYSIZE(szUrl), ICU_USERNAME, FALSE);
  144.             TraceMsg(TF_PIDLLIST_DUMP, "CFtpPidlList::TraceDump() Index=%d, url=%s", nIndex, szUrl);
  145.             ILFree(pidlFull);
  146.         }
  147.     }
  148. */
  149. #endif // DEBUG
  150. }
  151. void CFtpPidlList::UseCachedDirListings(BOOL fUseCachedDirListings)
  152. {
  153.     // Normally we do two passes in the tree walker code.  The first
  154.     // pass is to count up the time required to do the download. We
  155.     // normally force WININET to not use cached results because someone
  156.     // else could have changed the contents on the server.
  157.     // On the second pass, we normally do the work (upload, download, delete)
  158.     // and we want to use the cached results to get the perf advantage
  159.     // and the results shouldn't be more than a minute out of date.
  160.     if (fUseCachedDirListings)
  161.         m_dwInetFlags = INTERNET_NO_CALLBACK;
  162.     else
  163.         m_dwInetFlags = (INTERNET_NO_CALLBACK | INTERNET_FLAG_RESYNCHRONIZE | INTERNET_FLAG_RELOAD);
  164. }
  165. BOOL CFtpPidlList::AreAllFolders(void)
  166. {
  167.     BOOL fAllFolder = TRUE;
  168.     for (int nIndex = (GetCount() - 1); fAllFolder && (nIndex >= 0); nIndex--)
  169.     {
  170.         LPITEMIDLIST pidl = GetPidl((UINT)nIndex);
  171.         if (EVAL(pidl))
  172.             fAllFolder = FtpPidl_IsDirectory(pidl, TRUE);
  173.         // We do NOT need to free pidl because we get a pointer to someone else's copy.
  174.     }
  175.     return fAllFolder;
  176. }
  177. BOOL CFtpPidlList::AreAllFiles(void)
  178. {
  179.     BOOL fAllFiles = TRUE;
  180.     for (int nIndex = (GetCount() - 1); fAllFiles && (nIndex >= 0); nIndex--)
  181.     {
  182.         LPITEMIDLIST pidl = GetPidl((UINT)nIndex);
  183.         if (EVAL(pidl))
  184.             fAllFiles = !FtpPidl_IsDirectory(pidl, TRUE);
  185.         // We do NOT need to free pidl because we get a pointer to someone else's copy.
  186.     }
  187.     return fAllFiles;
  188. }
  189. /*****************************************************************************
  190.  *
  191.  *    CFtpPidlList::_Fill
  192.  *
  193.  *    Fill a list with an array.
  194.  *
  195.  *    The elements in the array are copied rather than stolen.
  196.  *
  197.  *****************************************************************************/
  198. HRESULT CFtpPidlList::_Fill(int cpidl, LPCITEMIDLIST rgpidl[])
  199. {
  200.     HRESULT hres = S_OK;
  201.     for (int ipidl = 0; (ipidl < cpidl) && SUCCEEDED(hres); ipidl++)
  202.     {
  203.         ASSERT(IsValidPIDL(rgpidl[ipidl]));
  204.         hres = InsertSorted(rgpidl[ipidl]);
  205.     }
  206.     return hres;
  207. }
  208. /*****************************************************************************
  209.  *
  210.  *    CFtpPidlList::GetPidlList
  211.  *
  212.  *****************************************************************************/
  213. LPCITEMIDLIST * CFtpPidlList::GetPidlList(void)
  214. {
  215.     LPITEMIDLIST * ppidl;
  216.     ppidl = (LPITEMIDLIST *) LocalAlloc(LPTR, sizeof(LPITEMIDLIST) * GetCount());
  217.     if (ppidl)
  218.     {
  219.         int nIndex;
  220.         for (nIndex = 0; nIndex < GetCount(); nIndex++)
  221.         {
  222.             // Later we can make this user ILClone() if we want to be able to wack on the
  223.             // pidl list while this list is being used.
  224.             ppidl[nIndex] = GetPidl(nIndex);
  225.         }
  226.     }
  227.     return (LPCITEMIDLIST *) ppidl;
  228. }
  229. /*****************************************************************************
  230.  *
  231.  *    CFtpPidlList::FreePidlList
  232.  *
  233.  *****************************************************************************/
  234. void CFtpPidlList::FreePidlList(LPCITEMIDLIST * ppidl)
  235. {
  236.     LocalFree(ppidl);
  237. }
  238. /*****************************************************************************
  239.  *
  240.  *    CFtpPidlList_Create
  241.  *
  242.  *    Start up a new pv list, with a recommended initial size and other
  243.  *    callback info.
  244.  *
  245.  *****************************************************************************/
  246. HRESULT CFtpPidlList_Create(int cpidl, LPCITEMIDLIST rgpidl[], CFtpPidlList ** ppflpidl)
  247. {
  248.     HRESULT hres = E_OUTOFMEMORY;
  249.     CFtpPidlList * pflpidl;
  250.     *ppflpidl = pflpidl = new CFtpPidlList();
  251.     if (pflpidl)
  252.     {
  253.         hres = pflpidl->_Fill(cpidl, rgpidl);
  254.         if (!EVAL(SUCCEEDED(hres)))
  255.         {
  256.             ASSERT(pflpidl->GetCount() == 0);
  257.             IUnknown_Set(ppflpidl, NULL);
  258.         }
  259.     }
  260.     return hres;
  261. }
  262. int CALLBACK PidlListDestroyCallback(LPVOID p, LPVOID pData)
  263. {
  264.     ILFree((LPITEMIDLIST) p);
  265.     return 1;
  266. }
  267. /****************************************************
  268.     Constructor
  269. ****************************************************/
  270. CFtpPidlList::CFtpPidlList() : m_cRef(1)
  271. {
  272.     DllAddRef();
  273.     // This needs to be allocated in Zero Inited Memory.
  274.     // Assert that all Member Variables are inited to Zero.
  275.     ASSERT(!m_pfl);
  276.     
  277.     CFtpList_Create(100, PidlListDestroyCallback, 100, &m_pfl);
  278.     ASSERT(m_pfl);      // This sucks
  279.     UseCachedDirListings(FALSE);
  280.     LEAK_ADDREF(LEAK_CFtpPidlList);
  281. }
  282. /****************************************************
  283.     Destructor
  284. ****************************************************/
  285. CFtpPidlList::~CFtpPidlList()
  286. {
  287.     AssertSorted();
  288.     if (m_pfl)
  289.         m_pfl->Release();
  290.     DllRelease();
  291.     LEAK_DELREF(LEAK_CFtpPidlList);
  292. }
  293. //===========================
  294. // *** IUnknown Interface ***
  295. //===========================
  296. ULONG CFtpPidlList::AddRef()
  297. {
  298.     m_cRef++;
  299.     return m_cRef;
  300. }
  301. ULONG CFtpPidlList::Release()
  302. {
  303.     ASSERT(m_cRef > 0);
  304.     m_cRef--;
  305.     if (m_cRef > 0)
  306.         return m_cRef;
  307.     delete this;
  308.     return 0;
  309. }
  310. HRESULT CFtpPidlList::QueryInterface(REFIID riid, void **ppvObj)
  311. {
  312.     if (IsEqualIID(riid, IID_IUnknown))
  313.     {
  314.         *ppvObj = SAFECAST(this, IUnknown *);
  315.     }
  316.     else
  317.     {
  318.         TraceMsg(TF_FTPQI, "CFtpPidlList::QueryInterface() failed.");
  319.         *ppvObj = NULL;
  320.         return E_NOINTERFACE;
  321.     }
  322.     AddRef();
  323.     return S_OK;
  324. }
  325. ////////////////////////////////////////////////////////////////////
  326. // Pild List Enum Helpers
  327. ////////////////////////////////////////////////////////////////////
  328. /*****************************************************************************
  329.      FUNCTION: RecursiveProcessPidl
  330.  
  331.     DESCRIPTION:
  332.         This function will will be called for each item in the initial Pidl List
  333.     (before the recursion occurs).  This is a wrapper because the first list is
  334.     a list of pidls.  The subsequent lists are of WIN32_FIND_DATA types.
  335. *****************************************************************************/
  336. int RecursiveProcessPidl(LPVOID pvPidl, LPVOID pvInetEnum)
  337. {
  338.     LPCITEMIDLIST pidl = (LPCITEMIDLIST) pvPidl;
  339.     INETENUM * pInetEnum = (INETENUM *) pvInetEnum;
  340.     LPITEMIDLIST pidlFull = ILCombine(pInetEnum->pidlRoot, pidl);
  341.     if (EVAL(pidlFull))
  342.     {
  343.         pInetEnum->hr = pInetEnum->pfnProcessItemCB((LPVOID) pInetEnum->pfnProcessItemCB, pInetEnum->hint, pidlFull, pInetEnum->pfValidhinst, pInetEnum->pvData);
  344.         ILFree(pidlFull);
  345.     }
  346.     return (SUCCEEDED(pInetEnum->hr) ? TRUE : FALSE);
  347. }
  348. /*****************************************************************************
  349.      FUNCTION: _EnumFolderPrep
  350.  
  351.     DESCRIPTION:
  352.         This function will step into the pszDir directory and enum all of it's
  353.     contents.  For each item, it will call the callback function provided (pfnProcessItemCB).
  354.     That callback function can then call EnumFolder() again (recursively) if
  355.     there is a subfolder.
  356.     NOTE:
  357.         This function needs to first find all the items and then in a second
  358.     loop call the callback function.  This is because the WININET FTP APIs
  359.     only allow one enum to occur at a time, which may not happen if half way through
  360.     enuming one dir, a recursive call starts enuming a sub dir.
  361. *****************************************************************************/
  362. HRESULT _EnumFolderPrep(HINTERNET hint, LPCITEMIDLIST pidlFull, CFtpPidlList * pPidlList, CWireEncoding * pwe, LPITEMIDLIST * ppidlCurrFtpPath)
  363. {
  364.     HRESULT hr = S_OK;
  365.     // 1. Get Current Directory (To restore later).
  366.     hr = FtpGetCurrentDirectoryPidlWrap(hint, TRUE, pwe, ppidlCurrFtpPath);
  367.     if (EVAL(SUCCEEDED(hr)))
  368.     {
  369.         CMultiLanguageCache cmlc;
  370.         CWireEncoding we;
  371.         if (!pwe)
  372.             pwe = &we;
  373.         // It's important that this is a relative CD.
  374.         // 2. Change Directory Into the subdirectory.   
  375.         hr = FtpSetCurrentDirectoryWrap(hint, TRUE, FtpPidl_GetLastItemWireName(pidlFull));
  376.         if (SUCCEEDED(hr))
  377.         {
  378.             LPITEMIDLIST pidlItem;
  379.             HINTERNET hInetFind = NULL;
  380.             hr = FtpFindFirstFilePidlWrap(hint, TRUE, &cmlc, pwe, NULL, &pidlItem, pPidlList->m_dwInetFlags, NULL, &hInetFind);
  381.             if (hInetFind)
  382.             {
  383.                 do
  384.                 {
  385.                     LPCWIRESTR pwireStr = FtpPidl_GetLastItemWireName(pidlFull);
  386.                     if (IS_VALID_FILE(pwireStr))
  387.                     {
  388.                         // Store entire pidl (containing WIN32_FIND_DATA) so we can get
  389.                         // the attributes and other info later.  Seeing if it's a dir
  390.                         // is one need...
  391.                         pPidlList->InsertSorted(pidlItem);
  392.                     }
  393.                     ILFree(pidlItem);
  394.                     hr = InternetFindNextFilePidlWrap(hInetFind, TRUE, &cmlc, pwe, &pidlItem);
  395.                 }
  396.                 while (SUCCEEDED(hr));
  397.             
  398.                 ILFree(pidlItem);
  399.                 InternetCloseHandle(hInetFind);
  400.             }
  401.             if (ERROR_NO_MORE_FILES == HRESULT_CODE(hr))
  402.                 hr = S_OK;
  403.         }
  404.         EVAL(SUCCEEDED(pwe->ReSetCodePages(&cmlc, pPidlList)));
  405.     }
  406.     return hr;
  407. }
  408. /*****************************************************************************
  409.      FUNCTION: _GetPathDifference
  410.  
  411.     DESCRIPTION:
  412.         This function will step into the pszDir directory and enum all of it's
  413.     contents.  For each item, it will call the callback function provided (pfnProcessItemCB).
  414.     That callback function can then call EnumFolder() again (recursively) if
  415.     there is a subfolder.
  416.     NOTE:
  417.         This function needs to first find all the items and then in a second
  418.     loop call the callback function.  This is because the WININET FTP APIs
  419.     only allow one enum to occur at a time, which may not happen if half way through
  420.     enuming one dir, a recursive call starts enuming a sub dir.
  421.     PARAMETERS:
  422.         pszBaseUrl - This needs to be escaped.
  423.         pszDir - This needs to be escaped.
  424.         *ppszUrlPathDiff - This will be UnEscaped.
  425. *****************************************************************************/
  426. void _GetPathDifference(LPCTSTR pszBaseUrl, LPCTSTR pszDir, LPTSTR * ppszUrlPathDiff)
  427. {
  428.     TCHAR szUrlPathDiff[MAX_URL_STRING];
  429.     TCHAR szFullUrl[MAX_URL_STRING];
  430.     DWORD cchSize = ARRAYSIZE(szFullUrl);
  431.     // This is needed for this case:
  432.     // pszBaseUrl="ftp://server/subdir1/", pszDir="/subdir1/subdir2/file.txt"
  433.     // So, szUrlPathDiff="subdir2/file.txt" instead of pszDir
  434.     //
  435.     // ICU_NO_ENCODE is needed because Download Dlg may have paths with
  436.     // spaces that can't be escaped.
  437.     InternetCombineUrl(pszBaseUrl, pszDir, szFullUrl, &cchSize, ICU_NO_ENCODE);
  438.     UrlGetDifference(pszBaseUrl, szFullUrl, szUrlPathDiff, ARRAYSIZE(szUrlPathDiff));
  439.     // We will now use szFullUrl to store the UnEscaped version since these buffers
  440.     // are so large.
  441.     UnEscapeString(szUrlPathDiff, szFullUrl, ARRAYSIZE(szFullUrl));
  442.     Str_SetPtr(ppszUrlPathDiff, szFullUrl);
  443. }
  444. /*****************************************************************************
  445.      FUNCTION: EnumFolder
  446.  
  447.     DESCRIPTION:
  448.         This function will step into the pszDir directory and enum all of it's
  449.     contents.  For each item, it will call the callback function provided (pfnProcessItemCB).
  450.     That callback function can then call EnumFolder() again (recursively) if
  451.     there is a subfolder.
  452.     PARAMETERS:
  453.         (pszBaseUrl=ftp://server/dir1/, pszDir=dir2, DirToEnum=ftp://server/dir1/dir2/)
  454.         pszDir - This is the directory we are enumerating. (dir2)  It is relative to pszBaseUrl.
  455.         hint - The current working directory will be set to pszBaseUrl.  _EnumFolderPrep will make it go into pszDir.
  456.     NOTE:
  457.         This function needs to first find all the items and then in a second
  458.     loop call the callback function.  This is because the WININET FTP APIs
  459.     only allow one enum to occur at a time, which may not happen if half way through
  460.     enuming one dir, a recursive call starts enuming a sub dir.
  461. *****************************************************************************/
  462. HRESULT EnumFolder(LPFNPROCESSITEMCB pfnProcessItemCB, HINTERNET hint, LPCITEMIDLIST pidlFull, CWireEncoding * pwe, BOOL * pfValidhinst, LPVOID pvData)
  463. {
  464.     CFtpPidlList * pPidlList;
  465.     BOOL fValidhinst = TRUE;
  466.     HRESULT hr = CFtpPidlList_Create(0, &pidlFull, &pPidlList);
  467.     if (SUCCEEDED(hr))
  468.     {
  469.         LPITEMIDLIST pidlCurrFtpPath = NULL;
  470.         hr = _EnumFolderPrep(hint, pidlFull, pPidlList, pwe, &pidlCurrFtpPath);
  471.         if (SUCCEEDED(hr))
  472.         {
  473.             hr = S_OK;
  474.             // 4. Process each file name, which may be recursive.
  475.             // This loop and the while loop above need to be
  476.             // separated because it's not possible to create
  477.             // more than one FTP Find File handle based on the
  478.             // same session.
  479.             for (int nIndex = 0; SUCCEEDED(hr) && (nIndex < pPidlList->GetCount()); nIndex++)
  480.             {
  481.                 LPITEMIDLIST pidlNewFull = ILCombine(pidlFull, pPidlList->GetPidl(nIndex));
  482.                 hr = pfnProcessItemCB(pfnProcessItemCB, hint, pidlNewFull, &fValidhinst, pvData);
  483.                 ILFree(pidlNewFull);
  484.             }
  485.             // 5. Go back to original directory (from Step 2)
  486.             // The only time we don't want to return to the original directory is if
  487.             // the hinst was freed in an wininet callback function.  We may cache the hinst
  488.             // so we need the directory to be valid later.
  489.             if (fValidhinst)
  490.             {
  491.                 if (SUCCEEDED(hr))
  492.                 {
  493.                     // We still want to reset the directory but we don't want to over write
  494.                     // the original error message.
  495.                     hr = FtpSetCurrentDirectoryPidlWrap(hint, TRUE, pidlCurrFtpPath, TRUE, TRUE);
  496.                 }
  497.             }
  498.             Pidl_Set(&pidlCurrFtpPath, NULL);
  499.         }
  500.         pPidlList->Release();
  501.     }
  502.     if (pfValidhinst)
  503.         *pfValidhinst = fValidhinst;
  504.     return hr;
  505. }