diff options
Diffstat (limited to 'private/ole32/com/objact/dllcache.cxx')
-rw-r--r-- | private/ole32/com/objact/dllcache.cxx | 4060 |
1 files changed, 4060 insertions, 0 deletions
diff --git a/private/ole32/com/objact/dllcache.cxx b/private/ole32/com/objact/dllcache.cxx new file mode 100644 index 000000000..1c963abbf --- /dev/null +++ b/private/ole32/com/objact/dllcache.cxx @@ -0,0 +1,4060 @@ +//+------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 1992 - 1993. +// +// File: dllcache.cxx +// +// Contents: Implementations of classes declared in dllcache.hxx +// +// Functions: +// CDllAptEntry::Init +// CDllAptEntry::Create +// CDllAptEntry:: +// +// CDllCache::CDllCache +// CDllCache::~CDllCache +// CDllCache::InitClsent +// CDllCache::CreateClsent +// CDllCache::GetClassObjForDdeByClsent +// CDllCache::CleanUpLocalServersForApartment +// CDllCache::CleanUpDllsForApartment +// CDllCache::CleanUpDllsForProcess +// CDllCache::CleanUpForApartmentByDllent +// CDllCache::AtStorageRef +// CDllCache::Release +// CDllCache::InitDllent +// CDllCache::CreateDllent +// CDllCache::NewAptEntries +// CDllCache::AllocAptEntry +// CDllCache::FreeAptEntry +// CDllCache::IsValidInApartment +// CDllCache::MakeValidInApartment +// CDllCache::GetClassInterface +// CDllCache::CanUnloadNow +// CDllCache::Remove +// CDllCache::Init +// CDllCache::GetClass +// CDllCache::GetOrLoadClass +// CDllCache::GetClassObjForDde +// CDllCache::GetClassInformationFromKey +// CDllCache::GetApartmentForCLSID +// CDllCache::Add +// CDllCache::FreeUnused +// CDllCache::RegisterServer +// CDllCache::Revoke +// CDllCache::SetDdeServerWindow +// CDllCache::Search (x4) +// CDllCache::AllocClassEntry +// CDllCache::AllocDllPathEntry +// CDllCache::FreeClassEntry +// CDllCache::FreeDllPathEntry +// +// CleanUpDllsForProcess +// CleanUpLocalServersForApartment +// CleanUpDllsForApartment +// GetAptForCLSID +// GetClassInformationForDde +// GetClassInformationFromKey +// OleMainThreadWndProc +// InitMainThreadWnd +// UninitMainThreadWnd +// +// +// History: 09-May-93 Ricksa Created +// 31-Dec-93 ErikGav Chicago port +// 09-Jun-94 BruceMa Check new pointers +// 21-Jun-94 BruceMa Check new pointers +// 24-Jun-94 Rickhi Add Apartment Crap +// 24-Jun-94 BruceMa Check new pointers +// 28-Jun-94 BruceMa Memory sift fixes +// 07-Jul-94 BruceMa Memory sift fixes +// 08-Nov-94 Ricksa Final threading changes +// 07-Mar-95 BruceMa Rewrote +// 06-Oct-95 BruceMa Various fixes - mostly race conditions +// 19-Apr-96 Rickhi Add Suspend/Resume/AddRef/Release +// +//-------------------------------------------------------------------------- +#include <ole2int.h> +#include <channelb.hxx> +#include <tracelog.hxx> +#include <scmmem.hxx> + +#include "objact.hxx" +#include <dllhost.hxx> +#include <sobjact.hxx> +#include <treat.hxx> + +LPCTSTR ptszOle32DllName = TEXT("OLE32.DLL"); + +// Name of window class and message class for dispatching messages. +//const TCHAR OLE_WINDOW_CLASS = TEXT("OleObjectRpcWindow"); +LPTSTR gOleWindowClass = NULL; // class used to create windows + +// Various things used for special single threaded DLL processing +DWORD gdwMainThreadId = 0; +HWND hwndOleMainThread = NULL; + +// this flag is to indicate whether it is UninitMainThread that is +// destroying the window, or system shut down destroying the window. +BOOL gfDestroyingMainWindow = FALSE; + +const TCHAR *ptszOleMainThreadWndName = TEXT("OleMainThreadWndName"); +#define DllRegisterClass RegisterClassT +#define DllUnregisterClass UnregisterClassT + + +#ifdef _CHICAGO_ +// Note: we have to create a unique string so that get +// register a unique class for each 16 bit app. +// The class space is global on chicago. +// +LPSTR ptszOleMainThreadWndClass = "OleMainThreadWndClass 0x######## "; +#define DllCreateWindowEx SSCreateWindowExA + +STDAPI_(LRESULT) OleNotificationProc(UINT wMsg, WPARAM wParam, LPARAM lParam); + +#else // !_CHICAGO_ + +const WCHAR *ptszOleMainThreadWndClass = L"OleMainThreadWndClass"; +#define DllCreateWindowEx CreateWindowEx + +#endif // _CHICAGO_ + + +#ifdef _UNICODE +#define TSZFMT "%ws" +#else +#define TSZFMT "%s" +#endif + +static const TCHAR tszOle32Dll[] = TEXT("OLE32.DLL"); + +#define OLE32_DLL tszOle32Dll +#define OLE32_BYTE_LEN sizeof(OLE32_DLL) +#define OLE32_CHAR_LEN (sizeof(OLE32_DLL) / sizeof(TCHAR) - 1) + + +//+------------------------------------------------------------------------- +// +// Function: CallFreeUnused +// +// Synopsis: Free unused from main thread +// +// Arguments: [pData] - pointer to single thread parameter packet (unused) +// +// Returns: S_OK - call succeeded +// +// Algorithm: Call free unused for both inproc servers and in proc handlers. +// +// History: 10-Nov-94 Ricksa Created +// +//-------------------------------------------------------------------------- +HRESULT CallFreeUnused(void) +{ + TRACECALL(TRACE_DLL, "CallFreeUnused"); + + gdllcacheInprocSrv.FreeUnused(); + gdllcacheHandler.FreeUnused(); + + return S_OK; +} + +//+------------------------------------------------------------------------- +// +// Member: CDllAptEntry::Init +// +// Synopsis: Initialize the entry to empty +// +// Arguments: dwNext - The next apartment entry in the free list +// +// Returns: +// +// History: 07-Mar-95 BruceMa Created +// +//-------------------------------------------------------------------------- +void CDllAptEntry::Init(DWORD dwNext) +{ + _dwNext = dwNext; + _dwSig = 0; +} + + + + + +//+------------------------------------------------------------------------- +// +// Member: CDllAptEntry::Create +// +// Synopsis: Create an apartment entry +// +// Arguments: hApt - The creating apartment +// +// Returns: +// +// History: 07-Mar-95 BruceMa Created +// +//-------------------------------------------------------------------------- +void CDllAptEntry::Create(HAPT hApt) +{ + _dwSig = DLL_APT_CACHE_SIG; + _hApt = hApt; +} + + + + +//+------------------------------------------------------------------------- +// +// Member: CDllCache::CDllCache +// +// Synopsis: Create a DLL cache object +// +// Algorithm: Let sub-objects do all the work. +// +// History: 09-May-93 Ricksa Created +// 07-Mar-95 BruceMa Rewrote +// +//-------------------------------------------------------------------------- +CDllCache::CDllCache(void) : + _pClassEntries(NULL), _pDllPathEntries(NULL), + _nClassEntryAvail(NONE), _nDllPathEntryAvail(NONE), + _nClassEntryInUse(NONE), _nDllPathEntryInUse(NONE), + _cClassEntries(0), _cDllPathEntries(0), + _cRefsServerProcess(0) +{ + Win4Assert (g_fDllState == DLL_STATE_STATIC_CONSTRUCTING); +} + + + +//+------------------------------------------------------------------------- +// +// Member: CDllCache::Load +// +// Synopsis: Load the module into the current apartment & retrieve +// the entry points +// +// Arguments: [ptszPath] - Dll path +// [ppfnGetClassObject] - where to return DllGetClassObejct EP +// [ppfnDllCanUnloadNow] - where to return DllCanUnloadNow EP +// fSixteenBit - Whether this is a 16-bit dll +// phDll - Address to store the loaded HMODULE +// pIsX86Dll returns TRUE if dll is an X86 dll +// fLoadAsX86 is TRUE if Dll was found on InprocServerX86 key +// +// Returns: S_OK - if successfull +// +// History: 24-Jun-94 Rickhi Created +// 07-Mar-95 BruceMa Rewrote +// 30-Sep-95 AlanWar Added support for WX86 +// +// +//-------------------------------------------------------------------------- +HRESULT CDllCache::Load(LPCTSTR ptszPath, + LPFNGETCLASSOBJECT *ppfnGetClassObject, + DLLUNLOADFNP *ppfnDllCanUnload, + BOOL fSixteenBit, + HMODULE *phDll +#ifdef WX86OLE + ,BOOL *pfIsX86Dll, + BOOL fLoadAsX86 +#endif + ) +{ + HRESULT hr = S_OK; +#ifdef WX86OLE + BOOL fIsX86Dll; +#endif + + if (fSixteenBit) + { + CairoleDebugOut((DEB_TRACE, + "Attempting to load 16 bit DLL " TSZFMT "\n", ptszPath)); + + // In this section, we need to call 16-bit DllGetClassObject. The + // g_OleThunkWow pointer is the VTABLE to use for getting back to + // the 16-bit implementation. + LPFNGETCLASSOBJECT pfnGetClassObject; + DLLUNLOADFNP pfnDllCanUnload; + + hr = g_pOleThunkWOW->LoadProcDll(ptszPath, + (DWORD *)&pfnGetClassObject, + (DWORD *)&pfnDllCanUnload, + (DWORD *)phDll); + + // A failure condition would mean that the DLL could not be found, + // or otherwise could not be loaded + if (FAILED(hr)) + { + CairoleDebugOut((DEB_ERROR, + "Load 16 bit DLL " TSZFMT " failed(%x)\n",ptszPath,hr)); + return CO_E_DLLNOTFOUND; + } + + // The other possible error is the DLL didn't have the required + // interface + if (ppfnGetClassObject) + { + if (pfnGetClassObject == NULL) + { + CairoleDebugOut((DEB_ERROR, + "Get pfnGetClassObject %ws failed\n", + ptszPath)); + + return(CO_E_ERRORINDLL); + } + *ppfnGetClassObject = pfnGetClassObject; + } + + if (ppfnDllCanUnload) + { + *ppfnDllCanUnload = pfnDllCanUnload; + } + } + else + { + CairoleDebugOut((DEB_TRACE, + "Attempting to load 32 bit DLL " TSZFMT "\n", ptszPath)); + +#ifdef WX86OLE + fLoadAsX86 = gcwx86.SetLoadAsX86(fLoadAsX86); +#endif + // Load the 32-bit DLL + *phDll = LoadLibraryExT(ptszPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); +#ifdef WX86OLE + gcwx86.SetLoadAsX86(fLoadAsX86); +#endif + + if (*phDll == NULL) + { + // Dll could not be loaded + CairoleDebugOut((DEB_ERROR, + "Load of " TSZFMT " failed\n", + ptszPath)); + return HRESULT_FROM_WIN32(GetLastError()); + } +#ifdef WX86OLE + fIsX86Dll = gcwx86.IsModuleX86(*phDll); + if (pfIsX86Dll) + { + *pfIsX86Dll = fIsX86Dll; + } +#endif + + // Get the entry points if desired + if (ppfnGetClassObject) + { +#ifdef WX86OLE + *ppfnGetClassObject = (LPFNGETCLASSOBJECT) + GetProcAddress(*phDll, DLL_GET_CLASS_OBJECT_EP); + if ((*ppfnGetClassObject == NULL) || + (fIsX86Dll && ( (*ppfnGetClassObject = + gcwx86.TranslateDllGetClassObject( + *ppfnGetClassObject)) == NULL))) +#else + if ((*ppfnGetClassObject = (LPFNGETCLASSOBJECT) + GetProcAddress(*phDll, DLL_GET_CLASS_OBJECT_EP)) == NULL) +#endif + { + // Doesn't have a valid entry point for creation of class objects + return CO_E_ERRORINDLL; + } + } + + if (ppfnDllCanUnload) + { + // Not having a unload entry point is valid behavior + *ppfnDllCanUnload = (DLLUNLOADFNP) GetProcAddress(*phDll, + DLL_CAN_UNLOAD_EP); +#ifdef WX86OLE + if (fIsX86Dll) + { + // Translating a NULL address will do nothing but return a + // NULL address + *ppfnDllCanUnload = + gcwx86.TranslateDllCanUnloadNow( + *ppfnDllCanUnload); + } +#endif + } + } + + return hr; +} + + + + + +//+------------------------------------------------------------------------- +// +// Member: CDllCache::InitClsent +// +// Synopsis: Initialize a class entry structure +// +// Algorithm: dwCls - The index of this class entry +// k - The next class entry index in the free list +// +// History: 07-Mar-95 BruceMa Created +// +//-------------------------------------------------------------------------- +void CDllCache::InitClsent(DWORD dwCls, DWORD k) +{ + _pClassEntries[dwCls]._dwNext = k; + _pClassEntries[dwCls]._dwSig = 0; + _pClassEntries[dwCls]._dwNextDllCls = NONE; +} + + + + + +//+------------------------------------------------------------------------- +// +// Member: CDllCache::CreateClsentLSvr +// +// Synopsis: Create a class entry for a local server +// +// Algorithm: dwCls - The index of this class entry +// rclsid - The class ID for this server +// punk - IUnknown for the server +// dwFlags - Either REGCLS_SINGLEUSE or REGCLS_MULTIPLEUSE +// dwContext - CLSCTX_INPROC_SERVER | CLSREG_LOCAL_SERVER +// dwReg - The registration key returned to the user +// +// History: 07-Mar-95 BruceMa Created +// +//-------------------------------------------------------------------------- +HRESULT CDllCache::CreateClsentLSvr(DWORD dwCls, + REFCLSID rclsid, + IUnknown *punk, + DWORD dwFlags, + DWORD dwContext, + DWORD dwReg) +{ + HRESULT hr = S_OK; + + // Initialize the class entry + _pClassEntries[dwCls]._dwSig = CLASS_CACHE_SIG; + _pClassEntries[dwCls]._fAtStorage = FALSE; + _pClassEntries[dwCls]._clsid = rclsid; + _pClassEntries[dwCls]._pUnk = punk; + _pClassEntries[dwCls]._dwContext = dwContext; + _pClassEntries[dwCls]._dwFlags = dwFlags; + _pClassEntries[dwCls]._hApt = GetCurrentApartmentId(); + _pClassEntries[dwCls]._dwReg = dwReg; + _pClassEntries[dwCls]._cCallOut = 0; + _pClassEntries[dwCls]._fRevoking = FALSE; + _pClassEntries[dwCls]._fRevokePending = FALSE; + _pClassEntries[dwCls]._fReleasing = FALSE; + _pClassEntries[dwCls]._dwDllEnt = NONE; + _pClassEntries[dwCls]._dwNextDllCls = NONE; + _pClassEntries[dwCls]._hWndDdeServer = NULL; + _pClassEntries[dwCls]._dwScmReg = NONE; + _pClassEntries[dwCls]._pObjServer = NULL; + + + if (dwContext & CLSCTX_LOCAL_SERVER) + { + // store off a pointer to the activation server object. + _pClassEntries[dwCls]._pObjServer = GetObjServer(); + + if (!(dwFlags & REGCLS_SUSPENDED)) + { + // Notify SCM that the class is started. + RegOutput *pRegOut = NULL; + + RegInput RegIn; + RegIn.dwSize = 1; + RegIn.rginent[0].clsid = rclsid; + RegIn.rginent[0].dwFlags = dwFlags; + RegIn.rginent[0].ipid = _pClassEntries[dwCls]._pObjServer->GetIPID(); + RegIn.rginent[0].oxid = _pClassEntries[dwCls]._pObjServer->GetOXID(); + + // Release the lock across outgoing calls to the SCM. + _mxs.Release(); + hr = gResolver.NotifyStarted(&RegIn, &pRegOut); + _mxs.Request(); + + if (SUCCEEDED(hr)) + { + _pClassEntries[dwCls]._fAtStorage = pRegOut->regoutent[0].dwAtStorage; + _pClassEntries[dwCls]._dwScmReg = pRegOut->regoutent[0].dwReg; + MIDL_user_free(pRegOut); + } + } + } + + return hr; +} + + + + + +//+------------------------------------------------------------------------- +// +// Member: CDllCache::CreateClsentInProc +// +// Synopsis: Create a class entry for an inproc server +// +// Algorithm: dwCls - The index of this class entry +// dwDll - The index of the parent dll path entry +// rclsid - The class ID for this server +// +// History: 07-Mar-95 BruceMa Created +// +//-------------------------------------------------------------------------- +HRESULT CDllCache::CreateClsentInProc(DWORD dwCls, + DWORD dwDll, + DWORD dwDllThreadModel, + DWORD dwNextDllCls, + REFCLSID rclsid +#ifdef WX86OLE + ,BOOL fWx86 +#endif +) +{ + ComDebOut((DEB_TRACE, + "CDllCache::CreateClsentInproc clsid:%I dwCls:%x dwDll:%x\n", + &rclsid, dwCls, dwDll)); + + // Initialize the class entry + _pClassEntries[dwCls]._dwSig = CLASS_CACHE_SIG; + _pClassEntries[dwCls]._fAtStorage = FALSE; + _pClassEntries[dwCls]._clsid = rclsid; + _pClassEntries[dwCls]._pUnk = NULL; + _pClassEntries[dwCls]._dwContext = +#ifdef WX86OLE + fWx86 ? CLSCTX_INPROC_SERVERX86 : +#endif + CLSCTX_INPROC_SERVER; + _pClassEntries[dwCls]._dwFlags = REGCLS_MULTIPLEUSE; + _pClassEntries[dwCls]._hApt = GetCurrentApartmentId(); + _pClassEntries[dwCls]._dwReg = 0; + _pClassEntries[dwCls]._cCallOut = 0; + _pClassEntries[dwCls]._fRevokePending = FALSE; + _pClassEntries[dwCls]._fRevoking = FALSE; + _pClassEntries[dwCls]._fReleasing = FALSE; + _pClassEntries[dwCls]._dwDllEnt = dwDll; + _pClassEntries[dwCls]._dwDllThreadModel= dwDllThreadModel; + _pClassEntries[dwCls]._dwNextDllCls = dwNextDllCls; + _pClassEntries[dwCls]._hWndDdeServer = NULL; + _pClassEntries[dwCls]._dwScmReg = NONE; + + if (dwDllThreadModel == FREE_THREADED || + dwDllThreadModel == BOTH_THREADED) + { + // for any FT and BOTH threaded classes, delay unloading the DLL + _pDllPathEntries[dwDll]._dwFlags |= DELAYED_UNLOAD; + } + return S_OK; +} + + + + + +//+--------------------------------------------------------------------------- +// +// Method: CDllCache::GetClassObjForDdeByClsent +// +// Synopsis: Get a class entry from the table for Dde, returning +// extra information, including the flags. +// +// Effects: The DdeServer needs the ability to query the class factory +// table to search for classes it needs to provide OLE 1.0 +// support for. This routine will allow it to access the +// required information. +// +// Arguments: dwCls - The index of this class entry +// lpDdeClassInfo - The DDE structure to fill in +// +// Returns: TRUE if the entry matched, FALSE if it did not. +// +// History: 5-28-94 kevinro Created +// 07-Mar-95 BruceMa Rewrote +// +//---------------------------------------------------------------------------- +BOOL CDllCache::GetClassObjForDdeByClsent(DWORD dwCls, + LPDDECLASSINFO lpDdeClassInfo) +{ + Win4Assert(IsValidPtrOut(lpDdeClassInfo, sizeof(DWORD)) && + "CDllCache::GetClassObjForDde invalid out parameter"); + + if (lpDdeClassInfo->dwContextMask & _pClassEntries[dwCls]._dwContext) + { + HAPT hApt = GetCurrentApartmentId(); + + if (hApt == _pClassEntries[dwCls]._hApt) + { + // Found a matching record, set its info + lpDdeClassInfo->dwContext = _pClassEntries[dwCls]._dwContext; + lpDdeClassInfo->dwFlags = _pClassEntries[dwCls]._dwFlags; + lpDdeClassInfo->dwThreadId = _pClassEntries[dwCls]._hApt; + lpDdeClassInfo->dwRegistrationKey = _pClassEntries[dwCls]._dwReg; + + if (lpDdeClassInfo->fClaimFactory == TRUE) + { + // Release the lock across the outgoing call + IUnknown *pUnkTmp = _pClassEntries[dwCls]._pUnk; + + _mxs.Release(); + HRESULT hr = pUnkTmp->QueryInterface( + IID_IClassFactory, + (void **)&(lpDdeClassInfo->punk)); + _mxs.Request(); + + if (hr != S_OK) + { + return FALSE; + } + + // We do this only after the QueryInterface has succeeded + if (_pClassEntries[dwCls]._dwFlags == REGCLS_SINGLEUSE) + { + // For a single use class we can only pass it out once. To + // guarantee it, we set the context to zero so that the + // above test will not pass again. + _pClassEntries[dwCls]._dwContext = 0; + } + } + else + { + lpDdeClassInfo->punk = NULL; + } + + return TRUE; + } + } + + return FALSE; + +} + + + + + +//+------------------------------------------------------------------------- +// +// Member: CDllCache::Release +// +// Synopsis: Disconnect and release the associated server +// +// Arguments: dwCls - The index of this class entry +// +// History: 07-Mar-95 BruceMa Created +// +//-------------------------------------------------------------------------- +HRESULT CDllCache::Release(DWORD dwCls) +{ + HRESULT hr = S_OK; + + CairoleDebugOut((DEB_TRACE, "CDllCache::Release Releasing class %d\n", + dwCls)); + + // Disallow recursive releases (like Lotus Notes 4.0) + if (_pClassEntries[dwCls]._fReleasing) + { + return S_OK; + } + + // Invalidate this class entry + _pClassEntries[dwCls]._dwContext = 0; + _pClassEntries[dwCls]._fReleasing = TRUE; + + // Release the lock across outgoing calls and SendMessage (We can do this + // without setting up local variables since the class entry can't be + // reused until we do FreeClassEntry) + if (_pClassEntries[dwCls]._pUnk != NULL) + { + _mxs.Release(); + + // Tell SCM about multiple use classes stopping. + if (_pClassEntries[dwCls]._dwScmReg != NONE) + { + gResolver.NotifyStopped(_pClassEntries[dwCls]._clsid, + _pClassEntries[dwCls]._dwScmReg); + _pClassEntries[dwCls]._dwScmReg = NONE; + } + + // If a DDE Server window exists for this class, then we need to + // release it now. + if (_pClassEntries[dwCls]._hWndDdeServer != NULL) + { + // It's possible that SendMessage could fail. However, there + // really isn't anything we can do about it. So, the error + // code is not checked. + SSSendMessage(_pClassEntries[dwCls]._hWndDdeServer, WM_USER, 0, 0); + _pClassEntries[dwCls]._hWndDdeServer == NULL; + } + + // Now really release it + if (_pClassEntries[dwCls]._pUnk != NULL) + { + if (IsValidInterface(_pClassEntries[dwCls]._pUnk)) + { + CoDisconnectObject(_pClassEntries[dwCls]._pUnk, NULL); + _pClassEntries[dwCls]._pUnk->Release(); + + hr = S_OK; + } + else + { + hr = CO_E_RELEASED; + } + } + + // Retake the lock + _mxs.Request(); + } + + return hr; +} + + + + + +//+------------------------------------------------------------------------- +// +// Member: CDllCache::InitDllent +// +// Synopsis: Initialize a dll path entry structure +// +// Arguments: dwDll - The index of this dll path entry +// k - The next dll path entry in the free list +// +// Algorithm: +// +// History: 07-Mar-95 BruceMa Created +// +//-------------------------------------------------------------------------- +void CDllCache::InitDllent(DWORD dwDll, DWORD k) +{ + _pDllPathEntries[dwDll]._dwNext = k; + _pDllPathEntries[dwDll]._dwSig = 0; + _pDllPathEntries[dwDll]._dwFlags = 0; + _pDllPathEntries[dwDll]._dw1stClass = NONE; + _pDllPathEntries[dwDll]._cAptEntries = NOMINAL_NUMBER_THREADS; + _pDllPathEntries[dwDll]._nAptAvail = 0; + _pDllPathEntries[dwDll]._nAptInUse = NONE; + _pDllPathEntries[dwDll]._dwExpireTime = 0; +} + + + + + +//+------------------------------------------------------------------------- +// +// Member: CDllCache::CreateDllent +// +// Synopsis: Fully initialize a DLL object +// +// Arguments: dwDll - The index of this dll path entry +// ptszDllPath - The load path for the dll +// fSixteenBit - Whether this is a 16-bit dll +// +// Algorithm: Creates the first CDllAptEntry which loads the +// DLL specified by the path, gets the DllGetClassObject +// entry point and the DllCanUnloadNow entry point. +// At this point object construction is complete. +// +// History: 09-May-93 Ricksa Created +// 07-Mar-95 BruceMa Rewrote +// +//-------------------------------------------------------------------------- +HRESULT CDllCache::CreateDllent(DWORD dwDll, + LPCTSTR ptszDllPath, + BOOL fSixteenBit, + LPFNGETCLASSOBJECT pfnGetClassObject, + DLLUNLOADFNP pfnDllCanUnload, + HMODULE hDll +#ifdef WX86OLE + ,BOOL fIsX86Dll, + BOOL fLoadAsX86 +#endif + ) +{ + TRACECALL(TRACE_DLL, "CDllCache::CreateDllent"); + CairoleDebugOut((DEB_TRACE, "Initializing dll " TSZFMT "\n", ptszDllPath)); + + // Get the path length and allocate for it + UINT ccH = lstrlen(ptszDllPath) + 1; + _pDllPathEntries[dwDll]._ptszPath = (TCHAR *) PrivMemAlloc(ccH * sizeof(TCHAR)); + if (_pDllPathEntries[dwDll]._ptszPath == NULL) + { + return E_OUTOFMEMORY; + } + + // Initialize the dll path entry + memcpy(_pDllPathEntries[dwDll]._ptszPath, ptszDllPath, ccH * sizeof(TCHAR)); + CharUpper(_pDllPathEntries[dwDll]._ptszPath); + _pDllPathEntries[dwDll]._dwFlags |= fSixteenBit ? SIXTEEN_BIT : 0; + _pDllPathEntries[dwDll]._cUsing = 0; + + // Compute a hash value for more optimal searchs + _pDllPathEntries[dwDll]._dwHash = Hash(_pDllPathEntries[dwDll]._ptszPath); + + + // 32 bit libraries have process wide handles, so + // we'll store them in the DllPathEntry + if (fSixteenBit) + { + _pDllPathEntries[dwDll]._hDll32 = 0; + } + else + { + _pDllPathEntries[dwDll]._hDll32 = hDll; + } + + // Construct the initial per apartment entry + DWORD dwAptent = AllocAptEntry(dwDll); + if (dwAptent == NONE) + { + return E_OUTOFMEMORY; + } + + // Initialize it + _pDllPathEntries[dwDll]._pAptEntries[dwAptent].Create(GetCurrentApartmentId()); + + // Check if this is "OLE32.DLL" + if (lstrcmp(_pDllPathEntries[dwDll]._ptszPath, ptszOle32DllName) + == 0) + { + _pDllPathEntries[dwDll]._pfnGetClassObject = DllGetClassObject; + _pDllPathEntries[dwDll]._pfnDllCanUnload = NULL; + _pDllPathEntries[dwDll]._dwFlags |= IS_OLE32; + } + else + { + _pDllPathEntries[dwDll]._pfnGetClassObject = pfnGetClassObject; + _pDllPathEntries[dwDll]._pfnDllCanUnload = pfnDllCanUnload; + _pDllPathEntries[dwDll]._pAptEntries[dwAptent]._hDll = (fSixteenBit ? hDll : 0); +#ifdef WX86OLE + if (fIsX86Dll) + { + _pDllPathEntries[dwDll]._dwFlags |= WX86_THUNK; + } + if (fLoadAsX86) + { + _pDllPathEntries[dwDll]._dwFlags |= WX86_LOADASX86; + } +#endif + } + + return S_OK; +} + + + + + +//+------------------------------------------------------------------------- +// +// Member: CDllCache::NewAptEntries +// +// Synopsis: Allocate and initialize the apartment entries for +// a dll path entry +// +// Arguments: dwDll - The index of this dll path entry +// +// Algorithm: +// +// History: 07-Mar-95 BruceMa Created +// +//-------------------------------------------------------------------------- +BOOL CDllCache::NewAptEntries(DWORD dwDll) +{ + + _pDllPathEntries[dwDll]._pAptEntries = new CDllAptEntry[NOMINAL_NUMBER_THREADS]; + if (_pDllPathEntries[dwDll]._pAptEntries == NULL) + { + return FALSE; + } + for (int dwApt = 0; dwApt < NOMINAL_NUMBER_THREADS; dwApt++) + { + _pDllPathEntries[dwDll]._pAptEntries[dwApt].Init( + dwApt == NOMINAL_NUMBER_THREADS - 1 ? NONE : dwApt + 1); + } + + return TRUE; +} + + + + + +//+------------------------------------------------------------------------- +// +// Member: CDllCache::AllocAptEntry +// +// Synopsis: Allocate a new apartment entry for a dll path entry +// +// Arguments: dwDll - The index of this dll path entry +// +// Algorithm: +// +// History: 07-Mar-95 BruceMa Created +// +//-------------------------------------------------------------------------- +DWORD CDllCache::AllocAptEntry(DWORD dwDll) +{ + // If we don't have any available entries, then expand the array + if (_pDllPathEntries[dwDll]._nAptAvail == NONE) + { + // Allocate a new array + DWORD cEnt = _pDllPathEntries[dwDll]._cAptEntries; + CDllAptEntry *p = new CDllAptEntry[cEnt + NOMINAL_NUMBER_THREADS]; + if (p == NULL) + { + return NONE; + } + + // Initialize it + memcpy(p, + _pDllPathEntries[dwDll]._pAptEntries, + _pDllPathEntries[dwDll]._cAptEntries * sizeof(CDllAptEntry)); + + // Free old array + delete _pDllPathEntries[dwDll]._pAptEntries; + _pDllPathEntries[dwDll]._pAptEntries = p; + + for (DWORD k = _pDllPathEntries[dwDll]._cAptEntries; + k < _pDllPathEntries[dwDll]._cAptEntries + NOMINAL_NUMBER_THREADS; + k++) + { + _pDllPathEntries[dwDll]._pAptEntries[k].Init( + k == _pDllPathEntries[dwDll]._cAptEntries + NOMINAL_NUMBER_THREADS - 1 ? NONE : k + 1 ); + } + _pDllPathEntries[dwDll]._nAptAvail = _pDllPathEntries[dwDll]._cAptEntries; + _pDllPathEntries[dwDll]._cAptEntries += NOMINAL_NUMBER_THREADS; + } + + // Return the next available entry + DWORD dwAptent = _pDllPathEntries[dwDll]._nAptAvail; + _pDllPathEntries[dwDll]._pAptEntries[dwAptent]._dwSig = DLL_APT_CACHE_SIG; + + _pDllPathEntries[dwDll]._nAptAvail = + _pDllPathEntries[dwDll]._pAptEntries[dwAptent]._dwNext; + _pDllPathEntries[dwDll]._pAptEntries[dwAptent]._dwNext = + _pDllPathEntries[dwDll]._nAptInUse; + _pDllPathEntries[dwDll]._nAptInUse = dwAptent; + return dwAptent; +} + + + + + +//+------------------------------------------------------------------------- +// +// Member: CDllCache::FreeAptEntry +// +// Synopsis: Free an apt entry in a dll path entry - i.e., make it available +// +// Arguments: dwDll - The index of this dll path entry +// dwAptent - The index of the apartment entry to free +// +// Algorithm: +// +// History: 07-Mar-95 BruceMa Created +// +//-------------------------------------------------------------------------- +void CDllCache::FreeAptEntry(DWORD dwDll, DWORD dwAptent) +{ + // It's at the head of the list + if (_pDllPathEntries[dwDll]._nAptInUse == dwAptent) + { + _pDllPathEntries[dwDll]._nAptInUse = + _pDllPathEntries[dwDll]._pAptEntries[dwAptent]._dwNext; + } + + // Otherwise search for the entry that points to the one we're freeing + else + { + for (DWORD dwPrev = _pDllPathEntries[dwDll]._nAptInUse; + _pDllPathEntries[dwDll]._pAptEntries[dwPrev]._dwNext != dwAptent; + dwPrev = _pDllPathEntries[dwDll]._pAptEntries[dwPrev]._dwNext) + { + } + _pDllPathEntries[dwDll]._pAptEntries[dwPrev]._dwNext = + _pDllPathEntries[dwDll]._pAptEntries[dwAptent]._dwNext; + } + + // Relink into the list of available entries + _pDllPathEntries[dwDll]._pAptEntries[dwAptent]._dwNext = + _pDllPathEntries[dwDll]._nAptAvail; + _pDllPathEntries[dwDll]._nAptAvail = dwAptent; + _pDllPathEntries[dwDll]._pAptEntries[dwAptent].Init( + _pDllPathEntries[dwDll]._pAptEntries[dwAptent]._dwNext); +} + + + + + +//+------------------------------------------------------------------------- +// +// Member: CDllCache::IsValidInApartment +// +// Synopsis: Determine whether Dll object is valid in the current apartment +// +// Arguments: dwDll - The index of this dll path entry +// hApt - apartment to check +// +// Returns: TRUE - it is valid +// FALSE - it isn't valid +// +// History: 10-Nov-94 Ricksa Created +// 07-Mar-95 BruceMa Rewrote +// +//-------------------------------------------------------------------------- +BOOL CDllCache::IsValidInApartment(DWORD dwDll, HAPT hApt) +{ + for (DWORD dwAptent = _pDllPathEntries[dwDll]._nAptInUse; + dwAptent != NONE; + dwAptent = _pDllPathEntries[dwDll]._pAptEntries[dwAptent]._dwNext) + { + if (_pDllPathEntries[dwDll]._pAptEntries[dwAptent]._hApt == hApt) + { + return TRUE; + } + } + + return FALSE; +} + + + + + +//+------------------------------------------------------------------------- +// +// Member: CDllCache::MakeValidInApartment +// +// Synopsis: Ensure the Dll object is valid in the current apartment +// +// Arguments: dwDll - The index of this dll path entry +// +// Returns: S_OK - Dll is valid in this apartment +// E_OUTOFMEMORY - Could not allocate memory +// +// History: 24-Jun-94 Rickhi Created +// 07-Mar-95 BruceMa Rewrote +// +//-------------------------------------------------------------------------- +HRESULT CDllCache::MakeValidInApartment(DWORD dwDll) +{ + HRESULT hr; +#ifdef WX86OLE + BOOL fIsX86Dll; +#endif + + + + // Walk the list of apartment entries looking for a match + // with the current apartment id. If one exists, we are valid, + // Otherwise, we will try to create an entry for the current + // apartment. + HAPT hApt = GetCurrentApartmentId(); + if (IsValidInApartment(dwDll, hApt)) + { + CairoleDebugOut((DEB_TRACE, "Making dll " TSZFMT " valid in apt %d\n", + _pDllPathEntries[dwDll]._ptszPath, hApt)); + return S_OK; + } + + // No match found, create a new entry + DWORD dwAptent = AllocAptEntry(dwDll); + if (dwAptent == NONE) + { + return E_OUTOFMEMORY; + } + + // Initialize the new apartment entry + _pDllPathEntries[dwDll]._pAptEntries[dwAptent].Create(hApt); + + + // Dll is always valid if Ole32 and for non-WOW case + if ((_pDllPathEntries[dwDll]._dwFlags & IS_OLE32) || !IsWOWProcess()) + { + _pDllPathEntries[dwDll]._pAptEntries[dwAptent]._hDll = 0; + return S_OK; + } + + // We need to release the lock across the LoadLibrary since there is + // a chance that an exiting thread waits on our mutext to + // CleanUpFoApartment while we wait on the kernel mutex which the + // exiting thread owns + TCHAR *ptszPath; + LPFNGETCLASSOBJECT pfnGetClassObject; + DLLUNLOADFNP pfnDllCanUnload; + DWORD dwSixteenBit; + HMODULE hDll; + + ptszPath = _pDllPathEntries[dwDll]._ptszPath; + dwSixteenBit = _pDllPathEntries[dwDll]._dwFlags & SIXTEEN_BIT; + + // Reset the entry point values on every apartment initialization + // to handle DLLs being unloaded and then reloaded at a different + // address. + _mxs.Release(); + hr = Load(ptszPath, + &pfnGetClassObject, + &pfnDllCanUnload, + dwSixteenBit, + &hDll +#ifdef WX86OLE + , &fIsX86Dll, + _pDllPathEntries[dwDll]._dwFlags & WX86_LOADASX86 +#endif + ); + _mxs.Request(); + +#ifdef WX86OLE + if (fIsX86Dll) + { + _pDllPathEntries[dwDll]._dwFlags |= WX86_THUNK; + } +#endif + + _pDllPathEntries[dwDll]._pfnGetClassObject = pfnGetClassObject; + _pDllPathEntries[dwDll]._pfnDllCanUnload = pfnDllCanUnload; + _pDllPathEntries[dwDll]._pAptEntries[dwAptent]._hDll = hDll; + + if (FAILED(hr)) + { + FreeAptEntry(dwDll, dwAptent); + } + else + { + CairoleDebugOut((DEB_TRACE, "Making dll %ws valid in apt %d\n", + _pDllPathEntries[dwDll]._ptszPath, hApt)); + } + + return hr; +} + + + + + +//+------------------------------------------------------------------------- +// +// Member: CDllCache::GetClassInterface +// +// Synopsis: Create the class factory from the DLL +// +// Arguments: [dwDll] - The index of this dll path entry +// [rclsid] - class ID +// [riid] - interface req'd of class object +// [hr] - HRESULT to return +// +// Returns: NULL - class factory could not be created +// ~NULL - newly created class factory +// +// History: 09-May-93 Ricksa Created +// 07-Mar-95 BruceMa Rewrote +// +//-------------------------------------------------------------------------- +IUnknown *CDllCache::GetClassInterface(DWORD dwDll, + DWORD dwDllThreadModel, + REFCLSID rclsid, + REFIID riid, + HRESULT& hr) +{ + TRACECALL(TRACE_DLL, "CDllCache::GetClassInterface"); + CairoleDebugOut((DEB_TRACE, "Getting class interface\n")); + + IUnknown *punk = NULL; + + // Make sure this Dll is valid in the current apartment. + if(FAILED(MakeValidInApartment(dwDll))) + { + return NULL; + } + + // Need to check to see if the class is 16-bit or not. + // If it is 16-bit, then this call needs to be routed through + // a thunk + if (!(_pDllPathEntries[dwDll]._dwFlags & SIXTEEN_BIT)) + { + // Find 32 bit interface + // + // We load single threaded DLLs specially if we are not in WOW. + // The reason for this is that the initial release of Daytona + // did not know about multiple threads and therefore, we want + // to make sure that they don't get inadvertently multithreaded. + // The reason we don't have to do this if we are in WOW is that + // only one thread is allowed to execute at a time even though + // there are multiple physical threads and therefore the DLL + // will never be executed in a multithreaded manner. + + // Release the lock across outgoing calls + LPFNGETCLASSOBJECT pfnGetClassObject = + _pDllPathEntries[dwDll]._pfnGetClassObject; + + // This prevents DllCanUnloadNow being called during this call out + _pDllPathEntries[dwDll]._cUsing++; + + // this resets the delay time for delayed unload DLLs + _pDllPathEntries[dwDll]._dwExpireTime = 0; + + _mxs.Release(); + + BOOL fThisThread = TRUE; + switch (dwDllThreadModel) + { + case SINGLE_THREADED: + if ((!IsWOWProcess() || !IsWOWThread() || !IsWOWThreadCallable()) + && !OnMainThread()) + { + // Pass the call to the main thread + fThisThread = FALSE; + if (IsMTAThread()) + { + hr = DoSTMTClassCreate(pfnGetClassObject, rclsid, riid, &punk); + } + else + { + hr = DoSTClassCreate(pfnGetClassObject, rclsid, riid, &punk); + } + } + break; + + case APT_THREADED: + if (IsMTAThread()) + { + // pass call to apartment thread worker + fThisThread = FALSE; + hr = DoATClassCreate(pfnGetClassObject, rclsid, riid, &punk); + } + break; + + case FREE_THREADED: + if (IsSTAThread()) + { + // pass call to apartment thread worker + fThisThread = FALSE; + hr = DoMTClassCreate(pfnGetClassObject, rclsid, riid, &punk); + } + break; + + case BOTH_THREADED: + break; + } + + if (fThisThread) + { + hr = (*pfnGetClassObject)(rclsid, riid, (void **) &punk); + } + + if (FAILED(hr)) + { + CairoleDebugOut((DEB_ERROR,"GetClassInterface failed (0x%x)\n",hr)); + } + + _mxs.Request(); + _pDllPathEntries[dwDll]._cUsing--; + } + else + { + // Find 16-bit interface + if (!IsWOWProcess()) + { + CairoleDebugOut((DEB_TRACE, + "GetClassInterface on 16bit while not in VDM\n")); + return NULL; + } + + if (!IsWOWThread()) + { + CairoleDebugOut((DEB_TRACE, + "GetClassInterface on 16bit while not in 16-bit thread\n")); + return NULL; + } + + // Release the lock across outgoing calls + LPFNGETCLASSOBJECT pfnGetClassObject = + _pDllPathEntries[dwDll]._pfnGetClassObject; + + // This prevents DllCanUnloadNow being called during this call out + _pDllPathEntries[dwDll]._cUsing++; + + _mxs.Release(); + hr = g_pOleThunkWOW->CallGetClassObject((DWORD)pfnGetClassObject, + rclsid, + riid, + (void **)&punk); + _mxs.Request(); + _pDllPathEntries[dwDll]._cUsing--; + + if (FAILED(hr)) + { + CairoleDebugOut((DEB_ERROR, + "GetClassInterface 16-bit failed (0x%x)\n",hr)); + } + } + + return punk; +} + + + + + +//+------------------------------------------------------------------------- +// +// Member: CDllCache::CanUnloadNow +// +// Synopsis: Find out whether DLL can be unloaded. +// +// Algorithm: If the DLL supports unloading, ask it if it can be +// unloaded and return the result to the caller. +// +// Arguments: dwDll - The index of this dll path entry +// +// History: 09-May-93 Ricksa Created +// 07-Mar-95 BruceMa Rewrote +// +//-------------------------------------------------------------------------- +HRESULT CDllCache::CanUnloadNow(DWORD dwDll) +{ + CairoleDebugOut((DEB_TRACE, "Calling CanUnloadNow on " TSZFMT "\n", + _pDllPathEntries[dwDll]._ptszPath)); + + // Single thread access to the table + COleStaticLock lck(_mxs); + + // Unless the code is changed, we should not be here in the Wow case + CairoleAssert(!IsWOWProcess() && "Freeing unused libraries in WOW"); + + + if (_pDllPathEntries[dwDll]._cUsing != 0) + { + // At least one thread is using the object unlocked so we better + // not release the DLL object. + return S_FALSE; + } + + // Does DLL support unloading itself? + if (_pDllPathEntries[dwDll]._pfnDllCanUnload) + { + // Release the lock across outgoing call + BOOL fSixteenBit = _pDllPathEntries[dwDll]._dwFlags & SIXTEEN_BIT; + DLLUNLOADFNP pfnDllCanUnload = _pDllPathEntries[dwDll]._pfnDllCanUnload; + HRESULT hr; + + _mxs.Release(); + + // Need to check to see if the class is 16-bit. + // If it is 16-bit, then this call needs to be routed through a thunk + if (!fSixteenBit) + { + // Call through to the DLL -- does it think it can unload? + hr = (*pfnDllCanUnload)(); + if (hr == S_OK && + _pDllPathEntries[dwDll]._dwFlags & DELAYED_UNLOAD) + { + // the DLL thinks it's OK to unload, but we are employing + // delayed unloading, so go check if we've reached the + // expire time yet. + DWORD dwCurrentTime = GetTickCount(); + if (_pDllPathEntries[dwDll]._dwExpireTime == 0) + { + // first time we've reached this state, record the + // expire timer. When current time exceeds this time + // we can unload. + _pDllPathEntries[dwDll]._dwExpireTime = dwCurrentTime + DLL_DELAY_UNLOAD_TIME; + if (_pDllPathEntries[dwDll]._dwExpireTime < DLL_DELAY_UNLOAD_TIME) + { + // handle counter wrapping, we'll just wait a little + // longer once every 49.7 days. + _pDllPathEntries[dwDll]._dwExpireTime = DLL_DELAY_UNLOAD_TIME; + } + hr = S_FALSE; + } + else + { + if ((_pDllPathEntries[dwDll]._dwExpireTime > dwCurrentTime) || + (dwCurrentTime + DLL_DELAY_UNLOAD_TIME < _pDllPathEntries[dwDll]._dwExpireTime)) + { + hr = S_FALSE; + } + } + } + } + else + { + if (!IsWOWThread() || !IsWOWThreadCallable()) + { + _mxs.Request(); + return S_FALSE; + } + + hr = g_pOleThunkWOW->CallCanUnloadNow((DWORD) pfnDllCanUnload); + } + + _mxs.Request(); + return hr; + + } + + return S_FALSE; +} + + + + + +//+------------------------------------------------------------------------- +// +// Member: CDllCache::CleanUpForApartmentByDllent +// +// Synopsis: Find and delete the apartment entry for the given apt +// +// Arguments: [dwDll] - the index of this dll path entry +// [hApt] - apartment to clean up +// +// Returns: TRUE - There are no apartments using this object +// FALSE - There are still apartments using this object +// +// Algorithm: Search the list for a matching apartment entry, unlink +// it from the chain, and delete it. +// +// History: 24-Jun-94 Rickhi Created +// 10-Nov-94 Ricksa Modified for DllCanUnloadNow +// 07-Mar-95 BruceMa Rewrote +// 29-May-96 BruceMa Also release class entries for the +// specified apartment for this dll +// +//-------------------------------------------------------------------------- +BOOL CDllCache::CleanUpForApartmentByDllent(DWORD dwDll, HAPT hApt) +{ + DWORD dwNext; + // Remove all class entries associated with this apartment + DWORD dwClsent, dwNextCls; + + + // Loop through the active apartments for this dll + for (DWORD dwAptent = _pDllPathEntries[dwDll]._nAptInUse; + dwAptent != NONE; + dwAptent = dwNext) + { + + // BUGBUG: The following for loop is inside the outer for loop to + // decrease the likelihood of a race condition in which another thread in + // the multithreaded apartement creates a class entry while the Release + // routine is being called only to have the corresponding apartment entry deleted + // and even worse, the DLL unloaded out from under it. + + for (dwClsent = _pDllPathEntries[dwDll]._dw1stClass; + dwClsent != NONE; + dwClsent = dwNextCls) + { + // Pickup the next class entry now in case we delete + // this class entry + dwNextCls = _pClassEntries[dwClsent]._dwNextDllCls; + + // Remove this class entry if it's for this apartment + if (_pClassEntries[dwClsent]._hApt == hApt) + { + // Release the server + Release(dwClsent); + + // In case another thread came in and invalidated dwNextCls + dwNextCls = _pClassEntries[dwClsent]._dwNextDllCls; + + // Release the class entry + FreeClassEntry(dwClsent); + } + } + + // Save the next apartment entry in case we delete this one + dwNext = _pDllPathEntries[dwDll]._pAptEntries[dwAptent]._dwNext; + + // Only for the specified apartment + if (_pDllPathEntries[dwDll]._pAptEntries[dwAptent]._hApt == hApt) + { + HMODULE hDll = + _pDllPathEntries[dwDll]._pAptEntries[dwAptent]._hDll; + BOOL fSixteenBit = _pDllPathEntries[dwDll]._dwFlags & SIXTEEN_BIT; + + // Free the library + if (fSixteenBit && IsWOWThread() && IsWOWThreadCallable() && + !(_pDllPathEntries[dwDll]._dwFlags & IS_OLE32)) + { + + // Release the lock across the free library + // No need to worry about another thread coming in and attaching while + // the lock is released because that would only happen in the free threaded + // apartment, which cannot host 16-bit DLLs. + _mxs.Release(); + g_pOleThunkWOW->UnloadProcDll((DWORD) hDll); + // Retake the lock + _mxs.Request(); + + // In case dwNext got invalidated + dwNext = _pDllPathEntries[dwDll]._pAptEntries[dwAptent]._dwNext; + } + + // Remove the apartment entry + FreeAptEntry(dwDll, dwAptent); + } + + + } + + return _pDllPathEntries[dwDll]._nAptInUse != NONE; +} + +//+------------------------------------------------------------------------- +// +// Member: CDllCache::RemoveAndUnload +// +// Synopsis: Unload a dll +// +// Arguments: dwDll - This index of this dll path entry +// +// History: 07-Mar-95 BruceMa Created +// +// Notes: The dll has already said it can unload +// +//-------------------------------------------------------------------------- +void CDllCache::RemoveAndUnload(DWORD dwDll) +{ + CairoleDebugOut((DEB_TRACE, "RemoveAndUnload " TSZFMT " dwDll:%x\n", + _pDllPathEntries[dwDll]._ptszPath, dwDll)); + Win4Assert(_pDllPathEntries[dwDll]._nAptInUse == NONE && "Cannot unload dll with apartments attached."); + Win4Assert(_pDllPathEntries[dwDll]._dw1stClass == NONE && "Cannot unload dll with classes attached."); + + // Invalidate this entry while holding the lock because we're going to + // unload the dll + _pDllPathEntries[dwDll]._dwSig = NULL; + + // 32 bit libraries haven't been freed yet + if (!(_pDllPathEntries[dwDll]._dwFlags & SIXTEEN_BIT) && + !IsWOWThread() && + !(_pDllPathEntries[dwDll]._dwFlags & IS_OLE32) && + _pDllPathEntries[dwDll]._hDll32) + { + _mxs.Release(); + FreeLibrary(_pDllPathEntries[dwDll]._hDll32); + _mxs.Request(); + } + + // Delete the path + PrivMemFree(_pDllPathEntries[dwDll]._ptszPath); + _pDllPathEntries[dwDll]. _ptszPath = NULL; + + + delete _pDllPathEntries[dwDll]._pAptEntries; + _pDllPathEntries[dwDll]._pAptEntries = NULL; + +} + + + + + +//+------------------------------------------------------------------------- +// +// Member: CDllCache::GetClass +// +// Synopsis: Get a class factory object for a class +// +// Arguments: [rclsid] Class ID +// [riid] Interface required of class object +// [fRemote] Whether path is remote +// [fForScm] Whether it's the scm requesting +// +// Returns: ~NULL - Class factory for object +// NULL - Class factory could not be found or +// constructed. +// +// History: 09-May-93 Ricksa Created +// 07-Mar-95 BruceMa Rewrote +// +//-------------------------------------------------------------------------- +HRESULT CDllCache::GetClass(REFCLSID rclsid, + REFIID riid, + BOOL fRemote, + BOOL fForSCM, + BOOL fSurrogate, +#ifdef WX86OLE + BOOL fWx86, +#endif + IUnknown **ppunk +) +{ + TRACECALL(TRACE_DLL, "CDllCache::GetClass"); + CairoleDebugOut((DEB_TRACE, "Get class\n")); + + HRESULT hr = S_OK; + + *ppunk = NULL; + + // Single thread access to the table + COleStaticLock lck(_mxs); + + // It's on behalf of the scm + if (fForSCM) + { + DWORD dwClsent; + + // Note: On Chicago we are already on the thread that registered + // the class; RPC sees to this. On NT we are on the thread that + // registered the class because GetAptForCLSID() followed by + // GetToComThread() guarantees this. + // + // Search for the entry for this CLSID + dwClsent = Search(rclsid, CLSCTX_LOCAL_SERVER, GetCurrentApartmentId()); + + // Check if we have a registered class + if (dwClsent != NONE && + !(_pClassEntries[dwClsent]._dwFlags & REGCLS_SUSPENDED)) + { + if (_pClassEntries[dwClsent]._dwFlags == REGCLS_SINGLEUSE) + { + _pClassEntries[dwClsent]._dwContext = 0; + } + + // Release the lock across outgoing call + IUnknown *pUnkTmp = _pClassEntries[dwClsent]._pUnk; + + // Indicate we're in an outgoing call + _pClassEntries[dwClsent]._cCallOut++; + + _mxs.Release(); + // Since we are being called on behalf of SCM we know that + // we are being invoked remotely. Set the Wx86 stub invoked flag + // if we are calling into x86 code so that any custom interfaces + // can be thunked back and not rejected. +#ifdef WX86OLE + if (gcwx86.IsN2XProxy(pUnkTmp)) + { + gcwx86.SetStubInvokeFlag(1); + } +#endif + hr = pUnkTmp->QueryInterface(fSurrogate ? IID_IClassFactory : riid, + (void **)ppunk); + + _mxs.Request(); + + // We're no longer in an outgoing call + _pClassEntries[dwClsent]._cCallOut--; + + // If a revoke came in while we were calling out then do the + // revoke now + if (_pClassEntries[dwClsent]._fRevokePending && + _pClassEntries[dwClsent]._cCallOut == 0) + { + // Release our reference on the server + Release(dwClsent); + + // Free the class entry + FreeClassEntry(dwClsent); + + // Since the object has been released return failure + hr = CO_E_OBJNOTCONNECTED; + } + + return hr; + } + } + else + { + // Else it's a local request + DWORD dwClsent; + + // Search for the class + dwClsent = Search(rclsid, +#ifdef WX86OLE + fWx86 ? CLSCTX_INPROC_SERVERX86 | CLSCTX_INPROC_HANDLERX86 : + CLSCTX_INPROC, +#else + CLSCTX_INPROC, +#endif + GetCurrentApartmentId()); + + // Check if we found it + if (dwClsent != NONE) + { + // If the path is remote and we were launched AtStorage, then + // we need to be launched AtStorage again at the remote site, so + // fail locally + if (fRemote && _pClassEntries[dwClsent]._fAtStorage) + { + return NULL; + } + + // Check if it's a locally registered local server + if (_pClassEntries[dwClsent]._dwDllEnt == NONE) + { + if (_pClassEntries[dwClsent]._dwFlags == REGCLS_SINGLEUSE) + { + _pClassEntries[dwClsent]._dwContext = 0; + } + + // Release the lock across outgoing call + IUnknown *pUnkTmp = _pClassEntries[dwClsent]._pUnk; + + // Indicate we're in an outgoing call + _pClassEntries[dwClsent]._cCallOut++; + + _mxs.Release(); + + hr = pUnkTmp->QueryInterface(fSurrogate ? IID_IClassFactory : riid, + (void **)ppunk); + _mxs.Request(); + + // We're no longer in an outgoing call + _pClassEntries[dwClsent]._cCallOut--; + + // If a revoke came in while we were calling out then do the + // revoke now + if (_pClassEntries[dwClsent]._fRevokePending && + _pClassEntries[dwClsent]._cCallOut == 0) + { + // Release our reference on the server + Release(dwClsent); + + // Free the class entry + FreeClassEntry(dwClsent); + } + } + + // Else it's a dll. Note - we could have AddRef'd the + // interface the first time we got it, when we created this + // class entry, but then there would be no way to know when + // to release it and therefore the dll could never unload. So + // instead we do the same as if we had just loaded the dll. + else + { + *ppunk = GetClassInterface(_pClassEntries[dwClsent]._dwDllEnt, + _pClassEntries[dwClsent]._dwDllThreadModel, + rclsid, + riid, + hr); + } + + return hr; + } + + } + + // + // It's ok to have no class factory in the cache, so return a success + // error code here. + // + Win4Assert( *ppunk == 0 ); + return S_OK; +} + +//+------------------------------------------------------------------------- +// +// Member: CDllCache::GetOrLoadClass +// +// Synopsis: Get a class factory object for a class, loading an INPROC +// DLL if needed +// +// Arguments: [rclsid] Class ID +// [riid] Interface required of class object +// [fRemote] Whether path is remote +// [fForScm] Whether it's the scm requesting +// [dwContext] Which context to load +// [dwCallerThreadModel] Which threading model to load +// [hr] Reference to HRESULT for error returns +// +// Returns: ~NULL - Class factory for object +// NULL - Class factory could not be found or +// constructed. +// +// History: 09-May-93 KevinRo +// +// Note: +// +//-------------------------------------------------------------------------- +IUnknown *CDllCache::GetOrLoadClass(REFCLSID rclsid, + REFIID riid, + BOOL fRemote, + BOOL fForSCM, +#ifdef WX86OLE + BOOL fWx86, +#endif + DWORD dwContext, + DWORD dwCallerThreadModel, + HRESULT &hr) +{ + CairoleDebugOut((DEB_ITRACE, + "CDllCache::GetOrLoadClass(clsid(%I),riid(%I),fRemote(%x),fForSCM(%x)" + ",dwContext(%x),dwCallerThreadModel(%x))\n", + &rclsid, + &riid, + fRemote, + fForSCM, + dwContext, + dwCallerThreadModel)); + + IUnknown *punk = NULL; + + hr = S_OK; + +#ifndef GET_INPROC_FROM_SCM + // Just in case we chicken out and back out our changes + // + // The context should either be INPROC_HANDLER or INPROC_SERVER, but + // never both. + // + Win4Assert((dwContext & CLSCTX_INPROC_HANDLERS) || (dwContext & CLSCTX_INPROC_SERVERS)); + Win4Assert(!!(dwContext & CLSCTX_INPROC_SERVERS) != !!(dwContext & CLSCTX_INPROC_HANDLERS)); +#endif // GET_INPROC_FROM_SCM + + // + // First, check to see if the class is available in the cache + // already. If it is, grab it and get out. + // +#ifdef WX86OLE + hr = GetClass(rclsid,riid,fRemote,fForSCM,FALSE,fWx86,&punk); +#else + hr = GetClass(rclsid,riid,fRemote,fForSCM,FALSE,&punk); +#endif + + // + // If it is, then just return it + // + if (punk != NULL) + { + CairoleDebugOut((DEB_ITRACE, + "::GetOrLoadClass(clsid(%I)...) found class in cache",&rclsid)); + return(punk); + } + +#ifndef GET_INPROC_FROM_SCM + // Just in case we chicken out and back out our changes + // + // The CLSID wasn't found. Look it up + // in the registry and see if it exists. We follow a priority order + // + + CairoleDebugOut((DEB_ITRACE,"::GetClass clsid(%I) not found. Try loading\n",&rclsid)); + + TCHAR achBuffer[80]; // Holds the string CLSID\{guid}\InprocServer|handler etc + + memcpy(achBuffer,CLSIDBACK,CLSIDBACK_BYTE_LEN); + + wStringFromGUID2T(rclsid,&achBuffer[CLSIDBACK_CHAR_LEN],GUIDSTR_MAX); + + // + // achBuffer now has the string 'CLSID\{strofguid}'. This is the prefix string we + // need for doing the following code. Each bit of code below will stomp its own. + // + // Note that GUIDSTR_MAX is the number of characters including the NULL of the + // length of a GUID. We are going to manually append an additional slash to + // the string, which 'eats' the NULL character. + // +#define PREFIX_STRING_OFFSET (CLSIDBACK_CHAR_LEN + GUIDSTR_MAX ) + + achBuffer[PREFIX_STRING_OFFSET - 1] = '\\'; + + TCHAR achDllPath[MAX_PATH]; + LONG clDllPath; + ULONG ulDllType; + LONG lErr; + + // + // Assume it won't be found + // + hr = REGDB_E_CLASSNOTREG; + + // If 16-bit has been requested and we find it, we'll return that + if (dwContext & CLSCTX_INPROC_SERVER16) + { + clDllPath = MAX_PATH; + memcpy(&achBuffer[PREFIX_STRING_OFFSET],tszInprocServer16,sizeof(tszInprocServer16)); + + CairoleAssert(punk == NULL); + hr = REGDB_E_CLASSNOTREG; + + // Read 16 bit DLL information + if (wQueryStripRegValue(HKEY_CLASSES_ROOT,achBuffer,achDllPath,&clDllPath) == ERROR_SUCCESS) + { + // + // Found a 16-bit INPROC server. Add it to the cache. + // +#ifdef WX86OLE + punk = Add(rclsid,riid,APT_THREADED,achDllPath,TRUE,TRUE,FALSE,hr); +#else + punk = Add(rclsid,riid,APT_THREADED,achDllPath,TRUE,TRUE,hr); +#endif + } + } + +#ifdef WX86OLE + if ((punk == NULL) && (fWx86) && (dwContext & CLSCTX_INPROC_SERVERX86)) + { + clDllPath = MAX_PATH; + memcpy(&achBuffer[PREFIX_STRING_OFFSET],tszInprocServerX86,sizeof(tszInprocServerX86)); + + hr = REGDB_E_CLASSNOTREG; + + // Read 32 bit DLL information + if (wGetDllInfo(HKEY_CLASSES_ROOT,achBuffer,achDllPath,&clDllPath,&ulDllType) == ERROR_SUCCESS) + { + // + // If we are after a proxy/stub dll, then load it as both + // no matter what the DLL says. + // + if (dwContext & CLSCTX_PS_DLL) + { + ulDllType = BOTH_THREADED; + } + + // + // If it turns out this path is for OLE32.DLL, then add the DLL without the + // path. + // + LPCTSTR pDllName = wCompareDllName(achDllPath,OLE32_DLL,OLE32_CHAR_LEN)? + OLE32_DLL:achDllPath; + + // + // load it. + // + punk = Add(rclsid,riid,ulDllType,achDllPath,TRUE,FALSE, + !(pDllName == OLE32_DLL),hr); + } + } +#endif + + // + // Could be that we are trying to load an INPROC_SERVER + // + if ( (punk == NULL) && (dwContext & CLSCTX_INPROC_SERVER)) + { + clDllPath = MAX_PATH; + + // + // Need to reassign since it could have changed above + // + hr = REGDB_E_CLASSNOTREG; + + memcpy(&achBuffer[PREFIX_STRING_OFFSET],tszInprocServer,sizeof(tszInprocServer)); + + // Read 32-bit DLL information + if (wGetDllInfo(HKEY_CLASSES_ROOT,achBuffer,achDllPath,&clDllPath,&ulDllType) == ERROR_SUCCESS) + { + // + // If we are after a proxy/stub dll, then load it as both + // no matter what the DLL says. + // + if (dwContext & CLSCTX_PS_DLL) + { + ulDllType = BOTH_THREADED; + } + // + // load it. + // +#ifdef WX86OLE + punk = Add(rclsid,riid,ulDllType,achDllPath,TRUE,FALSE,FALSE,hr); +#else + punk = Add(rclsid,riid,ulDllType,achDllPath,TRUE,FALSE,hr); +#endif + } + } + + // + // If INPROC_HANDLER16 set, then we look to load a 16-bit handler. + // If the handler is ole2.dll, then we will load ole32 instead. + // Otherwise we load the found 16bit dll but only if in a WOW thread. + // + if ((punk == NULL) && (dwContext & CLSCTX_INPROC_HANDLER16 )) + { + clDllPath = MAX_PATH; + + memcpy(&achBuffer[PREFIX_STRING_OFFSET],tszInprocHandler16,sizeof(tszInprocHandler16)); + + lErr = wGetDllInfo(HKEY_CLASSES_ROOT,achBuffer,achDllPath,&clDllPath,&ulDllType); + + // + // Need to reassign since it could have changed above + // + hr = REGDB_E_CLASSNOTREG; + + if (lErr == ERROR_SUCCESS) + { + // + // If the inproc handler is ole2.dll, then subst + // ole32.dll instead + // + if (wCompareDllName(achDllPath,OLE2_DLL,OLE2_CHAR_LEN)) + { + // Add and load OLE32.DLL +#ifdef WX86OLE + punk = Add(rclsid,riid,BOTH_THREADED,OLE32_DLL,TRUE,FALSE,FALSE,hr); +#else + punk = Add(rclsid,riid,BOTH_THREADED,OLE32_DLL,TRUE,FALSE,hr); +#endif + } + else + { + // Otherwise, load the 16-bit fellow but only if in WOW thread + if (IsWOWThread()) + { +#ifdef WX86OLE + punk = Add(rclsid,riid,ulDllType,achDllPath,TRUE,TRUE,FALSE,hr); +#else + punk = Add(rclsid,riid,ulDllType,achDllPath,TRUE,TRUE,hr); +#endif + } + } + } + } + + // + // See about 32-bit handlers. A was a change made after the + // Win95 (August release) and Windows/NT 3.51 release. Previously + // the code would give preference to loading 32-bit handlers. This + // means that even if an ISV provided both 16 and 32-bit handlers, + // the code would only attempt to provide the 32-bit handler. This + // was bad because servers with handlers could not be inserted into + // containers in the wrong model. We have fixed it here. + // + // Another thing to watch out for are applications that use our + // default handler. 16-bit applications can and should be able to + // use OLE32 has a handler. This will happen if the server app is + // actually a 32-bit. + // + // +#ifdef WX86OLE + if((punk == NULL) && (fWx86) && (dwContext & CLSCTX_INPROC_HANDLERX86)) + { + clDllPath = MAX_PATH; + + memcpy(&achBuffer[PREFIX_STRING_OFFSET],tszInprocHandlerX86,sizeof(tszInprocHandlerX86)); + + lErr = wGetDllInfo(HKEY_CLASSES_ROOT,achBuffer,achDllPath,&clDllPath,&ulDllType); + + // + // Need to reassign since it could have changed above + // + hr = REGDB_E_CLASSNOTREG; + + if (lErr == ERROR_SUCCESS) + { + // + // If it turns out this path is for OLE32.DLL, then add the DLL without the + // path. + // + LPCTSTR pDllName = wCompareDllName(achDllPath,OLE32_DLL,OLE32_CHAR_LEN)? + OLE32_DLL:achDllPath; + + // Add a 32-bit handler to the pile. + punk = Add(rclsid,riid,ulDllType,pDllName,TRUE,FALSE,!(pDllName == ptszOle32DllName),hr); + } + } +#endif + + if((punk == NULL) && (dwContext & CLSCTX_INPROC_HANDLERS)) + { + clDllPath = MAX_PATH; + + memcpy(&achBuffer[PREFIX_STRING_OFFSET],tszInprocHandler,sizeof(tszInprocHandler)); + + lErr = wGetDllInfo(HKEY_CLASSES_ROOT,achBuffer,achDllPath,&clDllPath,&ulDllType); + + // + // Need to reassign since it could have changed above + // + hr = REGDB_E_CLASSNOTREG; + + if (lErr == ERROR_SUCCESS) + { + // + // If it turns out this path is for OLE32.DLL, then add the DLL without the + // path. + // + LPCTSTR pDllName = wCompareDllName(achDllPath,OLE32_DLL,OLE32_CHAR_LEN)? + OLE32_DLL:achDllPath; + + // + // If we are looking for a INPROC_HANDER16 and this is OLE32.DLL, or if we + // are looking for an INPROC_HANDLER, then load this path. Note that pDllName + // was set above. + // + // If we're in a Wow thread the only 32 bit DLL we're allowed to load is + // OLE32.DLL + if ((IsWOWThread() && (pDllName == OLE32_DLL)) || + (!IsWOWThread() )) + { + // Add a 32-bit handler to the pile. +#ifdef WX86OLE + punk = Add(rclsid,riid,ulDllType,pDllName,TRUE,FALSE,FALSE,hr); +#else + punk = Add(rclsid,riid,ulDllType,pDllName,TRUE,FALSE,hr); +#endif + } + } +#ifdef WX86OLE + else if (gcwx86.IsWx86Installed() && (! gcwx86.IsWx86Enabled())) + { + // If Wx86 is installed on this system, but this is not a Wx86 + // process and we could not find an InprocHandler32 we want to + // look for a InprocHandlerX86 in case an x86 local server is + // avaialable. If we find an InprocHandlerX86 and it is Ole32.dll + // then we will use it otherwise we can't since we assume it is + // an x86 dll. + clDllPath = MAX_PATH; + + memcpy(&achBuffer[PREFIX_STRING_OFFSET],tszInprocHandlerX86,sizeof(tszInprocHandlerX86)); + + lErr = wGetDllInfo(HKEY_CLASSES_ROOT,achBuffer,achDllPath,&clDllPath,&ulDllType); + + if (lErr == ERROR_SUCCESS) + { + // + // If it turns out this path is for OLE32.DLL, then add the DLL without the + // path. + // + BOOLEAN fIsOle32 = wCompareDllName(achDllPath,OLE32_DLL,OLE32_CHAR_LEN); + if (fIsOle32) + { + // Only if it is Ole32.Dll Add a 32-bit handler to native pile. + LPCTSTR pDllName = OLE32_DLL; + punk = Add(rclsid,riid,ulDllType,pDllName,TRUE,FALSE,FALSE,hr); + } + } + } + if (lErr != ERROR_SUCCESS) +#else + else +#endif + { + // We're here if we couldn't find a 32-bit handler. If non-Wow caller didn't + // explicitly request 16-bit handler we'll look for one here. But the only one + // allowed is OLE2.DLL => OLE32.DLL + if (!IsWOWThread() && !(dwContext & CLSCTX_INPROC_HANDLER16)) + { + clDllPath = MAX_PATH; + + memcpy(&achBuffer[PREFIX_STRING_OFFSET],tszInprocHandler16,sizeof(tszInprocHandler16)); + + lErr = wGetDllInfo(HKEY_CLASSES_ROOT,achBuffer,achDllPath,&clDllPath,&ulDllType); + + // + // Need to reassign since it could have changed above + // + hr = REGDB_E_CLASSNOTREG; + + if (lErr == ERROR_SUCCESS) + { + // + // If the inproc handler is ole2.dll, then subst + // ole32.dll instead + // + if (wCompareDllName(achDllPath,OLE2_DLL,OLE2_CHAR_LEN)) + { + // Add and load OLE32.DLL +#ifdef WX86OLE + punk = Add(rclsid,riid,BOTH_THREADED,OLE32_DLL,TRUE,FALSE,FALSE,hr); +#else + punk = Add(rclsid,riid,BOTH_THREADED,OLE32_DLL,TRUE,FALSE,hr); +#endif + } + } + } + } + } + + return(punk); +#else // GET_INPROC_FROM_SCM + return NULL; // don't have it cached +#endif // GET_INPROC_FROM_SCM +} + + + + +//+--------------------------------------------------------------------------- +// +// Method: CDllCache::GetClassObjForDde +// +// Synopsis: Get a class entry from the table for Dde, returning +// extra information, including the flags. +// +// Effects: The DdeServer needs the ability to query the class factory +// table to search for classes it needs to provide OLE 1.0 +// support for. This routine will allow it to access the +// required information. +// +// Arguments: [clsid] Class to lookup ClassObject for +// [lpDdeInfo] Structure to fill in +// +// Returns: TRUE if the entry matched, FALSE if it did not. +// +// History: 5-28-94 kevinro Created +// 07-Mar-95 BruceMa Rewrote +// +// Notes: +// +//---------------------------------------------------------------------------- +BOOL CDllCache::GetClassObjForDde(REFCLSID clsid, + LPDDECLASSINFO lpDdeInfo) +{ + TRACECALL(TRACE_DLL, "CDllCache::GetClassObjForDde"); + + CairoleDebugOut((DEB_TRACE, "Get class object for DDE\n")); + + // Single thread access to the table + COleStaticLock lck(_mxs); + + Win4Assert(IsValidPtrOut(lpDdeInfo, sizeof(DWORD)) && + "CDllCache::GetClassObjForDde invalid out parameter"); + + DWORD dwClsent = Search(clsid, CLSCTX_LOCAL_SERVER, GetCurrentApartmentId()); + + if (dwClsent != NONE) + { + return GetClassObjForDdeByClsent(dwClsent, lpDdeInfo); + } + + return FALSE; +} + + + + + +//+--------------------------------------------------------------------------- +// +// Member: CDllCache::GetClassInformationFromKey +// +// Synopsis: Get class object information for the Dde server using a key +// +// Effects: This routine is called by the DDE server code to retrieve the +// class information for a specific class registration. The +// theory is that the DDE server window has already called +// GetClassInformationForDde. Some time X has passed, and the +// server has a request for the specific class registration. +// +// This routine allows the DDE server to request current +// class information by specific registration key. +// +// Arguments: [lpDdeInfo] Structure to fill in with information +// +// Requires: lpDdeInfo->dwRegistrationKey is the key to the specific +// class being asked for. +// +// Returns: TRUE Structure filled in with appropriate information +// FALSE The registration is no longer valid. +// +// Signals: +// +// Modifies: +// +// Algorithm: +// +// History: 5-28-94 kevinro Created +// 07-Mar-95 BruceMa Rewrote +// +// Notes: +// +//---------------------------------------------------------------------------- +BOOL CDllCache::GetClassInformationFromKey(LPDDECLASSINFO lpDdeInfo) +{ + TRACECALL(TRACE_DLL, "CDllCache::GetClassInformationFromKey"); + + CairoleDebugOut((DEB_TRACE, "Get class inbfo from key for DDE\n")); + + // Single thread access to the table + COleStaticLock lck(_mxs); + + DWORD dwClsent = Search(lpDdeInfo->dwRegistrationKey, GetCurrentApartmentId()); + + if (dwClsent != NONE) + { + return GetClassObjForDdeByClsent(dwClsent, lpDdeInfo); + } + + return FALSE; +} + + + + +#ifdef _CHICAGO_ +//+------------------------------------------------------------------------- +// +// Member: CDllCache::GetApartmentForCLSID +// +// Synopsis: Get the apartment id for this clsid +// +// Arguments: [rclsid] class ID +// [hApt] Where to return the apartment id +// +// Returns: TRUE Got the apartment id for an available class object +// FALSE No available class object for given class +// +// History: 30-Apr-93 JohannP Created +// 07-Mar-95 BruceMa Rewrote +// 06-Oct-95 BruceMa Make for Chicago only +// +//-------------------------------------------------------------------------- +inline BOOL CDllCache::GetApartmentForCLSID(REFCLSID rclsid, HAPT &hApt) +{ + + CairoleDebugOut((DEB_TRACE, "Get apartment for CLSID\n")); + + // On Chicago we are already on the correct thread + hApt = GetCurrentApartmentId(); + return TRUE; +} +#endif // _CHICAGO_ + + + + + +//+------------------------------------------------------------------------- +// +// Member: CDllCache::Add +// +// Synopsis: Add a DLL entry to the cache +// +// Arguments: [rclsid] - class id +// [riid] - interface required of the class object +// [ptszDllPath] - path to DLL +// [fGetClassObject] - whether class factory object is needed. +// [fSixteenBit] - TRUE if we want the 16bit dll +// [fWx86] - TRUE if x86 dll in Wx86 process +// [hr] - hresult returned +// +// Returns: Pointer to class factory if requested. +// +// Algorithm: Create a path key. If such a DLL is already cached, use +// that otherwise create a new DLL path entry. Then add +// a new class object. +// +// History: 09-May-93 Ricksa Created +// 28-Jun-94 BruceMa Memory SIFT fixes +// 07-Jul-94 BruceMa Memory SIFT fixes +// 21-Nov-94 BruceMa Don't return E_OUTOFMEMORY if can't find +// dll +// 07-Mar-95 BruceMa Rewrote +// +// Notes: +// +//-------------------------------------------------------------------------- +IUnknown *CDllCache::Add(REFCLSID rclsid, + REFIID riid, + DWORD dwDllThreadModel, + const TCHAR *ptszDllPath, + BOOL fGetClassObject, + BOOL fSixteenBit, +#ifdef WX86OLE + BOOL fWx86, +#endif + HRESULT& hr) +{ + TRACECALL(TRACE_DLL, "CDllCache::Add"); + CairoleDebugOut((DEB_TRACE, "Add dll %ts to cache\n", ptszDllPath)); + + hr = E_FAIL; + + IUnknown *punk = NULL; + + // Single thread access to the table + COleStaticLock lck(_mxs); + + // This better be a valid dll path + if (ptszDllPath != NULL) + { + LPFNGETCLASSOBJECT pfnGetClassObject; + DLLUNLOADFNP pfnDllCanUnload; + HMODULE hDll; + +#ifdef WX86OLE + BOOL fIsX86Dll = FALSE; +#endif + + // Check if we already have an entry for this dll + DWORD dwDllent = SearchForDll(ptszDllPath +#ifdef WX86OLE + , fWx86 +#endif +); + // If not, create a new dll path entry + if (dwDllent == NONE) + { + // Check if this is "OLE32.DLL" - we don't need to load + // ourselves; we're already running + if (lstrcmp(ptszDllPath, ptszOle32DllName) + != 0) + { + // We need to release the lock across the LoadLibrary since + // there is a chance that an exiting thread waits on our + // mutext to CleanUpForApartment while we wait on the kernel + // mutex which the exiting thread owns, causing a deadlock. + _mxs.Release(); + + // Load the library + hr = Load(ptszDllPath, + &pfnGetClassObject, + &pfnDllCanUnload, + fSixteenBit, + &hDll +#ifdef WX86OLE + ,&fIsX86Dll, + fWx86 +#endif +); + + // Retake the lock while intializng the dll entry + _mxs.Request(); + + // Check for success + if (FAILED(hr)) + { + return NULL; + } + } + + // Check whether another thread got in and loaded the dll + dwDllent = SearchForDll(ptszDllPath +#ifdef WX86OLE + , fWx86 +#endif +); + + // If so, then use that + if (dwDllent != NONE) + { + // Make it valid for this apartment + if (FAILED(MakeValidInApartment(dwDllent))) + { + return NULL; + } + _mxs.Release(); + FreeLibrary(hDll); + _mxs.Request(); + } + + // Else create a new dll entry + else + { + // Allocate a dll path entry + dwDllent = AllocDllPathEntry(); + if (dwDllent == NONE) + { + hr = E_OUTOFMEMORY; + return NULL; + } + + // Initialize the dll path entry (this will load the dll) + hr = CreateDllent(dwDllent, + ptszDllPath, + fSixteenBit, + pfnGetClassObject, + pfnDllCanUnload, + hDll +#ifdef WX86OLE + ,fIsX86Dll, + fWx86 +#endif +); + if (FAILED(hr)) + { + FreeDllPathEntry(dwDllent); + return NULL; + } + + _pDllPathEntries[dwDllent]._dw1stClass = NONE; + + // Make it valid for this apartment + if (FAILED(MakeValidInApartment(dwDllent))) + { + return NULL; + } + } + } + + CairoleAssert(dwDllent != NONE); + + + // Get requested interface + punk = GetClassInterface(dwDllent, + dwDllThreadModel, + rclsid, + riid, + hr); + + if (SUCCEEDED(hr)) + { + // Add a class entry for this interface if we don't already + // have one + + // search according to type of dll as passed into thus func + if (Search(rclsid, +#ifdef WX86OLE + fWx86 ? CLSCTX_INPROC_SERVERX86 : +#endif + CLSCTX_INPROC_SERVER, + GetCurrentApartmentId()) == NONE) + { + DWORD dwClsent = AllocClassEntry(); + if (dwClsent == NONE) + { + return NULL; + } + CreateClsentInProc(dwClsent, + dwDllent, + dwDllThreadModel, + _pDllPathEntries[dwDllent]._dw1stClass, + rclsid +#ifdef WX86OLE + ,fWx86 +#endif +); + _pDllPathEntries[dwDllent]._dw1stClass = dwClsent; + } + + return punk; + } + } + + return NULL; +} + + + + + +//+------------------------------------------------------------------------- +// +// Member: CDllCache::FreeUnused +// +// Synopsis: Free any unused DLLs +// +// Algorithm: For each DLL in the list of DLLs call its Dll can unload +// now entry point. +// +// History: 09-May-93 Ricksa Created +// 04-May-94 AlexT Only free DLL if it returns S_OK! +// 07-Mar-95 BruceMa Rewrote +// +// +// +//-------------------------------------------------------------------------- +void CDllCache::FreeUnused(void) +{ + TRACECALL(TRACE_DLL, "CDllCache::FreeUnused"); + + CairoleDebugOut((DEB_TRACE, "Free unused dll's\n")); + + // Single thread access to the table + COleStaticLock lck(_mxs); + + // Unless the code is changed, we should not be here in the Wow case + CairoleAssert(!IsWOWProcess() && "Freeing unused libraries in WOW"); + + // Free any loaded dll's (only if we still have a cache) + if (_pDllPathEntries) + { + // Sequentially scan the list of loaded dll's, unloading where we can + DWORD dwNext; + + for (DWORD dwDllent = _nDllPathEntryInUse; + dwDllent != NONE; + dwDllent = dwNext) + { + // Save the next entry in case we free this one + dwNext = _pDllPathEntries[dwDllent]._dwNext; + + // Check if we can unload this dll + if (CanUnloadNow(dwDllent) == S_OK) + { + // remove our apartment from the dll + BOOL fRemove = !CleanUpForApartmentByDllent(dwDllent, GetCurrentApartmentId()); + + // get the next entry again in case we Released the lock + // in the above routine + dwNext = _pDllPathEntries[dwDllent]._dwNext; + + if (fRemove) + { + RemoveAndUnload(dwDllent); + FreeDllPathEntry(dwDllent); + } + } + } + } +} + + + + + +//+------------------------------------------------------------------------- +// +// Member: CDllCache::RegisterServer +// +// Synopsis: Register a class factory +// +// Arguments: [rclsid] - class ID +// [punk] - class factory instance ptr +// [flags] - type of factory instance +// +// Returns: Registration key +// +// Algorithm: Create a class entry and then add server to list of +// registered servers. +// +// History: 09-May-93 Ricksa Created +// 07-Mar-95 BruceMa Rewrote +// +//-------------------------------------------------------------------------- +HRESULT CDllCache::RegisterServer(REFCLSID rclsid, + IUnknown *punk, + DWORD dwFlags, + DWORD dwContext, + LPDWORD lpdwRegister) +{ + CairoleDebugOut((DEB_TRACE, "Register server\n")); + TRACECALL(TRACE_DLL, "CDllCache::RegisterServer"); + + HRESULT hr = E_OUTOFMEMORY; + + // Take a reference immediately (if we fail for any reason we + // Release it below) + punk->AddRef(); + + { + // scoped single thread access to the table + COleStaticLock lck(_mxs); + + // Allocate a new class entry + DWORD dwClsent = AllocClassEntry(); + if (dwClsent != NONE) + { + // Formulate a unique registration key for this class entry + DWORD dwReg = (GetCurrentApartmentId()) << 16 | dwClsent; + + // Initialize it + hr = CreateClsentLSvr(dwClsent, + rclsid, + punk, + dwFlags, + dwContext, + dwReg); + + if (SUCCEEDED(hr)) + { + *lpdwRegister = dwReg; + } + else + { + FreeClassEntry(dwClsent); + } + } + } + + // If we failed we release our reference. This is done outside the + // scope of the lock. + if (FAILED(hr)) + { + punk->Release(); + } + + return(hr); +} + + + + + +//+------------------------------------------------------------------------- +// +// Member: CDllCache::Revoke +// +// Synopsis: Revoke the registration of a previously registered +// class object. +// +// Arguments: [dwRegister] - registration key +// +// Returns: TRUE - remote objects have been deregistered. +// FALSE - remote objects were not deregistered. +// +// Algorithm: First validate the registration key. If objects have +// been revoked already we will always say TRUE. Then if +// the object is remote, revoke this list of remote objects. +// For local objects we just revoke the single entry. +// +// Note: dwRegistry has the form aptId << 16 + CClassEntry index +// +// History: 09-May-93 Ricksa Created +// 01-Jul-94 AlexT Don't call out while holding mutex +// 13-Feb-95 BruceMa Change registration/revocation logic +// 07-Mar-95 BruceMa Rewrote +// +//-------------------------------------------------------------------------- +HRESULT CDllCache::Revoke(DWORD dwRegister) +{ + IUnknown *pUnk; + + TRACECALL(TRACE_DLL, "CDllCache::Revoke"); + + CairoleDebugOut((DEB_TRACE, "Revokeclass object %x\n", dwRegister)); + + // Single thread access to the table + COleStaticLock lck(_mxs); + + DWORD dwClsent = dwRegister & 0xffff; + + + // There is case where an app calls CoRevoke from CoDisconnectObject which + // we call from Release - so guard against this recursive behavior + if (_pClassEntries[dwClsent]._fRevoking) + { + return S_OK; + } + + // Make sure the registration key contains an index to a valid + // in use class entry and that the class entry is still valid + if (!(0 <= dwClsent && + dwClsent < _cClassEntries && + _pClassEntries[dwClsent]._dwSig == CLASS_CACHE_SIG)) + { + CairoleAssert("CDllCache::Revoke Invalid registration key"); + return CO_E_OBJNOTREG; + } + + // Make sure apartment id's match if we're not free threaded + DWORD dwRegAptId = (dwRegister >> 16) & 0xffff; // Chicago has + // negative tid's + HAPT hCurrApt = GetCurrentApartmentId(); + + if (!(dwRegAptId == (hCurrApt & 0xffff) && + hCurrApt == _pClassEntries[dwClsent]._hApt)) + { + CairoleDebugOut((DEB_ERROR, + "CDllCache::Revoke %x: Wrong thread attempting to revoke\n", dwRegister)); + return RPC_E_WRONG_THREAD; + } + + // If there is an active outgoing call on another thread, then let + // that thread do the revoke + if (_pClassEntries[dwClsent]._cCallOut > 0) + { + _pClassEntries[dwClsent]._fRevokePending = TRUE; + return S_OK; + } + + // Release our reference on the server + _pClassEntries[dwClsent]._fRevoking = TRUE; + Release(dwClsent); + + // Free the class entry + FreeClassEntry(dwClsent); + + return S_OK; +} + + +//+------------------------------------------------------------------------- +// +// Member: CDllCache::AddRefServerProcess +// +// Synopsis: increments the reference count on the server process +// +// Notes: CoAddRefServerProcess and CoReleaseServerProcess are used +// by the applications object instances and LockServer +// implementation in order to control the lifetime of the server +// process. +// +// When a new object instance is created, and when class object's +// LockServer(TRUE) method is called, applications should call +// CoAddRefServerProcess. When an object instance's reference +// count reaches zero, and when the class object's +// LockServer(FALSE) method is called, applications should call +// CoReleaseServerProcess. +// +// When the server's global reference count reaches zero, all +// externaly registered class objects are automatically +// suspended, allowing the server to shutdown in a thread-safe +// manner. +// +// History: 17-Apr-96 Rickhi Created +// +//+------------------------------------------------------------------------- +ULONG CDllCache::AddRefServerProcess(void) +{ + COleStaticLock lck(_mxs); + return ++_cRefsServerProcess; +} + +ULONG CDllCache::ReleaseServerProcess(void) +{ + COleStaticLock lck(_mxs); + + ULONG cRefs = --_cRefsServerProcess; + if (cRefs == 0) + { + HRESULT hr = SuspendProcessClassObjects(); + Win4Assert(hr == S_OK); + } + return cRefs; +} + +//+------------------------------------------------------------------------- +// +// Member: CDllCache::SuspendProcessClassObjects +// +// Synopsis: Marks all the externally registered class objects in this +// process as suspended, so that no new activation requests +// from the SCM will be honoured. +// +// Notes: See AddRefServerProcess and ReleaseServerProcess above. +// +// History: 17-Apr-96 Rickhi Created +// +//+------------------------------------------------------------------------- +HRESULT CDllCache::SuspendProcessClassObjects(void) +{ + CairoleDebugOut((DEB_ACTIVATE, "SuspendProcessClassObjects\n")); + COleStaticLock lck(_mxs); + + // walk the list of class entries, and for any that are registered as + // local servers, mark them as suspended. + + for (int k = _nClassEntryInUse; k != NONE; k = _pClassEntries[k]._dwNext) + { + if (_pClassEntries[k]._dwContext & CLSCTX_LOCAL_SERVER) + { + _pClassEntries[k]._dwFlags |= REGCLS_SUSPENDED; + } + } + + return S_OK; +} + +//+------------------------------------------------------------------------- +// +// Member: CDllCache::ResumeProcessClassObjects +// +// Synopsis: Marks all the externally registered class objects in this +// process as available, so that new activation requests from +// the SCM will be honoured. This also notifies the SCM about +// any class objects that have been registered suspended. +// +// Notes: See SuspendProcessClassObjects above. +// +// History: 17-Apr-96 Rickhi Created +// +//+------------------------------------------------------------------------- +HRESULT CDllCache::ResumeProcessClassObjects(void) +{ + CairoleDebugOut((DEB_ACTIVATE, "ResumeProcessClassObjects\n")); + + HRESULT hr = S_OK; + COleStaticLock lck(_mxs); + + // if none in use, exit early, otherwise _nClassEntryInUse == -1 and + // we try to allocate a huge amount of stack space. + + if (_nClassEntryInUse == NONE) + return hr; + + // allocate a block of memory on the stack, large enough to hold the + // maximum number of entries we may have to register with the SCM. + + ULONG cbAlloc = sizeof(RegInput) + + ((sizeof(RegInputEntry) + sizeof(DWORD))* + _nClassEntryInUse); + + RegInput *pRegIn = (RegInput*) _alloca(cbAlloc); + RegInputEntry *pRegEnt = &pRegIn->rginent[0]; + DWORD *pRegIndex = (DWORD *)(&pRegIn->rginent[_nClassEntryInUse+1]); + ULONG cToReg = 0; + + + // walk the list of class entries, and for any that are registered as + // local servers and marked suspended, mark them as available and + // notify the SCM about them. + + for (int k = _nClassEntryInUse; k != NONE; k = _pClassEntries[k]._dwNext) + { + if ((_pClassEntries[k]._dwFlags & REGCLS_SUSPENDED) && + (_pClassEntries[k]._dwScmReg == NONE)) + { + // must be for a local server + Win4Assert(_pClassEntries[k]._dwContext & CLSCTX_LOCAL_SERVER); + Win4Assert(_pClassEntries[k]._pObjServer != NULL); + + // turn off the suspended flag for this clsid. + _pClassEntries[k]._dwFlags &= ~REGCLS_SUSPENDED; + + // add to the list to tell the SCM about + pRegEnt->clsid = _pClassEntries[k]._clsid; + pRegEnt->dwFlags = _pClassEntries[k]._dwFlags; + pRegEnt->oxid = _pClassEntries[k]._pObjServer->GetOXID(); + pRegEnt->ipid = _pClassEntries[k]._pObjServer->GetIPID(); + pRegEnt++; + + *pRegIndex = k; // remember the index of this entry + pRegIndex++; // so we can update it below. + + cToReg++; + } + } + + // reset the pointers we mucked with in the loop above, and set the + // total number of entries we are passing to the SCM. + + pRegIn->dwSize = cToReg; + pRegEnt = &pRegIn->rginent[0]; + pRegIndex = (DWORD *)(&pRegIn->rginent[_nClassEntryInUse+1]); + + + // call the SCM to register all the classes and get back all the + // registration keys. + + RegOutput *pRegOut = NULL; + _mxs.Release(); + hr = gResolver.NotifyStarted(pRegIn, &pRegOut); + _mxs.Request(); + + + if (SUCCEEDED(hr)) + { + Win4Assert((pRegOut->dwSize == pRegIn->dwSize) && + "CRpcResolver::NotifyStarted Invalid regout"); + + // update the entries with the registration keys from the SCM. + for (ULONG i = 0; i < cToReg; i++) + { + k = *pRegIndex; + pRegIndex++; + + _pClassEntries[k]._fAtStorage = pRegOut->regoutent[0].dwAtStorage; + _pClassEntries[k]._dwScmReg = pRegOut->regoutent[0].dwReg; + } + + // Free memory from RPC + MIDL_user_free(pRegOut); + } + + return hr; +} + + +//+--------------------------------------------------------------------------- +// +// Member: CDllCache::SetDdeServerWindow +// +// Synopsis: Finds the registration associated with dwKey, and sets the +// HWND for the DDE server. +// +// Effects: Part of the shutdown of a class object involves telling the +// DDE server to stop servicing requests for the class. The +// communication mechanism used between the DDE server and +// the Class Registration Table is a window handle. +// +// During the initial creation of the DDE server window, we +// don't know what the window handle is going to be. This +// routine allows us to set the window handle into the table, +// so we can be called back. +// +// It is also possible that the Server Window may go away +// before the class object is revoked. This routine is also +// called in that case to set the hwnd to NULL. +// +// Arguments: [dwKey] -- Key for the registration +// [hwndDdeServer] -- Window handle to Dde Server +// +// Returns: TRUE if call was successful. +// FALSE if the dwKey was not valid. +// +// History: 7-05-94 kevinro Created +// 07-Mar-95 BruceMa Rewrote +// +// Notes: +// +// This is part of the DDE server support. The code that calls it is +// in com\remote\dde\server +// +//---------------------------------------------------------------------------- +BOOL CDllCache::SetDdeServerWindow(DWORD dwKey, HWND hwndDdeServer) +{ + TRACECALL(TRACE_DLL, "CDllCache::SetDdeServer"); + + CairoleDebugOut((DEB_TRACE, "Set DDE server window\n")); + + // Single thread access to the table + COleStaticLock lck(_mxs); + + Win4Assert(dwKey != 0); + Win4Assert((hwndDdeServer == NULL) || IsWindow(hwndDdeServer)); + + // Search for the class entry + DWORD dwClsent = Search(dwKey, GetCurrentApartmentId()); + + // Found it + if (dwClsent != NONE) + { + _pClassEntries[dwClsent]._hWndDdeServer = hwndDdeServer; + return TRUE; + } + else + { + return FALSE; + } +} + + + +//+------------------------------------------------------------------------- +// +// Member: CDllCache::CleanUpLocalServersForApartment +// +// Synopsis: Clean up any registered local servers for the current apartment +// +// Algorithm: Delete internal objects +// +// History: 02-Feb-94 Ricksa Created +// 07-Mar-95 BruceMa Rewrote +// +//-------------------------------------------------------------------------- +void CDllCache::CleanUpLocalServersForApartment(void) +{ + // Single thread access to the tables + COleStaticLock lck(_mxs); + + // It is possible for the entries to be NULL if CoInitializeEx fails. + if (_pClassEntries) + { + HAPT hApt = GetCurrentApartmentId(); + DWORD dwNext; + + for (DWORD dwClsent = _nClassEntryInUse; dwClsent != NONE; + dwClsent = dwNext) + { + // Get the next entry in case we release this one + dwNext = _pClassEntries[dwClsent]._dwNext; + + // Check whether it's valid and for this apartment + // Only release if it was for a local server. If it is for + // a Dll it will get released later, either when the Dll + // is unloaded or at CoUninitialize + + if (_pClassEntries[dwClsent]._dwSig == CLASS_CACHE_SIG && + _pClassEntries[dwClsent]._hApt == hApt && + _pClassEntries[dwClsent]._dwDllEnt == NONE) + { + // Let the developer know that they're missing a Revoke + CairoleDebugOut((DEB_ERROR, + "Missing revoke on pClassFactory=%lx (%I)\n", + _pClassEntries[dwClsent]._pUnk, + &(_pClassEntries[dwClsent]._clsid))); + + // Release the server + Release(dwClsent); + + // In case dwNext got invalidated + dwNext = _pClassEntries[dwClsent]._dwNext; + + // Release the class entry + FreeClassEntry(dwClsent); + } + } + } +} + +//+------------------------------------------------------------------------- +// +// Member: CDllCache::CleanUpDllsForApartment +// +// Synopsis: Clean up any class information for the current apartment +// +// Algorithm: Delete internal objects +// +// History: 02-Feb-94 Ricksa Created +// 07-Mar-95 BruceMa Rewrote +// +//-------------------------------------------------------------------------- +void CDllCache::CleanUpDllsForApartment(void) +{ + // Release all the DLL objects associated with + // this apartment + + HAPT hApt = GetCurrentApartmentId(); + + // Single thread access to the tables + COleStaticLock lck(_mxs); + + if (_pDllPathEntries) + { + DWORD dwNext; + BOOL fMore; + + for (DWORD dwDllent = _nDllPathEntryInUse; dwDllent != NONE; + dwDllent = dwNext) + { + // Save the next entry in case we delete this one + dwNext = _pDllPathEntries[dwDllent]._dwNext; + + if (IsValidInApartment(dwDllent, hApt)) + { + // Clean up this entry for this apartment + BOOL fRemove = !CleanUpForApartmentByDllent(dwDllent, hApt); + + // In case dwNext got invalidated + dwNext = _pDllPathEntries[dwDllent]._dwNext; + + if (fRemove) + { + HRESULT hr = S_OK; + if (!IsWOWProcess()) + { + _mxs.Release(); + hr = CanUnloadNow(dwDllent); + _mxs.Request(); + } + + if ((hr == S_OK) && (_pDllPathEntries[dwDllent]._nAptInUse == NONE)) + { + // Delete the dll entry if no more apartments are using it + // *and* the Dll says it's OK. + RemoveAndUnload(dwDllent); + FreeDllPathEntry(dwDllent); + } + } + } + } + } +} + + + + +//+------------------------------------------------------------------------- +// +// Member: CDllCache::CleanUpDllsForProcess +// +// Synopsis: Clean up any remaining cache allocations +// +// Algorithm: Delete internal objects +// +// History: 02-Feb-94 Ricksa Created +// 07-Mar-95 BruceMa Rewrote +// +//-------------------------------------------------------------------------- +void CDllCache::CleanUpDllsForProcess(void) +{ + CairoleDebugOut((DEB_TRACE, "Clean up Dll class cache for process\n")); + + // Single thread + COleStaticLock lck(_mxs); + + // In case CoInitialize failed + if (_pDllPathEntries) + { + // Delete the dll path entries. This is explicit because there + // are apartment entries to clean up. (Note - this must be done + // first because there are class entries associated with a dll.) + while (_nDllPathEntryInUse != NONE) + { + DWORD dwApt; + + for(dwApt = _pDllPathEntries[_nDllPathEntryInUse]._nAptInUse; + dwApt != NONE; + dwApt = _pDllPathEntries[_nDllPathEntryInUse]._nAptInUse) + { + CleanUpForApartmentByDllent(_nDllPathEntryInUse, + _pDllPathEntries[_nDllPathEntryInUse]._pAptEntries[dwApt]._hApt ); + } + RemoveAndUnload(_nDllPathEntryInUse); + FreeDllPathEntry(_nDllPathEntryInUse); + } + PrivMemFree(_pDllPathEntries); + _pDllPathEntries = NULL; + } + + _cDllPathEntries = 0; + _nDllPathEntryInUse = NONE; + _nDllPathEntryAvail = NONE; + + + // Now free the class entries. + if (_pClassEntries) + { + // Delete the class entries + PrivMemFree(_pClassEntries); + _pClassEntries = NULL; + } + + _cClassEntries = 0; + _nClassEntryInUse = NONE; + _nClassEntryAvail = NONE; + + CleanupTreatAs(); +} + +//+------------------------------------------------------------------------- +// +// Member: CDllCache::Search +// +// Synopsis: Search for a class entry by clsid and specific context +// and specific apartment +// +// Algorithm: +// +// History: 07-Mar-95 BruceMa Created +// +//-------------------------------------------------------------------------- +DWORD CDllCache::Search(REFCLSID clsid, DWORD dwContext, HAPT hApt) +{ + // Search + for (int k = _nClassEntryInUse; k != NONE; k = _pClassEntries[k]._dwNext) + { + if (IsEqualCLSID(clsid, _pClassEntries[k]._clsid) && + (_pClassEntries[k]._dwContext & dwContext) && + !((dwContext & CLSCTX_INPROC_SERVER) && + (_pClassEntries[k]._dwFlags & REGCLS_SURROGATE)) && + (_pClassEntries[k]._hApt == hApt)) + { + return k; + } + } + + return NONE; +} + + + + + +//+------------------------------------------------------------------------- +// +// Member: CDllCache::Search +// +// Synopsis: Search for a class entry by registration key +// +// Algorithm: +// +// History: 07-Mar-95 BruceMa Created +// +//-------------------------------------------------------------------------- +DWORD CDllCache::Search(DWORD dwRegKey, HAPT hApt) +{ + // Search + for (int k = _nClassEntryInUse; k != NONE; k = _pClassEntries[k]._dwNext) + { + if (dwRegKey == _pClassEntries[k]._dwReg && + hApt == _pClassEntries[k]._hApt) + { + return k; + } + } + + return NONE; +} + + + + + +//+------------------------------------------------------------------------- +// +// Member: CDllCache::SearchForDll +// +// Synopsis: Search for a dll path entry +// +// Algorithm: Upper case and compute a hash. Then search by the hash +// value and by the pathname only if the hash matches. +// +// History: 07-Mar-95 BruceMa Created +// +//-------------------------------------------------------------------------- +DWORD CDllCache::SearchForDll(const TCHAR *ptszDllPath +#ifdef WX86OLE + , BOOL fWx86 +#endif +) +{ + TCHAR tszPath[MAX_PATH]; + LPTSTR ptszPath = tszPath; + DWORD cCh = lstrlen(ptszDllPath); + DWORD dwHash; + BOOL fFreePath = FALSE; +#ifdef WX86OLE + BOOL fWx86Dll; +#endif + + // Compute a hash value for the search path + if (cCh > MAX_PATH - 1) + { + ptszPath = (LPTSTR) PrivMemAlloc((cCh + 1) * sizeof(TCHAR)); + if (ptszPath == NULL) + { + return NONE; + } + fFreePath = TRUE; + } + lstrcpy(ptszPath, ptszDllPath); + CharUpper(ptszPath); + dwHash = Hash(ptszPath); + + // Search + for (int k = _nDllPathEntryInUse; k != NONE; + k = _pDllPathEntries[k]._dwNext) + { +#ifdef WX86OLE + fWx86Dll = _pDllPathEntries[k]._dwFlags & WX86_LOADASX86; +#endif + if (_pDllPathEntries[k]._dwHash == dwHash && + _pDllPathEntries[k]._dwSig == DLL_PATH_CACHE_SIG +#ifdef WX86OLE + // We also must match the dll type (x86 or risc) + && ((fWx86 && fWx86Dll) || (! fWx86 && ! fWx86Dll)) +#endif + ) + { + if (lstrcmp(_pDllPathEntries[k]._ptszPath, ptszPath) == 0) + { + if (fFreePath) + { + PrivMemFree(ptszPath); + } + return k; + } + } + } + + if (fFreePath) + { + PrivMemFree(ptszPath); + } + + return NONE; +} + + + + + +//+------------------------------------------------------------------------- +// +// Member: CDllCache::AllocClassEntry +// +// Synopsis: Allocate a new class entry +// +// Algorithm: +// +// History: 07-Mar-95 BruceMa Created +// +//-------------------------------------------------------------------------- +DWORD CDllCache::AllocClassEntry(void) +{ + // If we don't have any available entries, then expand the array + if (_nClassEntryAvail == NONE) + { + // Allocate a new array + SClassEntry *p = (SClassEntry *) PrivMemAlloc(sizeof(SClassEntry) * + (_cClassEntries + NOMINAL_CACHE_SIZE)); + if (p == NULL) + { + return NONE; + } + + // Initialize it and free the old one. + memcpy(p, _pClassEntries, _cClassEntries * sizeof(SClassEntry)); + + PrivMemFree(_pClassEntries); + _pClassEntries = p; + + for (DWORD k = _cClassEntries; + k < _cClassEntries + NOMINAL_CACHE_SIZE; + k++) + { + InitClsent(k, k == _cClassEntries + NOMINAL_CACHE_SIZE - 1 ? NONE + : k + 1 ); + } + _nClassEntryAvail = _cClassEntries; + _cClassEntries += NOMINAL_CACHE_SIZE; + } + + // Init and return the next available entry + DWORD dwClsent = _nClassEntryAvail; + _pClassEntries[dwClsent]._dwSig = CLASS_CACHE_SIG; + + _nClassEntryAvail = _pClassEntries[dwClsent]._dwNext; + _pClassEntries[dwClsent]._dwNext = _nClassEntryInUse; + Win4Assert((_pClassEntries[dwClsent]._dwNext == NONE || + _pClassEntries[dwClsent]._dwNext < _cClassEntries) + && "Bad class entry index"); + _nClassEntryInUse = dwClsent; + return dwClsent; +} + + + + + +//+------------------------------------------------------------------------- +// +// Member: CDllCache::AllocDllPathEntry +// +// Synopsis: Allocate a new dll path entry +// +// Algorithm: +// +// History: 07-Mar-95 BruceMa Created +// +//-------------------------------------------------------------------------- +DWORD CDllCache::AllocDllPathEntry(void) +{ + // If we don't have any available entries, then expand the array + if (_nDllPathEntryAvail == NONE) + { + // Allocate a new array + SDllPathEntry *p = (SDllPathEntry *) PrivMemAlloc( + sizeof(SDllPathEntry) * (_cDllPathEntries + NOMINAL_CACHE_SIZE)); + if (p == NULL) + { + return NONE; + } + + // Initialize it and free the old one. + memcpy(p, _pDllPathEntries, _cDllPathEntries * sizeof(SDllPathEntry)); + + PrivMemFree(_pDllPathEntries); + _pDllPathEntries = p; + + for (DWORD k = _cDllPathEntries; + k < _cDllPathEntries + NOMINAL_CACHE_SIZE; + k++) + { + InitDllent(k, k == _cDllPathEntries + NOMINAL_CACHE_SIZE - 1 ? NONE + : k + 1 ); + } + _nDllPathEntryAvail = _cDllPathEntries; + _cDllPathEntries += NOMINAL_CACHE_SIZE; + } + + // Init and return the next available entry + DWORD dwDllent = _nDllPathEntryAvail; + _pDllPathEntries[dwDllent]._dwSig = DLL_PATH_CACHE_SIG; + + // Allocate and initialize apartment entries + if (!NewAptEntries(dwDllent)) + { + return NONE; + } + + _nDllPathEntryAvail = _pDllPathEntries[dwDllent]._dwNext; + _pDllPathEntries[dwDllent]._dwNext = _nDllPathEntryInUse; + _nDllPathEntryInUse =dwDllent; + return dwDllent; +} + + + + + +//+------------------------------------------------------------------------- +// +// Member: CDllCache::FreeClassEntry +// +// Synopsis: Free a class entry - i.e., make it available +// +// Algorithm: +// +// History: 07-Mar-95 BruceMa Created +// +//-------------------------------------------------------------------------- +void CDllCache::FreeClassEntry(DWORD dwClsent) +{ + DWORD dwPrevCls; + BOOL fBreak = FALSE; + + // Saftey check + if (_pClassEntries[dwClsent]._dwSig != CLASS_CACHE_SIG) + { + return; + } + + // First check whether it is on a list of class entries for a given dll + // - if so, unthread it + for (DWORD dwDllent = _nDllPathEntryInUse; + dwDllent != NONE; + dwDllent = _pDllPathEntries[dwDllent]._dwNext) + { + for (DWORD dwNextCls = _pDllPathEntries[dwDllent]._dw1stClass; + dwNextCls != NONE; + dwPrevCls = dwNextCls, + dwNextCls = _pClassEntries[dwNextCls]._dwNextDllCls) + { + if (dwNextCls == dwClsent) + { + // It's at the head of the list + if (dwNextCls == _pDllPathEntries[dwDllent]._dw1stClass) + { + _pDllPathEntries[dwDllent]._dw1stClass = + _pClassEntries[dwNextCls]._dwNextDllCls; + } + + // Else it's in the list + else + { + _pClassEntries[dwPrevCls]._dwNextDllCls = + _pClassEntries[dwNextCls]._dwNextDllCls; + } + fBreak = TRUE; + break; + } + } + if (fBreak) + { + break; + } + } + + + // Then remove it from the list of in-use entries + // It's at the head of the list + if (_nClassEntryInUse == dwClsent) + { + _nClassEntryInUse = _pClassEntries[dwClsent]._dwNext; + Win4Assert((_nClassEntryInUse == NONE || + _nClassEntryInUse < _cClassEntries) && "Bad class entry index"); + } + + // Otherwise search for the entry that points to the one we're releasing + else + { + for (DWORD dwPrev = _nClassEntryInUse; + _pClassEntries[dwPrev]._dwNext != dwClsent; + dwPrev = _pClassEntries[dwPrev]._dwNext) + { + } + _pClassEntries[dwPrev]._dwNext = _pClassEntries[dwClsent]._dwNext; + } + + // Relink into the list of available entries + _pClassEntries[dwClsent]._dwNext = _nClassEntryAvail; + _nClassEntryAvail = dwClsent; + InitClsent(dwClsent, _pClassEntries[dwClsent]._dwNext); +} + + + + + +//+------------------------------------------------------------------------- +// +// Member: CDllCache::FreeDllPathEntry +// +// Synopsis: Free a dll path entry - i.e., make it available +// +// Algorithm: +// +// History: 07-Mar-95 BruceMa Created +// +//-------------------------------------------------------------------------- +void CDllCache::FreeDllPathEntry(DWORD dwDllent) +{ + ComDebOut((DEB_TRACE, "FreeDllPathEntry dwDll:%x\n", dwDllent)); + + // It's at the head of the list + if (_nDllPathEntryInUse == dwDllent) + { + _nDllPathEntryInUse = _pDllPathEntries[dwDllent]._dwNext; + } + + // Otherwise search for the entry that points to the one we're releasing + else + { + for (DWORD dwPrev = _nDllPathEntryInUse; + _pDllPathEntries[dwPrev]._dwNext != dwDllent; + dwPrev = _pDllPathEntries[dwPrev]._dwNext) + { + } + _pDllPathEntries[dwPrev]._dwNext = _pDllPathEntries[dwDllent]._dwNext; + } + + // Relink into the list of available entries + _pDllPathEntries[dwDllent]._dwNext = _nDllPathEntryAvail; + _nDllPathEntryAvail = dwDllent; + + // Delete the array of apartment entries + if (_pDllPathEntries[dwDllent]._pAptEntries) + { + delete _pDllPathEntries[dwDllent]._pAptEntries; + } + + // Now reinitialize this dll path entry + InitDllent(dwDllent, _pDllPathEntries[dwDllent]._dwNext); +} + + + + + +//+------------------------------------------------------------------------- +// +// Member: CDllCache::Hash +// +// Synopsis: Compute a hash value for a pathname +// +// Algorithm: +// +// History: 07-Mar-95 BruceMa Created +// +//-------------------------------------------------------------------------- +DWORD CDllCache::Hash(LPTSTR ptszPath) +{ + DWORD dwHash = 0; + + for (DWORD k = 0; ptszPath[k]; k++) + { + dwHash *= 3; + dwHash ^= ptszPath[k]; + } + return dwHash; +} + + + + + +//+------------------------------------------------------------------------- +// +// Function: CleanUpDllsForProcess +// +// Synopsis: Free all cached Dll class object information for this process +// +// Algorithm: Tell class caches to free themselves +// +// History: 02-Feb-94 Ricksa Created +// +//-------------------------------------------------------------------------- +void CleanUpDllsForProcess(void) +{ + // Clean up server cache + gdllcacheInprocSrv.CleanUpDllsForProcess(); + + // Clean up handler cache + gdllcacheHandler.CleanUpDllsForProcess(); +} + +//+------------------------------------------------------------------------- +// +// Function: CleanUpDllsForApartment +// +// Synopsis: Free all cached class object information for this Apartment. +// +// Algorithm: Tell class caches to cleanup the current apartment +// +// History: 26-Jun-94 Rickhi Created +// +//-------------------------------------------------------------------------- +void CleanUpDllsForApartment(void) +{ + // Clean up server cache + gdllcacheInprocSrv.CleanUpDllsForApartment(); + + // Clean up handler cache + gdllcacheHandler.CleanUpDllsForApartment(); +} + +//+------------------------------------------------------------------------- +// +// Function: CleanUpLocalServersForApartment +// +// Synopsis: Free all cached LocalServer class objects for this Apartment. +// +// Algorithm: Tell class caches to cleanup the current apartment +// +// History: 18-Dec-95 Rickhi Created +// +//-------------------------------------------------------------------------- +void CleanUpLocalServersForApartment(void) +{ + // Clean up server cache + gdllcacheInprocSrv.CleanUpLocalServersForApartment(); + + // dont need to call on gdllcacheHandler since local servers are never + // stored in that cache. +} + + + +#ifdef _CHICAGO_ +//+------------------------------------------------------------------------- +// +// Function: GetAptForCLSID +// +// Synopsis: Get the ApartmentId for a given class +// +// Algorithm: search the cache of registrations for an available class +// object of the given class, and return its apartment id. +// +// Returns: HAPT - if found (thread id is member) +// haptNULL - if not +// +// History: 30-Apr-94 JohannP Created +// 06-Oct-95 BruceMa Make for Chicago only +// +//-------------------------------------------------------------------------- +HAPT GetAptForCLSID(const GUID *pclsid) +{ + HAPT hApt; + if (gdllcacheInprocSrv.GetApartmentForCLSID(*pclsid, hApt)) + { + return hApt; + } + else + { + return haptNULL; + } +} +#endif // _CHICAGO_ + + + + +//+--------------------------------------------------------------------------- +// +// Function: GetClassInformationForDde +// +// Synopsis: Get class object information for the Dde server +// +// Effects: +// +// Arguments: [clsid] -- ClassID to search for +// [lpDdeInfo] -- Structure to fill in with information +// +// Requires: +// +// Returns: +// +// Signals: +// +// Modifies: +// +// Algorithm: +// +// History: 5-28-94 kevinro Created +// +// Notes: +// +//---------------------------------------------------------------------------- +BOOL GetClassInformationForDde( REFCLSID clsid, + LPDDECLASSINFO lpDdeInfo) +{ + return gdllcacheInprocSrv.GetClassObjForDde(clsid,lpDdeInfo); +} + + + + + +//+--------------------------------------------------------------------------- +// +// Function: GetClassInformationFromKey +// +// Synopsis: Get class object information for the Dde server using a key +// +// Arguments: [lpDdeInfo] -- Structure to fill in with information +// +// Requires: lpDdeInfo->dwRegistrationKey is the key to the specific +// class being asked for. +// +// Returns: TRUE - Structure filled in with appropriate information +// FALSE - The registration is no longer valid. +// +// Signals: +// +// Modifies: +// +// Algorithm: +// +// History: 5-28-94 kevinro Created +// +// Notes: +// +// See CDllCache::GetClassInformationFromKey +// +//---------------------------------------------------------------------------- +BOOL GetClassInformationFromKey(LPDDECLASSINFO lpDdeInfo) +{ + return gdllcacheInprocSrv.GetClassInformationFromKey(lpDdeInfo); +} + + + + + +//+--------------------------------------------------------------------------- +// +// Function: SetDdeServerWindow +// +// Synopsis: Finds the registration associated with dwKey, and sets the +// HWND for the DDE server. +// +// Effects: See CDllCache::SetDdeServerWindow for details +// +// Arguments: [dwKey] -- Key for the registration +// [hwndDdeServer] -- Window handle to Dde Server +// +// Returns: TRUE if call was successful. +// FALSE if the dwKey was not valid. +// +// History: 7-05-94 kevinro Created +// +// Notes: +// +//---------------------------------------------------------------------------- +BOOL SetDdeServerWindow( DWORD dwKey, HWND hwndDdeServer) +{ + + TRACECALL(TRACE_DLL, "SetDdeServerWindow"); + return gdllcacheInprocSrv.SetDdeServerWindow(dwKey, hwndDdeServer); +} + + +//+--------------------------------------------------------------------------- +// +// Function: OleMainThreadWndProc +// +// Synopsis: Window proc for handling messages to the main thread +// +// Arguments: [hWnd] - window the message is on +// [message] - message the window receives +// [wParam] - first message parameter +// [lParam] - second message parameter. +// +// Returns: Depends on the message +// +// Algorithm: If the message is one a user message that we have defined, +// dispatch it. Otherwise, send any other message to the +// default window proc. +// +// History: 22-Nov-94 Ricksa Created +// +//---------------------------------------------------------------------------- +LRESULT OleMainThreadWndProc( + HWND hWnd, + UINT message, + WPARAM wParam, + LPARAM lParam) +{ + switch(message) + { + case WM_OLE_GETCLASS: + // get the host interface for single-threaded dlls + return GetSingleThreadedHost(lParam); + +#ifdef _CHICAGO_ + case WM_OLE_ORPC_NOTIFY: + // got the initialization message + OleNotificationProc(message, wParam, lParam); + return 0; + +#endif // _CHICAGO_ + +#ifndef _CHICAGO_ + // Check whether it is UninitMainThreadWnd or system shutdown that + // is destroying the window. Only actually do the destroy if it is us. + // + // BUGBUG: Chicago hit this but the debugger was unable to tell + // us who we were called by....so for now i just removed it. + case WM_DESTROY: + case WM_CLOSE: + if (gfDestroyingMainWindow == FALSE) + { + // Not in UninitMainThreadWnd so just ignore this message. Do not + // dispatch it. + ComDebOut((DEB_WARN, "Attempted to destroy Window outside of UninitMainThreadWnd")); + return 0; + } + // else fallthru +#endif // _CHICAGO_ + } + + // We don't process the message so pass it on to the default + // window proc. + return SSDefWindowProc(hWnd, message, wParam, lParam); +} + + + +//+--------------------------------------------------------------------------- +// +// Function: InitMainThreadWnd +// +// Synopsis: Do initialization necessary for main window processing. +// +// Returns: TRUE - we got initialized +// FALSE - initialization failed. +// +// Algorithm: First register out window class. Then create our main thread +// window. Finally, save the id of the main thread. +// +// History: 22-Nov-94 Ricksa Created +// 24-Mar-95 JohannP Added notify mechanismen +// +// Notes: +// +//---------------------------------------------------------------------------- +BOOL InitMainThreadWnd(void) +{ + ComDebOut((DEB_ENDPNT, "InitMainThreadWnd on %x\n", GetCurrentThreadId())); + Win4Assert(IsSTAThread()); + +#ifdef _CHICAGO_ + if (IsWOWProcess()) + { + // Chicago WOW requires a different class per thread. + wsprintfA(ptszOleMainThreadWndClass,"OleMainThreadWndClass %08X", + CoGetCurrentProcess()); + } +#endif // _CHICAGO_ + + BOOL fRetVal = TRUE; + + // Register windows class. + WNDCLASST xClass; + xClass.style = 0; + xClass.lpfnWndProc = OleMainThreadWndProc; + xClass.cbClsExtra = 0; + + // DDE needs some extra space in the window + xClass.cbWndExtra = sizeof(LPVOID) + sizeof(ULONG) + sizeof(HANDLE); + xClass.hInstance = g_hinst; + xClass.hIcon = NULL; + xClass.hCursor = NULL; + xClass.hbrBackground = (HBRUSH) (COLOR_BACKGROUND + 1); + xClass.lpszMenuName = NULL; + xClass.lpszClassName = ptszOleMainThreadWndClass; + + gOleWindowClass = (LPTSTR) RegisterClassT( &xClass ); + if (gOleWindowClass == 0) + { + // it is possible the dll got unloaded without us having called + // unregister so we call it here and try again. + + UnregisterClassT(ptszOleMainThreadWndClass, g_hinst); + gOleWindowClass = (LPTSTR) RegisterClassT(&xClass); + + if (gOleWindowClass == 0) + { + ComDebOut((DEB_ERROR, "RegisterClass failed in InitMainThreadWnd\n")); + fRetVal = FALSE; + } + } + + // Remember the main thread + gdwMainThreadId = GetCurrentThreadId(); + + if (!IsWOWProcess() && fRetVal) + { + // this window is only needed for the non WOW case since + // WOW is not a real apartment case + // Create a main window for this application instance. + + hwndOleMainThread = DllCreateWindowEx( + 0, + gOleWindowClass, + ptszOleMainThreadWndName, + // must use WS_POPUP so the window does not get assigned + // a hot key by user. + (WS_DISABLED | WS_POPUP), + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + NULL, + NULL, + g_hinst, + NULL); + + Win4Assert(hwndOleMainThread && "Register Window on OleWindowClass failed \n"); + if (!hwndOleMainThread) + { + // We did not get a window so we can not report success. + // Cleanup the registered window class and gdwMainThreadId. + UninitMainThreadWnd(); + fRetVal = FALSE; + } + } + + ComDebOut((DEB_ENDPNT, "InitMainThreadWnd done on %x\n", gdwMainThreadId)); + return fRetVal; +} + + + +//+--------------------------------------------------------------------------- +// +// Function: UninitMainThreadWnd +// +// Synopsis: Free resources used by main window processing. +// +// Algorithm: Destroy the window and then unregister the window class. +// +// History: 22-Nov-94 Ricksa Created +// 24-Mar-95 JohannP Added notify mechanismen +// +// Notes: +// +//---------------------------------------------------------------------------- +void UninitMainThreadWnd(void) +{ + ComDebOut((DEB_ENDPNT, "UninitMainThreadWnd on %x\n", gdwMainThreadId)); + Win4Assert(IsSTAThread()); + + if (gdwMainThreadId) + { + BOOL fRet = FALSE; + +#ifdef _CHICAGO_ + // destroy the notification window if it still exist + COleTls tls; + if (tls->hwndOleRpcNotify != NULL) + { + ComDebOut((DEB_ENDPNT,"Destroying NotifyThreadWnd %x\n", tls->hwndOleRpcNotify)); + fRet = SSDestroyWindow(tls->hwndOleRpcNotify); + Win4Assert(fRet && "Destroy Window failed on NotifyThreadWnd\n"); + tls->hwndOleRpcNotify = NULL; + } +#endif // _CHICAGO_ + + // Destroy the window + if (!IsWOWProcess() && IsWindow(hwndOleMainThread)) + { + // flag here is to indicate that we are destroying the window. + // as opposed to the system shutdown closing the window. the + // flag is looked at in dcomrem\chancont\ThreadWndProc. + gfDestroyingMainWindow = TRUE; + SSDestroyWindow(hwndOleMainThread); + gfDestroyingMainWindow = FALSE; + hwndOleMainThread = NULL; + } + + // Unregister the window class + if (UnregisterClassT(ptszOleMainThreadWndClass, g_hinst) == FALSE) + { + ComDebOut((DEB_ERROR,"Unregister Class failed on OleMainThreadWndClass %ws because %d\n", ptszOleMainThreadWndClass, GetLastError())); + } + + gdwMainThreadId = 0; + } + + ComDebOut((DEB_ENDPNT,"UninitMainThreadWnd done on %x\n", gdwMainThreadId)); + return; +} |