648 lines
21 KiB
C++
648 lines
21 KiB
C++
/*****************************************************************************
|
|
*
|
|
* ftpeidl.cpp - IEnumIDList interface
|
|
*
|
|
* FtpNameCache
|
|
*
|
|
* Enumerating an FTP site is an expensive operation, because
|
|
* it can entail dialing the phone, connecting to an ISP, then
|
|
* connecting to the site, logging in, cd'ing to the appropriate
|
|
* location, pumping over an "ls" command, parsing the result,
|
|
* then closing the connection.
|
|
*
|
|
* So we cache the results of an enumeration inside a pidl list.
|
|
* If the user does a REFRESH, then we toss the list and create
|
|
* a new one.
|
|
*
|
|
* NOTE! that the WinINet API does not allow a FindFirst to be
|
|
* interrupted. In other words, once you do an FtpFindFirst,
|
|
* you must read the directory to completion and close the
|
|
* handle before you can do anything else to the site.
|
|
*
|
|
* As a result, we cannot use lazy evaluation on the enumerated
|
|
* contents. (Not that it helps any, because WinINet will just
|
|
* do an "ls", parse the output, and then hand the items back
|
|
* one element at a time via FtpFindNext. You may as well retrieve
|
|
* them all the moment they're ready.)
|
|
*
|
|
\*****************************************************************************/
|
|
|
|
#include "priv.h"
|
|
#include "ftpeidl.h"
|
|
#include "view.h"
|
|
#include "util.h"
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* We actually cache the result of the enumeration in the parent
|
|
* FtpDir, because FTP enumeration is very expensive.
|
|
*
|
|
* Since DVM_REFRESH forces us to re-enumerate, but we might have
|
|
* outstanding IEnumIDList's, we need to treat the object cache
|
|
* as yet another object that needs to be refcounted.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
|
|
/*****************************************************************************
|
|
* _fFilter
|
|
*
|
|
* Decides whether the file attributes agree with the filter criteria.
|
|
*
|
|
* If hiddens are excluded, then exclude hiddens. (Duh.)
|
|
*
|
|
* Else, include or exclude based on folder/nonfolder-ness.
|
|
*
|
|
* Let's look at that expression in slow motion.
|
|
*
|
|
* "The attributes pass the filter if both...
|
|
* (1) it passes the INCLUDEHIDDEN criterion, and
|
|
* (2) it passes the FOLDERS/NONFOLDERS criterion.
|
|
*
|
|
* The INCLUDEHIDDEN criterion is passed if FILE_ATTRIBUTE_HIDDEN
|
|
* implies SHCONTF_INCLUDEHIDDEN.
|
|
*
|
|
* The FOLDERS/NONFOLDERS criterion is passed if the appropriate bit
|
|
* is set in the shcontf, based on the actual type of the file."
|
|
*****************************************************************************/
|
|
BOOL CFtpEidl::_fFilter(DWORD shcontf, DWORD dwFAFLFlags)
|
|
{
|
|
BOOL fResult = FALSE;
|
|
|
|
if (shcontf & SHCONTF_FOLDERS)
|
|
fResult |= dwFAFLFlags & FILE_ATTRIBUTE_DIRECTORY;
|
|
|
|
if (shcontf & SHCONTF_NONFOLDERS)
|
|
fResult |= !(dwFAFLFlags & FILE_ATTRIBUTE_DIRECTORY);
|
|
|
|
if ((dwFAFLFlags & FILE_ATTRIBUTE_HIDDEN) && !(shcontf & SHCONTF_INCLUDEHIDDEN))
|
|
fResult = FALSE;
|
|
|
|
return fResult;
|
|
}
|
|
|
|
|
|
/*****************************************************************************\
|
|
* _AddFindDataToPidlList
|
|
*
|
|
* Add information in a WIN32_FIND_DATA to the cache.
|
|
* Except that dot and dotdot don't go in.
|
|
\*****************************************************************************/
|
|
HRESULT CFtpEidl::_AddFindDataToPidlList(LPCITEMIDLIST pidl)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
if (EVAL(m_pflHfpl))
|
|
{
|
|
ASSERT(IsValidPIDL(pidl));
|
|
hr = m_pflHfpl->InsertSorted(pidl);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/*****************************************************************************\
|
|
FUNCTION: _HandleSoftLinks
|
|
|
|
DESCRIPTION:
|
|
A softlink is a file on an UNIX server that reference another file or
|
|
directory. We can detect these by the fact that (pwfd->dwFileAttribes == 0).
|
|
If that is true, we have some work to do. First we find out if it's a file
|
|
or a directory by trying to ChangeCurrentWorking directories into it. If we
|
|
can we turn the dwFileAttributes from 0 to (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT).
|
|
If it's just a softlink to a file, then we change it to
|
|
(FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_REPARSE_POINT). We later use the
|
|
FILE_ATTRIBUTE_REPARSE_POINT attribute to put the shortcut overlay on it to
|
|
que the user.
|
|
|
|
RETURN VALUE:
|
|
HRESULT - If FAILED() is returned, the item will not be added to the
|
|
list view.
|
|
\*****************************************************************************/
|
|
HRESULT CFtpEidl::_HandleSoftLinks(HINTERNET hint, LPITEMIDLIST pidl, LPWIRESTR pwCurrentDir, DWORD cchSize)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
// Is it a softlink? It just came in off the wire and wininet returns 0 (zero)
|
|
// for softlinks. This function will determine if it's a SoftLink to a file
|
|
// or a directory and then set FILE_ATTRIBUTE_REPARSE_POINT or
|
|
// (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT) respectively.
|
|
if (0 == FtpPidl_GetAttributes(pidl))
|
|
{
|
|
LPCWIRESTR pwWireFileName = FtpPidl_GetFileWireName(pidl);
|
|
|
|
// Yes, so I will need to attempt to CD into that directory to test if it's a directory.
|
|
// I need to get back because ".." won't work. I will cache the return so I don't keep
|
|
// getting it if there is a directory full of them.
|
|
|
|
// Did we get the current directory yet? This is the bread crums so I can
|
|
// find my way back.
|
|
if (!pwCurrentDir[0])
|
|
EVAL(SUCCEEDED(FtpGetCurrentDirectoryWrap(hint, TRUE, pwCurrentDir, cchSize)));
|
|
|
|
// Yes, so is it a directory?
|
|
if (SUCCEEDED(FtpSetCurrentDirectoryPidlWrap(hint, TRUE, pidl, FALSE, FALSE))) // Relative CD
|
|
{
|
|
// Does it have a virtual root?
|
|
if (m_pfd->GetFtpSite()->HasVirtualRoot())
|
|
{
|
|
LPCITEMIDLIST pidlVirtualRoot = m_pfd->GetFtpSite()->GetVirtualRootReference();
|
|
LPITEMIDLIST pidlSoftLinkDest = NULL;
|
|
CWireEncoding * pwe = m_pfd->GetFtpSite()->GetCWireEncoding();
|
|
|
|
// Yes, so we need to make sure this dir softlink doesn't point
|
|
// outside of the virtual root, or it would cause invalid FTP URLs.
|
|
// File SoftLinks are fine because the old FTP Code abuses FTP URLs.
|
|
// I'm just not ready to drop my morals just yet.
|
|
if (SUCCEEDED(FtpGetCurrentDirectoryPidlWrap(hint, TRUE, pwe, &pidlSoftLinkDest)))
|
|
{
|
|
if (!FtpItemID_IsParent(pidlVirtualRoot, pidlSoftLinkDest))
|
|
{
|
|
// This is a Softlink or HardLink to a directory outside of the virtual root.
|
|
hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); // Skip this one.
|
|
}
|
|
|
|
ILFree(pidlSoftLinkDest);
|
|
}
|
|
}
|
|
|
|
// Return to where we came from.
|
|
//TraceMsg(TF_WININET_DEBUG, "_HandleSoftLinks FtpSetCurrentDirectory(%hs) worked", pwWireFileName);
|
|
EVAL(SUCCEEDED(FtpSetCurrentDirectoryWrap(hint, TRUE, pwCurrentDir))); // Absolute CD
|
|
FtpPidl_SetAttributes(pidl, (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT));
|
|
FtpPidl_SetFileItemType(pidl, TRUE);
|
|
}
|
|
else // No, it's one of those files w/o extensions.
|
|
{
|
|
TraceMsg(TF_WININET_DEBUG, "_HandleSoftLinks FtpSetCurrentDirectory(%s) failed", pwWireFileName);
|
|
FtpPidl_SetAttributes(pidl, (FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_REPARSE_POINT));
|
|
FtpPidl_SetFileItemType(pidl, FALSE);
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/*****************************************************************************\
|
|
* CFtpEidl::_PopulateItem
|
|
*
|
|
* Fill a cache with stuff.
|
|
*
|
|
* EEK! Some ftp servers (e.g., ftp.funet.fi) run with ls -F!
|
|
* This means that things get "*" appended to them if they are executable.
|
|
\*****************************************************************************/
|
|
HRESULT CFtpEidl::_PopulateItem(HINTERNET hint0, HINTPROCINFO * phpi)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
HINTERNET hint;
|
|
LPITEMIDLIST pidl;
|
|
CMultiLanguageCache cmlc;
|
|
CWireEncoding * pwe = m_pfd->GetFtpSite()->GetCWireEncoding();
|
|
|
|
if (phpi->psb)
|
|
{
|
|
phpi->psb->SetStatusMessage(IDS_LS, NULL);
|
|
EVAL(SUCCEEDED(_SetStatusBarZone(phpi->psb, phpi->pfd->GetFtpSite())));
|
|
}
|
|
|
|
hr = FtpFindFirstFilePidlWrap(hint0, TRUE, &cmlc, pwe, NULL, &pidl,
|
|
(INTERNET_NO_CALLBACK | INTERNET_FLAG_DONT_CACHE | INTERNET_FLAG_RESYNCHRONIZE | INTERNET_FLAG_RELOAD), NULL, &hint);
|
|
if (hint)
|
|
{
|
|
WIRECHAR wCurrentDir[MAX_PATH]; // Used for _HandleSoftLinks().
|
|
|
|
wCurrentDir[0] = 0;
|
|
if (EVAL(m_pff))
|
|
{
|
|
// It would be better to CoCreateInstance the History object by using
|
|
// shell32!_SHCoCreateInstance() because it doesn't require COM.
|
|
// If any more bugs are found, see if it's exported in Win95 and use it.
|
|
if (FAILED(m_hrOleInited))
|
|
{
|
|
// Win95's background enum thread doesn't call CoInitialize() so this AddToUrlHistory will fail.
|
|
// We init it ourselves.
|
|
m_hrOleInited = SHCoInitialize();
|
|
}
|
|
m_pff->AddToUrlHistory(m_pfd->GetPidlReference());
|
|
}
|
|
|
|
//TraceMsg(TF_FTP_OTHER, "CFtpEidl::_PopulateItem() adding Name=%s", wCurrentDir);
|
|
if (pidl && SUCCEEDED(_HandleSoftLinks(hint0, pidl, wCurrentDir, ARRAYSIZE(wCurrentDir))))
|
|
hr = _AddFindDataToPidlList(pidl);
|
|
|
|
ILFree(pidl);
|
|
while (SUCCEEDED(hr))
|
|
{
|
|
hr = InternetFindNextFilePidlWrap(hint, TRUE, &cmlc, pwe, &pidl);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
//TraceMsg(TF_FTP_OTHER, "CFtpEidl::_PopulateItem() adding Name=%hs", FtpPidl_GetLastItemWireName(pidl));
|
|
// We may decide to not add it for some reasons.
|
|
if (SUCCEEDED(_HandleSoftLinks(hint0, pidl, wCurrentDir, ARRAYSIZE(wCurrentDir))))
|
|
hr = _AddFindDataToPidlList(pidl);
|
|
|
|
ILFree(pidl);
|
|
}
|
|
else
|
|
{
|
|
// We failed to get the next file.
|
|
if (HRESULT_FROM_WIN32(ERROR_NO_MORE_FILES) != hr)
|
|
{
|
|
DisplayWininetError(phpi->hwnd, TRUE, HRESULT_CODE(hr), IDS_FTPERR_TITLE_ERROR, IDS_FTPERR_FOLDERENUM, IDS_FTPERR_WININET, MB_OK, NULL);
|
|
hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); // Clean error to indicate we already displayed the error and don't need to do it later.
|
|
}
|
|
else
|
|
hr = S_OK; // That's fine if there aren't any more files to get
|
|
|
|
break; // We are done here.
|
|
}
|
|
}
|
|
|
|
EVAL(SUCCEEDED(pwe->ReSetCodePages(&cmlc, m_pflHfpl)));
|
|
InternetCloseHandle(hint);
|
|
}
|
|
else
|
|
{
|
|
// This will happen in two cases.
|
|
// 1. The folder is empty. (GetLastError() == ERROR_NO_MORE_FILES)
|
|
// 2. The user doesn't have enough access to view the folder. (GetLastError() == ERROR_INTERNET_EXTENDED_ERROR)
|
|
if (HRESULT_FROM_WIN32(ERROR_NO_MORE_FILES) != hr)
|
|
{
|
|
DisplayWininetError(phpi->hwnd, TRUE, HRESULT_CODE(hr), IDS_FTPERR_TITLE_ERROR, IDS_FTPERR_OPENFOLDER, IDS_FTPERR_WININET, MB_OK, NULL);
|
|
hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); // Clean error to indicate we already displayed the error and don't need to do it later.
|
|
WININET_ASSERT(SUCCEEDED(hr));
|
|
}
|
|
else
|
|
hr = S_OK;
|
|
|
|
TraceMsg(TF_FTP_IDENUM, "CFtpEnum_New() - Can't opendir. hres=%#08lx.", hr);
|
|
}
|
|
|
|
if (phpi->psb)
|
|
phpi->psb->SetStatusMessage(IDS_EMPTY, NULL);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/*****************************************************************************\
|
|
* CFtpEidl::_Init
|
|
\*****************************************************************************/
|
|
HRESULT CFtpEidl::_Init(void)
|
|
{
|
|
HRESULT hr = S_FALSE;
|
|
|
|
ASSERT(m_pfd);
|
|
IUnknown_Set(&m_pflHfpl, NULL);
|
|
m_pflHfpl = m_pfd->GetHfpl(); // Use cached copy if it exists.
|
|
|
|
if (m_pflHfpl)
|
|
{
|
|
// We will just use the previous copy because we already have the contents.
|
|
// TODO: Maybe we want to purge the results if a certain amount of time as ellapsed.
|
|
m_fInited = TRUE;
|
|
hr = S_OK;
|
|
}
|
|
else if (!m_pfd->GetFtpSite()->IsSiteBlockedByRatings(m_hwndOwner))
|
|
{
|
|
CFtpPidlList_Create(0, NULL, &m_pflHfpl);
|
|
if (m_pflHfpl)
|
|
{
|
|
CStatusBar * psb = GetCStatusBarFromDefViewSite(_punkSite);
|
|
|
|
ASSERT(!m_pfd->IsRoot());
|
|
//TraceMsg(TF_ALWAYS, "CFtpEidl::_Init() and enumerating");
|
|
hr = m_pfd->WithHint(psb, m_hwndOwner, CFtpEidl::_PopulateItemCB, this, _punkSite, m_pff);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
m_pfd->SetCache(m_pflHfpl);
|
|
m_fInited = TRUE;
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
IUnknown_Set(&m_pflHfpl, NULL);
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* CFtpEidl::_NextOne
|
|
*****************************************************************************/
|
|
LPITEMIDLIST CFtpEidl::_NextOne(DWORD * pdwIndex)
|
|
{
|
|
LPITEMIDLIST pidl = NULL;
|
|
LPITEMIDLIST pidlResult = NULL;
|
|
|
|
if (m_pflHfpl)
|
|
{
|
|
while ((*pdwIndex < (DWORD) m_pflHfpl->GetCount()) && (pidl = m_pflHfpl->GetPidl(*pdwIndex)))
|
|
{
|
|
ASSERT(IsValidPIDL(pidl));
|
|
(*pdwIndex)++;
|
|
|
|
if (_fFilter(m_shcontf, FtpPidl_GetAttributes(pidl)))
|
|
{
|
|
pidlResult = ILClone(pidl);
|
|
break; // We don't need to search any more.
|
|
}
|
|
}
|
|
}
|
|
|
|
return pidlResult;
|
|
}
|
|
|
|
|
|
//===========================
|
|
// *** IEnumIDList Interface ***
|
|
//===========================
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* IEnumIDList::Next
|
|
*
|
|
* Creates a brand new enumerator based on an existing one.
|
|
*
|
|
*
|
|
* OLE random documentation of the day: IEnumXXX::Next.
|
|
*
|
|
* rgelt - Receives an array of size celt (or larger).
|
|
*
|
|
* "Receives an array"? No, it doesn't receive an array.
|
|
* It *is* an array. The array receives *elements*.
|
|
*
|
|
* "Or larger"? Does this mean I can return more than the caller
|
|
* asked for? No, of course not, because the caller didn't allocate
|
|
* enough memory to hold that many return values.
|
|
*
|
|
* No semantics are assigned to the possibility of celt = 0.
|
|
* Since I am a mathematician, I treat it as vacuous success.
|
|
*
|
|
* pcelt is documented as an INOUT parameter, but no semantics
|
|
* are assigned to its input value.
|
|
*
|
|
* The dox don't say that you are allowed to return *pcelt < celt
|
|
* for reasons other than "no more elements", but the shell does
|
|
* it everywhere, so maybe it's legal...
|
|
*
|
|
*****************************************************************************/
|
|
HRESULT CFtpEidl::Next(ULONG celt, LPITEMIDLIST * rgelt, ULONG *pceltFetched)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
LPITEMIDLIST pidl = NULL;
|
|
DWORD dwIndex;
|
|
// The shell on pre-NT5 enums us w/o ole initialized which causes problems
|
|
// when we call CoCreateInstance(). This happens in the thunking code
|
|
// of encode.cpp when thunking strings.
|
|
HRESULT hrOleInit = SHOleInitialize(0);
|
|
|
|
if (pceltFetched) // In case of failure.
|
|
{
|
|
*pceltFetched = 0;
|
|
}
|
|
|
|
if (m_fDead)
|
|
return E_FAIL;
|
|
|
|
if (!m_fInited)
|
|
{
|
|
hr = _Init();
|
|
if (FAILED(hr) && (HRESULT_FROM_WIN32(ERROR_CANCELLED) != hr))
|
|
{
|
|
// Did we need to redirect because of a new password or username?
|
|
if (HRESULT_FROM_WIN32(ERROR_NETWORK_ACCESS_DENIED) == hr)
|
|
{
|
|
m_fDead = TRUE;
|
|
hr = E_FAIL;
|
|
}
|
|
else if (!m_fErrorDisplayed)
|
|
{
|
|
DisplayWininetError(m_hwndOwner, FALSE, HRESULT_CODE(hr), IDS_FTPERR_TITLE_ERROR, IDS_FTPERR_GETDIRLISTING, IDS_FTPERR_WININET, MB_OK, NULL);
|
|
m_fErrorDisplayed = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
// Do they want more and do we have more to give?
|
|
for (dwIndex = 0; (dwIndex < celt) && (pidl = _NextOne(&m_nIndex)); dwIndex++)
|
|
rgelt[dwIndex] = pidl; // Yes, so give away...
|
|
|
|
if (pceltFetched)
|
|
*pceltFetched = dwIndex;
|
|
|
|
// Were we able to give any?
|
|
if (0 == dwIndex)
|
|
hr = S_FALSE;
|
|
}
|
|
|
|
SHOleUninitialize(hrOleInit);
|
|
return hr;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* IEnumIDList::Skip
|
|
*****************************************************************************/
|
|
|
|
HRESULT CFtpEidl::Skip(ULONG celt)
|
|
{
|
|
m_nIndex += celt;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* IEnumIDList::Reset
|
|
*****************************************************************************/
|
|
|
|
HRESULT CFtpEidl::Reset(void)
|
|
{
|
|
m_fErrorDisplayed = FALSE;
|
|
if (!m_fInited)
|
|
_Init();
|
|
|
|
m_nIndex = 0;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/*****************************************************************************\
|
|
* IEnumIDList::Clone
|
|
*
|
|
* Creates a brand new enumerator based on an existing one.
|
|
\*****************************************************************************/
|
|
HRESULT CFtpEidl::Clone(IEnumIDList **ppenum)
|
|
{
|
|
return CFtpEidl_Create(m_pfd, m_pff, m_hwndOwner, m_shcontf, m_nIndex, ppenum);
|
|
}
|
|
|
|
|
|
/*****************************************************************************\
|
|
* CFtpEidl_Create
|
|
*
|
|
* Creates a brand new enumerator based on an ftp site.
|
|
\*****************************************************************************/
|
|
HRESULT CFtpEidl_Create(CFtpDir * pfd, CFtpFolder * pff, HWND hwndOwner, DWORD shcontf, IEnumIDList ** ppenum)
|
|
{
|
|
CFtpEidl * pfe;
|
|
HRESULT hres = CFtpEidl_Create(pfd, pff, hwndOwner, shcontf, &pfe);
|
|
|
|
*ppenum = NULL;
|
|
if (pfe)
|
|
{
|
|
hres = pfe->QueryInterface(IID_IEnumIDList, (LPVOID *) ppenum);
|
|
pfe->Release();
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* CFtpEidl_Create
|
|
*
|
|
* Creates a brand new enumerator based on an ftp site.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
HRESULT CFtpEidl_Create(CFtpDir * pfd, CFtpFolder * pff, HWND hwndOwner, DWORD shcontf, CFtpEidl ** ppfe)
|
|
{
|
|
CFtpEidl * pfe = new CFtpEidl();
|
|
HRESULT hr = E_OUTOFMEMORY;
|
|
|
|
ASSERT(pfd && pff && ppfe);
|
|
*ppfe = pfe;
|
|
if (pfe)
|
|
{
|
|
ATOMICRELEASE(pfe->m_pm);
|
|
pfe->m_pm = pff->GetIMalloc();
|
|
|
|
IUnknown_Set(&pfe->m_pff, pff);
|
|
IUnknown_Set(&pfe->m_pfd, pfd);
|
|
pfe->m_pflHfpl = pfd->GetHfpl();
|
|
|
|
pfe->m_shcontf = shcontf;
|
|
pfe->m_hwndOwner = hwndOwner;
|
|
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/*****************************************************************************\
|
|
* CFtpEidl_Create
|
|
*
|
|
* Creates a brand new enumerator based on an ftp site.
|
|
\*****************************************************************************/
|
|
HRESULT CFtpEidl_Create(CFtpDir * pfd, CFtpFolder * pff, HWND hwndOwner, DWORD shcontf, DWORD dwIndex, IEnumIDList ** ppenum)
|
|
{
|
|
CFtpEidl * pfe;
|
|
HRESULT hres = CFtpEidl_Create(pfd, pff, hwndOwner, shcontf, &pfe);
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
pfe->m_nIndex = dwIndex;
|
|
|
|
hres = pfe->QueryInterface(IID_IEnumIDList, (LPVOID *) ppenum);
|
|
ASSERT(SUCCEEDED(hres));
|
|
|
|
pfe->Release();
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/****************************************************\
|
|
Constructor
|
|
\****************************************************/
|
|
CFtpEidl::CFtpEidl() : m_cRef(1)
|
|
{
|
|
DllAddRef();
|
|
|
|
// This needs to be allocated in Zero Inited Memory.
|
|
// Assert that all Member Variables are inited to Zero.
|
|
ASSERT(!m_fInited);
|
|
ASSERT(!m_nIndex);
|
|
ASSERT(!m_shcontf);
|
|
ASSERT(!m_pflHfpl);
|
|
ASSERT(!m_pfd);
|
|
ASSERT(!m_pm);
|
|
ASSERT(!m_hwndOwner);
|
|
ASSERT(!m_fInited);
|
|
ASSERT(!m_fDead);
|
|
|
|
m_hrOleInited = E_FAIL;
|
|
LEAK_ADDREF(LEAK_CFtpEidl);
|
|
}
|
|
|
|
|
|
/****************************************************\
|
|
Destructor
|
|
\****************************************************/
|
|
CFtpEidl::~CFtpEidl()
|
|
{
|
|
IUnknown_Set(&m_pflHfpl, NULL);
|
|
IUnknown_Set(&m_pm, NULL);
|
|
IUnknown_Set(&m_pfd, NULL);
|
|
IUnknown_Set(&m_pff, NULL);
|
|
|
|
DllRelease();
|
|
LEAK_DELREF(LEAK_CFtpEidl);
|
|
SHCoUninitialize(m_hrOleInited);
|
|
}
|
|
|
|
|
|
//===========================
|
|
// *** IUnknown Interface ***
|
|
//===========================
|
|
|
|
ULONG CFtpEidl::AddRef()
|
|
{
|
|
m_cRef++;
|
|
return m_cRef;
|
|
}
|
|
|
|
ULONG CFtpEidl::Release()
|
|
{
|
|
ASSERT(m_cRef > 0);
|
|
m_cRef--;
|
|
|
|
if (m_cRef > 0)
|
|
return m_cRef;
|
|
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
HRESULT CFtpEidl::QueryInterface(REFIID riid, void **ppvObj)
|
|
{
|
|
if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IEnumIDList))
|
|
{
|
|
*ppvObj = SAFECAST(this, IEnumIDList*);
|
|
}
|
|
else if (IsEqualIID(riid, IID_IObjectWithSite))
|
|
{
|
|
*ppvObj = SAFECAST(this, IObjectWithSite*);
|
|
}
|
|
else
|
|
{
|
|
TraceMsg(TF_FTPQI, "CFtpEidl::QueryInterface() failed.");
|
|
*ppvObj = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
AddRef();
|
|
return S_OK;
|
|
}
|