|
|
/*****************************************************************************
* * ftp.cpp - FTP folder bookkeeping * *****************************************************************************/
#include "priv.h"
#include "ftpinet.h"
#include "ftpsite.h"
#include "ftplist.h"
#include "msieftp.h"
#include "cookie.h"
extern CFtpList * g_FtpSiteCache; extern DWORD g_dwOpenConnections;
/*****************************************************************************
* * Dynamic Globals. There should be as few of these as possible. * * All access to dynamic globals must be thread-safe. * *****************************************************************************/
ULONG g_cRef = 0; /* Global reference count */ CRITICAL_SECTION g_csDll; /* The shared critical section */
extern HANDLE g_hthWorker; // Background worker thread
#ifdef DEBUG
DWORD g_TlsMem = 0xffffffff; extern DWORD g_TLSliStopWatchStartHi; extern DWORD g_TLSliStopWatchStartLo;
LEAKSTRUCT g_LeakList[] = { {0, "CFtpFolder"}, {0, "CFtpDir"}, {0, "CFtpSite"}, {0, "CFtpObj"}, {0, "CFtpEidl"}, {0, "CFtpDrop"}, {0, "CFtpList"}, {0, "CFtpStm"}, {0, "CAccount"}, {0, "CFtpFactory"}, {0, "CFtpContextMenu"}, {0, "CFtpEfe"}, {0, "CFtpGlob"}, {0, "CFtpIcon"}, {0, "CMallocItem"}, {0, "CFtpPidlList"}, {0, "CFtpProp"}, {0, "CStatusBar"}, {0, "CFtpView"}, {0, "CFtpWebView"}, {0, "CCookieList"}, {0, "CDropOperation"} }; #endif // DEBUG
ULONG g_cRef_CFtpView = 0; // Needed to determine when to purge cache.
/*****************************************************************************
* * DllAddRef / DllRelease * * Maintain the DLL reference count. * *****************************************************************************/
void DllAddRef(void) { CREATE_CALLERS_ADDRESS; // For debug spew.
ULONG cRef = InterlockedIncrement((LPLONG)&g_cRef); TraceMsg(TF_FTPREF, "DllAddRef() cRef=%d, called from=%#08lx.", cRef, GET_CALLERS_ADDRESS); }
void DllRelease(void) { CREATE_CALLERS_ADDRESS; // For debug spew.
ASSERT( 0 != g_cRef ); ULONG cRef = InterlockedDecrement((LPLONG)&g_cRef); TraceMsg(TF_FTPREF, "DllRelease() cRef=%d, called from=%#08lx.", cRef, GET_CALLERS_ADDRESS); }
/*****************************************************************************
* * DllGetClassObject * * OLE entry point. Produces an IClassFactory for the indicated GUID. * * The artificial refcount inside DllGetClassObject helps to * avoid the race condition described in DllCanUnloadNow. It's * not perfect, but it makes the race window much smaller. * *****************************************************************************/
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID * ppvObj) { HRESULT hres;
DllAddRef(); if (IsEqualIID(rclsid, CLSID_FtpFolder) || IsEqualIID(rclsid, CLSID_FtpWebView) || IsEqualIID(rclsid, CLSID_FtpDataObject) || IsEqualIID(rclsid, CLSID_FtpInstaller)) { hres = CFtpFactory_Create(rclsid, riid, ppvObj); } else { *ppvObj = NULL; hres = CLASS_E_CLASSNOTAVAILABLE; }
DllRelease(); return hres; }
/*****************************************************************************
* * DllCanUnloadNow * * OLE entry point. Fail iff there are outstanding refs. * * There is an unavoidable race condition between DllCanUnloadNow * and the creation of a new IClassFactory: Between the time we * return from DllCanUnloadNow() and the caller inspects the value, * another thread in the same process may decide to call * DllGetClassObject, thus suddenly creating an object in this DLL * when there previously was none. * * It is the caller's responsibility to prepare for this possibility; * there is nothing we can do about it. * *****************************************************************************/
STDMETHODIMP DllCanUnloadNow(void) { HRESULT hres;
ENTERCRITICALNOASSERT;
// Purge Cache if there aren't any FtpViews open.
if ((0 == g_cRef_CFtpView)) { // Since no views are open, we want to try to purge
// the Delayed Actions so we can closed down the background
// thread. Is it running?
if (AreOutstandingDelayedActions()) { LEAVECRITICALNOASSERT; PurgeDelayedActions(); // Try to close it down.
ENTERCRITICALNOASSERT; }
if (!AreOutstandingDelayedActions()) // Did it close down?
{ // We need to purge the session key because we lost the password
// redirects in the CFtpSites. So we would login but later fail
// when we try to fish out the password when falling back to
// URLMON/shdocfl for file downloads. (NT #362108)
PurgeSessionKey(); CFtpPunkList_Purge(&g_FtpSiteCache); // Yes so purge the cache...
} }
hres = g_cRef ? S_FALSE : S_OK; TraceMsg(TF_FTP_DLLLOADING, "DllCanUnloadNow() DllRefs=%d, returning hres=%#08lx. (S_OK means yes)", g_cRef, hres);
LEAVECRITICALNOASSERT;
return hres; }
void CheckForLeaks(BOOL fForce) { #ifdef DEBUG
DWORD dwLeakCount = 0;
if (fForce) { // Let's free our stuff so we can make sure not to leak it.
// This is done more to force our selves to be w/o leaks
// than anything else.
DllCanUnloadNow(); }
for (int nIndex = 0; nIndex < ARRAYSIZE(g_LeakList); nIndex++) dwLeakCount += g_LeakList[nIndex].dwRef;
if ((!g_FtpSiteCache || fForce) && (dwLeakCount || g_dwOpenConnections || g_cRef)) { TraceMsg(TF_ALWAYS, "***********************************************"); TraceMsg(TF_ALWAYS, "* LEAK - LEAK - LEAK - LEAK - LEAK *"); TraceMsg(TF_ALWAYS, "* *"); TraceMsg(TF_ALWAYS, "* WARNING: The FTP Shell Extension Leaked *"); TraceMsg(TF_ALWAYS, "* one or more objects *"); TraceMsg(TF_ALWAYS, "***********************************************"); TraceMsg(TF_ALWAYS, "* *"); for (int nIndex = 0; nIndex < ARRAYSIZE(g_LeakList); nIndex++) { if (g_LeakList[nIndex].dwRef) TraceMsg(TF_ALWAYS, "* %hs, Leaked=%d *", g_LeakList[nIndex].szObject, g_LeakList[nIndex].dwRef); } TraceMsg(TF_ALWAYS, "* *"); TraceMsg(TF_ALWAYS, "* Open Wininet Connections=%d *", g_dwOpenConnections); TraceMsg(TF_ALWAYS, "* DLL Refs=%d *", g_cRef); TraceMsg(TF_ALWAYS, "* *"); TraceMsg(TF_ALWAYS, "***********************************************"); ASSERT(0); }
#endif // DEBUG
}
// Globals to free.
extern CCookieList * g_pCookieList;
/*****************************************************************************\
DESCRIPTION: DLL entry point. \*****************************************************************************/ STDAPI_(BOOL) DllEntry(HINSTANCE hinst, DWORD dwReason, LPVOID lpReserved) { // This is called in two situations, FreeLibrary() is called and lpReserved is
// NULL, or the process is shutting down and lpReserved is not NULL.
BOOL fIsProcessShuttingDown = (lpReserved ? TRUE : FALSE);
switch (dwReason) { case DLL_PROCESS_ATTACH: SHFusionInitializeFromModule(hinst); InitializeCriticalSection(&g_csDll); #ifdef DEBUG
g_TlsMem = TlsAlloc(); g_TLSliStopWatchStartHi = TlsAlloc(); g_TLSliStopWatchStartLo = TlsAlloc(); #endif
// Don't put it under #ifdef DEBUG
CcshellGetDebugFlags(); DisableThreadLibraryCalls(hinst);
g_hthWorker = NULL;
g_hinst = hinst; g_formatEtcOffsets.cfFormat = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_SHELLIDLISTOFFSET); g_formatPasteSucceeded.cfFormat = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_PASTESUCCEEDED); g_cfTargetCLSID = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_TARGETCLSID);
g_dropTypes[DROP_FCont].cfFormat = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_FILECONTENTS); g_dropTypes[DROP_FGDW].cfFormat = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW); g_dropTypes[DROP_FGDA].cfFormat = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_FILEDESCRIPTORA); g_dropTypes[DROP_IDList].cfFormat = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_SHELLIDLIST); g_dropTypes[DROP_FNMA].cfFormat = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_FILENAMEMAPA); g_dropTypes[DROP_FNMW].cfFormat = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_FILENAMEMAPW); g_dropTypes[DROP_PrefDe].cfFormat = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT); g_dropTypes[DROP_PerfDe].cfFormat = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_PERFORMEDDROPEFFECT); g_dropTypes[DROP_FTP_PRIVATE].cfFormat = (CLIPFORMAT)RegisterClipboardFormat(TEXT("FtpPrivateData")); g_dropTypes[DROP_URL].cfFormat = (CLIPFORMAT)RegisterClipboardFormat(TEXT("UniformResourceLocator")); g_dropTypes[DROP_OLEPERSIST].cfFormat = (CLIPFORMAT)RegisterClipboardFormat(TEXT("OleClipboardPersistOnFlush"));
GetModuleFileNameA(GetModuleHandle(TEXT("SHELL32")), g_szShell32, ARRAYSIZE(g_szShell32));
if (FAILED(CFtpSite_Init())) return 0;
break;
case DLL_PROCESS_DETACH: { CCookieList * pCookieList = (CCookieList *) InterlockedExchangePointer((void **) &g_pCookieList, NULL); if (pCookieList) delete pCookieList;
// Yes, so we need to make sure all of the CFtpView's have closed down
// or it's really bad to purge the FTP cache of FTP Servers (CFtpSite) and
// their directories (CFtpDir).
ASSERT(0 == g_cRef_CFtpView);
// Now force the Delayed Actions to happen now instead of waiting.
PurgeDelayedActions();
// OndrejS turned this on. It's firing but I think they are false positives. Since FTP
// Folders does so much caching, this is non-trivial to track down. I will turn this off
// until Ondrej has time to verify.
// CheckForLeaks(fIsProcessShuttingDown);
UnloadWininet(); DeleteCriticalSection(&g_csDll); #ifdef DEBUG
if (g_TLSliStopWatchStartHi) { TlsFree(g_TLSliStopWatchStartHi); g_TLSliStopWatchStartHi = NULL; } if (g_TLSliStopWatchStartLo) { TlsFree(g_TLSliStopWatchStartLo); g_TLSliStopWatchStartLo = NULL; } #endif
SHFusionUninitialize(); } break; } return 1; }
|