Source code of Windows XP (NT5)
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.

647 lines
21 KiB

  1. /*****************************************************************************
  2. *
  3. * ftpeidl.cpp - IEnumIDList interface
  4. *
  5. * FtpNameCache
  6. *
  7. * Enumerating an FTP site is an expensive operation, because
  8. * it can entail dialing the phone, connecting to an ISP, then
  9. * connecting to the site, logging in, cd'ing to the appropriate
  10. * location, pumping over an "ls" command, parsing the result,
  11. * then closing the connection.
  12. *
  13. * So we cache the results of an enumeration inside a pidl list.
  14. * If the user does a REFRESH, then we toss the list and create
  15. * a new one.
  16. *
  17. * NOTE! that the WinINet API does not allow a FindFirst to be
  18. * interrupted. In other words, once you do an FtpFindFirst,
  19. * you must read the directory to completion and close the
  20. * handle before you can do anything else to the site.
  21. *
  22. * As a result, we cannot use lazy evaluation on the enumerated
  23. * contents. (Not that it helps any, because WinINet will just
  24. * do an "ls", parse the output, and then hand the items back
  25. * one element at a time via FtpFindNext. You may as well retrieve
  26. * them all the moment they're ready.)
  27. *
  28. \*****************************************************************************/
  29. #include "priv.h"
  30. #include "ftpeidl.h"
  31. #include "view.h"
  32. #include "util.h"
  33. /*****************************************************************************
  34. *
  35. * We actually cache the result of the enumeration in the parent
  36. * FtpDir, because FTP enumeration is very expensive.
  37. *
  38. * Since DVM_REFRESH forces us to re-enumerate, but we might have
  39. * outstanding IEnumIDList's, we need to treat the object cache
  40. * as yet another object that needs to be refcounted.
  41. *
  42. *****************************************************************************/
  43. /*****************************************************************************
  44. * _fFilter
  45. *
  46. * Decides whether the file attributes agree with the filter criteria.
  47. *
  48. * If hiddens are excluded, then exclude hiddens. (Duh.)
  49. *
  50. * Else, include or exclude based on folder/nonfolder-ness.
  51. *
  52. * Let's look at that expression in slow motion.
  53. *
  54. * "The attributes pass the filter if both...
  55. * (1) it passes the INCLUDEHIDDEN criterion, and
  56. * (2) it passes the FOLDERS/NONFOLDERS criterion.
  57. *
  58. * The INCLUDEHIDDEN criterion is passed if FILE_ATTRIBUTE_HIDDEN
  59. * implies SHCONTF_INCLUDEHIDDEN.
  60. *
  61. * The FOLDERS/NONFOLDERS criterion is passed if the appropriate bit
  62. * is set in the shcontf, based on the actual type of the file."
  63. *****************************************************************************/
  64. BOOL CFtpEidl::_fFilter(DWORD shcontf, DWORD dwFAFLFlags)
  65. {
  66. BOOL fResult = FALSE;
  67. if (shcontf & SHCONTF_FOLDERS)
  68. fResult |= dwFAFLFlags & FILE_ATTRIBUTE_DIRECTORY;
  69. if (shcontf & SHCONTF_NONFOLDERS)
  70. fResult |= !(dwFAFLFlags & FILE_ATTRIBUTE_DIRECTORY);
  71. if ((dwFAFLFlags & FILE_ATTRIBUTE_HIDDEN) && !(shcontf & SHCONTF_INCLUDEHIDDEN))
  72. fResult = FALSE;
  73. return fResult;
  74. }
  75. /*****************************************************************************\
  76. * _AddFindDataToPidlList
  77. *
  78. * Add information in a WIN32_FIND_DATA to the cache.
  79. * Except that dot and dotdot don't go in.
  80. \*****************************************************************************/
  81. HRESULT CFtpEidl::_AddFindDataToPidlList(LPCITEMIDLIST pidl)
  82. {
  83. HRESULT hr = E_FAIL;
  84. if (EVAL(m_pflHfpl))
  85. {
  86. ASSERT(IsValidPIDL(pidl));
  87. hr = m_pflHfpl->InsertSorted(pidl);
  88. }
  89. return hr;
  90. }
  91. /*****************************************************************************\
  92. FUNCTION: _HandleSoftLinks
  93. DESCRIPTION:
  94. A softlink is a file on an UNIX server that reference another file or
  95. directory. We can detect these by the fact that (pwfd->dwFileAttribes == 0).
  96. If that is true, we have some work to do. First we find out if it's a file
  97. or a directory by trying to ChangeCurrentWorking directories into it. If we
  98. can we turn the dwFileAttributes from 0 to (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT).
  99. If it's just a softlink to a file, then we change it to
  100. (FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_REPARSE_POINT). We later use the
  101. FILE_ATTRIBUTE_REPARSE_POINT attribute to put the shortcut overlay on it to
  102. que the user.
  103. RETURN VALUE:
  104. HRESULT - If FAILED() is returned, the item will not be added to the
  105. list view.
  106. \*****************************************************************************/
  107. HRESULT CFtpEidl::_HandleSoftLinks(HINTERNET hint, LPITEMIDLIST pidl, LPWIRESTR pwCurrentDir, DWORD cchSize)
  108. {
  109. HRESULT hr = S_OK;
  110. // Is it a softlink? It just came in off the wire and wininet returns 0 (zero)
  111. // for softlinks. This function will determine if it's a SoftLink to a file
  112. // or a directory and then set FILE_ATTRIBUTE_REPARSE_POINT or
  113. // (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT) respectively.
  114. if (0 == FtpPidl_GetAttributes(pidl))
  115. {
  116. LPCWIRESTR pwWireFileName = FtpPidl_GetFileWireName(pidl);
  117. // Yes, so I will need to attempt to CD into that directory to test if it's a directory.
  118. // I need to get back because ".." won't work. I will cache the return so I don't keep
  119. // getting it if there is a directory full of them.
  120. // Did we get the current directory yet? This is the bread crums so I can
  121. // find my way back.
  122. if (!pwCurrentDir[0])
  123. EVAL(SUCCEEDED(FtpGetCurrentDirectoryWrap(hint, TRUE, pwCurrentDir, cchSize)));
  124. // Yes, so is it a directory?
  125. if (SUCCEEDED(FtpSetCurrentDirectoryPidlWrap(hint, TRUE, pidl, FALSE, FALSE))) // Relative CD
  126. {
  127. // Does it have a virtual root?
  128. if (m_pfd->GetFtpSite()->HasVirtualRoot())
  129. {
  130. LPCITEMIDLIST pidlVirtualRoot = m_pfd->GetFtpSite()->GetVirtualRootReference();
  131. LPITEMIDLIST pidlSoftLinkDest = NULL;
  132. CWireEncoding * pwe = m_pfd->GetFtpSite()->GetCWireEncoding();
  133. // Yes, so we need to make sure this dir softlink doesn't point
  134. // outside of the virtual root, or it would cause invalid FTP URLs.
  135. // File SoftLinks are fine because the old FTP Code abuses FTP URLs.
  136. // I'm just not ready to drop my morals just yet.
  137. if (SUCCEEDED(FtpGetCurrentDirectoryPidlWrap(hint, TRUE, pwe, &pidlSoftLinkDest)))
  138. {
  139. if (!FtpItemID_IsParent(pidlVirtualRoot, pidlSoftLinkDest))
  140. {
  141. // This is a Softlink or HardLink to a directory outside of the virtual root.
  142. hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); // Skip this one.
  143. }
  144. ILFree(pidlSoftLinkDest);
  145. }
  146. }
  147. // Return to where we came from.
  148. //TraceMsg(TF_WININET_DEBUG, "_HandleSoftLinks FtpSetCurrentDirectory(%hs) worked", pwWireFileName);
  149. EVAL(SUCCEEDED(FtpSetCurrentDirectoryWrap(hint, TRUE, pwCurrentDir))); // Absolute CD
  150. FtpPidl_SetAttributes(pidl, (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT));
  151. FtpPidl_SetFileItemType(pidl, TRUE);
  152. }
  153. else // No, it's one of those files w/o extensions.
  154. {
  155. TraceMsg(TF_WININET_DEBUG, "_HandleSoftLinks FtpSetCurrentDirectory(%s) failed", pwWireFileName);
  156. FtpPidl_SetAttributes(pidl, (FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_REPARSE_POINT));
  157. FtpPidl_SetFileItemType(pidl, FALSE);
  158. }
  159. }
  160. return hr;
  161. }
  162. /*****************************************************************************\
  163. * CFtpEidl::_PopulateItem
  164. *
  165. * Fill a cache with stuff.
  166. *
  167. * EEK! Some ftp servers (e.g., ftp.funet.fi) run with ls -F!
  168. * This means that things get "*" appended to them if they are executable.
  169. \*****************************************************************************/
  170. HRESULT CFtpEidl::_PopulateItem(HINTERNET hint0, HINTPROCINFO * phpi)
  171. {
  172. HRESULT hr = S_OK;
  173. HINTERNET hint;
  174. LPITEMIDLIST pidl;
  175. CMultiLanguageCache cmlc;
  176. CWireEncoding * pwe = m_pfd->GetFtpSite()->GetCWireEncoding();
  177. if (phpi->psb)
  178. {
  179. phpi->psb->SetStatusMessage(IDS_LS, NULL);
  180. EVAL(SUCCEEDED(_SetStatusBarZone(phpi->psb, phpi->pfd->GetFtpSite())));
  181. }
  182. hr = FtpFindFirstFilePidlWrap(hint0, TRUE, &cmlc, pwe, NULL, &pidl,
  183. (INTERNET_NO_CALLBACK | INTERNET_FLAG_DONT_CACHE | INTERNET_FLAG_RESYNCHRONIZE | INTERNET_FLAG_RELOAD), NULL, &hint);
  184. if (hint)
  185. {
  186. WIRECHAR wCurrentDir[MAX_PATH]; // Used for _HandleSoftLinks().
  187. wCurrentDir[0] = 0;
  188. if (EVAL(m_pff))
  189. {
  190. // It would be better to CoCreateInstance the History object by using
  191. // shell32!_SHCoCreateInstance() because it doesn't require COM.
  192. // If any more bugs are found, see if it's exported in Win95 and use it.
  193. if (FAILED(m_hrOleInited))
  194. {
  195. // Win95's background enum thread doesn't call CoInitialize() so this AddToUrlHistory will fail.
  196. // We init it ourselves.
  197. m_hrOleInited = SHCoInitialize();
  198. }
  199. m_pff->AddToUrlHistory(m_pfd->GetPidlReference());
  200. }
  201. //TraceMsg(TF_FTP_OTHER, "CFtpEidl::_PopulateItem() adding Name=%s", wCurrentDir);
  202. if (pidl && SUCCEEDED(_HandleSoftLinks(hint0, pidl, wCurrentDir, ARRAYSIZE(wCurrentDir))))
  203. hr = _AddFindDataToPidlList(pidl);
  204. ILFree(pidl);
  205. while (SUCCEEDED(hr))
  206. {
  207. hr = InternetFindNextFilePidlWrap(hint, TRUE, &cmlc, pwe, &pidl);
  208. if (SUCCEEDED(hr))
  209. {
  210. //TraceMsg(TF_FTP_OTHER, "CFtpEidl::_PopulateItem() adding Name=%hs", FtpPidl_GetLastItemWireName(pidl));
  211. // We may decide to not add it for some reasons.
  212. if (SUCCEEDED(_HandleSoftLinks(hint0, pidl, wCurrentDir, ARRAYSIZE(wCurrentDir))))
  213. hr = _AddFindDataToPidlList(pidl);
  214. ILFree(pidl);
  215. }
  216. else
  217. {
  218. // We failed to get the next file.
  219. if (HRESULT_FROM_WIN32(ERROR_NO_MORE_FILES) != hr)
  220. {
  221. DisplayWininetError(phpi->hwnd, TRUE, HRESULT_CODE(hr), IDS_FTPERR_TITLE_ERROR, IDS_FTPERR_FOLDERENUM, IDS_FTPERR_WININET, MB_OK, NULL);
  222. hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); // Clean error to indicate we already displayed the error and don't need to do it later.
  223. }
  224. else
  225. hr = S_OK; // That's fine if there aren't any more files to get
  226. break; // We are done here.
  227. }
  228. }
  229. EVAL(SUCCEEDED(pwe->ReSetCodePages(&cmlc, m_pflHfpl)));
  230. InternetCloseHandle(hint);
  231. }
  232. else
  233. {
  234. // This will happen in two cases.
  235. // 1. The folder is empty. (GetLastError() == ERROR_NO_MORE_FILES)
  236. // 2. The user doesn't have enough access to view the folder. (GetLastError() == ERROR_INTERNET_EXTENDED_ERROR)
  237. if (HRESULT_FROM_WIN32(ERROR_NO_MORE_FILES) != hr)
  238. {
  239. DisplayWininetError(phpi->hwnd, TRUE, HRESULT_CODE(hr), IDS_FTPERR_TITLE_ERROR, IDS_FTPERR_OPENFOLDER, IDS_FTPERR_WININET, MB_OK, NULL);
  240. hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); // Clean error to indicate we already displayed the error and don't need to do it later.
  241. WININET_ASSERT(SUCCEEDED(hr));
  242. }
  243. else
  244. hr = S_OK;
  245. TraceMsg(TF_FTP_IDENUM, "CFtpEnum_New() - Can't opendir. hres=%#08lx.", hr);
  246. }
  247. if (phpi->psb)
  248. phpi->psb->SetStatusMessage(IDS_EMPTY, NULL);
  249. return hr;
  250. }
  251. /*****************************************************************************\
  252. * CFtpEidl::_Init
  253. \*****************************************************************************/
  254. HRESULT CFtpEidl::_Init(void)
  255. {
  256. HRESULT hr = S_FALSE;
  257. ASSERT(m_pfd);
  258. IUnknown_Set(&m_pflHfpl, NULL);
  259. m_pflHfpl = m_pfd->GetHfpl(); // Use cached copy if it exists.
  260. if (m_pflHfpl)
  261. {
  262. // We will just use the previous copy because we already have the contents.
  263. // TODO: Maybe we want to purge the results if a certain amount of time as ellapsed.
  264. m_fInited = TRUE;
  265. hr = S_OK;
  266. }
  267. else if (!m_pfd->GetFtpSite()->IsSiteBlockedByRatings(m_hwndOwner))
  268. {
  269. CFtpPidlList_Create(0, NULL, &m_pflHfpl);
  270. if (m_pflHfpl)
  271. {
  272. CStatusBar * psb = GetCStatusBarFromDefViewSite(_punkSite);
  273. ASSERT(!m_pfd->IsRoot());
  274. //TraceMsg(TF_ALWAYS, "CFtpEidl::_Init() and enumerating");
  275. hr = m_pfd->WithHint(psb, m_hwndOwner, CFtpEidl::_PopulateItemCB, this, _punkSite, m_pff);
  276. if (SUCCEEDED(hr))
  277. {
  278. m_pfd->SetCache(m_pflHfpl);
  279. m_fInited = TRUE;
  280. hr = S_OK;
  281. }
  282. else
  283. IUnknown_Set(&m_pflHfpl, NULL);
  284. }
  285. }
  286. return hr;
  287. }
  288. /*****************************************************************************
  289. * CFtpEidl::_NextOne
  290. *****************************************************************************/
  291. LPITEMIDLIST CFtpEidl::_NextOne(DWORD * pdwIndex)
  292. {
  293. LPITEMIDLIST pidl = NULL;
  294. LPITEMIDLIST pidlResult = NULL;
  295. if (m_pflHfpl)
  296. {
  297. while ((*pdwIndex < (DWORD) m_pflHfpl->GetCount()) && (pidl = m_pflHfpl->GetPidl(*pdwIndex)))
  298. {
  299. ASSERT(IsValidPIDL(pidl));
  300. (*pdwIndex)++;
  301. if (_fFilter(m_shcontf, FtpPidl_GetAttributes(pidl)))
  302. {
  303. pidlResult = ILClone(pidl);
  304. break; // We don't need to search any more.
  305. }
  306. }
  307. }
  308. return pidlResult;
  309. }
  310. //===========================
  311. // *** IEnumIDList Interface ***
  312. //===========================
  313. /*****************************************************************************
  314. *
  315. * IEnumIDList::Next
  316. *
  317. * Creates a brand new enumerator based on an existing one.
  318. *
  319. *
  320. * OLE random documentation of the day: IEnumXXX::Next.
  321. *
  322. * rgelt - Receives an array of size celt (or larger).
  323. *
  324. * "Receives an array"? No, it doesn't receive an array.
  325. * It *is* an array. The array receives *elements*.
  326. *
  327. * "Or larger"? Does this mean I can return more than the caller
  328. * asked for? No, of course not, because the caller didn't allocate
  329. * enough memory to hold that many return values.
  330. *
  331. * No semantics are assigned to the possibility of celt = 0.
  332. * Since I am a mathematician, I treat it as vacuous success.
  333. *
  334. * pcelt is documented as an INOUT parameter, but no semantics
  335. * are assigned to its input value.
  336. *
  337. * The dox don't say that you are allowed to return *pcelt < celt
  338. * for reasons other than "no more elements", but the shell does
  339. * it everywhere, so maybe it's legal...
  340. *
  341. *****************************************************************************/
  342. HRESULT CFtpEidl::Next(ULONG celt, LPITEMIDLIST * rgelt, ULONG *pceltFetched)
  343. {
  344. HRESULT hr = S_OK;
  345. LPITEMIDLIST pidl = NULL;
  346. DWORD dwIndex;
  347. // The shell on pre-NT5 enums us w/o ole initialized which causes problems
  348. // when we call CoCreateInstance(). This happens in the thunking code
  349. // of encode.cpp when thunking strings.
  350. HRESULT hrOleInit = SHOleInitialize(0);
  351. if (pceltFetched) // In case of failure.
  352. {
  353. *pceltFetched = 0;
  354. }
  355. if (m_fDead)
  356. return E_FAIL;
  357. if (!m_fInited)
  358. {
  359. hr = _Init();
  360. if (FAILED(hr) && (HRESULT_FROM_WIN32(ERROR_CANCELLED) != hr))
  361. {
  362. // Did we need to redirect because of a new password or username?
  363. if (HRESULT_FROM_WIN32(ERROR_NETWORK_ACCESS_DENIED) == hr)
  364. {
  365. m_fDead = TRUE;
  366. hr = E_FAIL;
  367. }
  368. else if (!m_fErrorDisplayed)
  369. {
  370. DisplayWininetError(m_hwndOwner, FALSE, HRESULT_CODE(hr), IDS_FTPERR_TITLE_ERROR, IDS_FTPERR_GETDIRLISTING, IDS_FTPERR_WININET, MB_OK, NULL);
  371. m_fErrorDisplayed = TRUE;
  372. }
  373. }
  374. }
  375. if (S_OK == hr)
  376. {
  377. // Do they want more and do we have more to give?
  378. for (dwIndex = 0; (dwIndex < celt) && (pidl = _NextOne(&m_nIndex)); dwIndex++)
  379. rgelt[dwIndex] = pidl; // Yes, so give away...
  380. if (pceltFetched)
  381. *pceltFetched = dwIndex;
  382. // Were we able to give any?
  383. if (0 == dwIndex)
  384. hr = S_FALSE;
  385. }
  386. SHOleUninitialize(hrOleInit);
  387. return hr;
  388. }
  389. /*****************************************************************************
  390. * IEnumIDList::Skip
  391. *****************************************************************************/
  392. HRESULT CFtpEidl::Skip(ULONG celt)
  393. {
  394. m_nIndex += celt;
  395. return S_OK;
  396. }
  397. /*****************************************************************************
  398. * IEnumIDList::Reset
  399. *****************************************************************************/
  400. HRESULT CFtpEidl::Reset(void)
  401. {
  402. m_fErrorDisplayed = FALSE;
  403. if (!m_fInited)
  404. _Init();
  405. m_nIndex = 0;
  406. return S_OK;
  407. }
  408. /*****************************************************************************\
  409. * IEnumIDList::Clone
  410. *
  411. * Creates a brand new enumerator based on an existing one.
  412. \*****************************************************************************/
  413. HRESULT CFtpEidl::Clone(IEnumIDList **ppenum)
  414. {
  415. return CFtpEidl_Create(m_pfd, m_pff, m_hwndOwner, m_shcontf, m_nIndex, ppenum);
  416. }
  417. /*****************************************************************************\
  418. * CFtpEidl_Create
  419. *
  420. * Creates a brand new enumerator based on an ftp site.
  421. \*****************************************************************************/
  422. HRESULT CFtpEidl_Create(CFtpDir * pfd, CFtpFolder * pff, HWND hwndOwner, DWORD shcontf, IEnumIDList ** ppenum)
  423. {
  424. CFtpEidl * pfe;
  425. HRESULT hres = CFtpEidl_Create(pfd, pff, hwndOwner, shcontf, &pfe);
  426. *ppenum = NULL;
  427. if (pfe)
  428. {
  429. hres = pfe->QueryInterface(IID_IEnumIDList, (LPVOID *) ppenum);
  430. pfe->Release();
  431. }
  432. return hres;
  433. }
  434. /*****************************************************************************
  435. *
  436. * CFtpEidl_Create
  437. *
  438. * Creates a brand new enumerator based on an ftp site.
  439. *
  440. *****************************************************************************/
  441. HRESULT CFtpEidl_Create(CFtpDir * pfd, CFtpFolder * pff, HWND hwndOwner, DWORD shcontf, CFtpEidl ** ppfe)
  442. {
  443. CFtpEidl * pfe = new CFtpEidl();
  444. HRESULT hr = E_OUTOFMEMORY;
  445. ASSERT(pfd && pff && ppfe);
  446. *ppfe = pfe;
  447. if (pfe)
  448. {
  449. ATOMICRELEASE(pfe->m_pm);
  450. pfe->m_pm = pff->GetIMalloc();
  451. IUnknown_Set(&pfe->m_pff, pff);
  452. IUnknown_Set(&pfe->m_pfd, pfd);
  453. pfe->m_pflHfpl = pfd->GetHfpl();
  454. pfe->m_shcontf = shcontf;
  455. pfe->m_hwndOwner = hwndOwner;
  456. }
  457. return hr;
  458. }
  459. /*****************************************************************************\
  460. * CFtpEidl_Create
  461. *
  462. * Creates a brand new enumerator based on an ftp site.
  463. \*****************************************************************************/
  464. HRESULT CFtpEidl_Create(CFtpDir * pfd, CFtpFolder * pff, HWND hwndOwner, DWORD shcontf, DWORD dwIndex, IEnumIDList ** ppenum)
  465. {
  466. CFtpEidl * pfe;
  467. HRESULT hres = CFtpEidl_Create(pfd, pff, hwndOwner, shcontf, &pfe);
  468. if (SUCCEEDED(hres))
  469. {
  470. pfe->m_nIndex = dwIndex;
  471. hres = pfe->QueryInterface(IID_IEnumIDList, (LPVOID *) ppenum);
  472. ASSERT(SUCCEEDED(hres));
  473. pfe->Release();
  474. }
  475. return hres;
  476. }
  477. /****************************************************\
  478. Constructor
  479. \****************************************************/
  480. CFtpEidl::CFtpEidl() : m_cRef(1)
  481. {
  482. DllAddRef();
  483. // This needs to be allocated in Zero Inited Memory.
  484. // Assert that all Member Variables are inited to Zero.
  485. ASSERT(!m_fInited);
  486. ASSERT(!m_nIndex);
  487. ASSERT(!m_shcontf);
  488. ASSERT(!m_pflHfpl);
  489. ASSERT(!m_pfd);
  490. ASSERT(!m_pm);
  491. ASSERT(!m_hwndOwner);
  492. ASSERT(!m_fInited);
  493. ASSERT(!m_fDead);
  494. m_hrOleInited = E_FAIL;
  495. LEAK_ADDREF(LEAK_CFtpEidl);
  496. }
  497. /****************************************************\
  498. Destructor
  499. \****************************************************/
  500. CFtpEidl::~CFtpEidl()
  501. {
  502. IUnknown_Set(&m_pflHfpl, NULL);
  503. IUnknown_Set(&m_pm, NULL);
  504. IUnknown_Set(&m_pfd, NULL);
  505. IUnknown_Set(&m_pff, NULL);
  506. DllRelease();
  507. LEAK_DELREF(LEAK_CFtpEidl);
  508. SHCoUninitialize(m_hrOleInited);
  509. }
  510. //===========================
  511. // *** IUnknown Interface ***
  512. //===========================
  513. ULONG CFtpEidl::AddRef()
  514. {
  515. m_cRef++;
  516. return m_cRef;
  517. }
  518. ULONG CFtpEidl::Release()
  519. {
  520. ASSERT(m_cRef > 0);
  521. m_cRef--;
  522. if (m_cRef > 0)
  523. return m_cRef;
  524. delete this;
  525. return 0;
  526. }
  527. HRESULT CFtpEidl::QueryInterface(REFIID riid, void **ppvObj)
  528. {
  529. if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IEnumIDList))
  530. {
  531. *ppvObj = SAFECAST(this, IEnumIDList*);
  532. }
  533. else if (IsEqualIID(riid, IID_IObjectWithSite))
  534. {
  535. *ppvObj = SAFECAST(this, IObjectWithSite*);
  536. }
  537. else
  538. {
  539. TraceMsg(TF_FTPQI, "CFtpEidl::QueryInterface() failed.");
  540. *ppvObj = NULL;
  541. return E_NOINTERFACE;
  542. }
  543. AddRef();
  544. return S_OK;
  545. }