NT4/private/ole32/com/objact/dllcache.cxx
2020-09-30 17:12:29 +02:00

4061 lines
126 KiB
C++

//+-------------------------------------------------------------------------
//
// 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;
}