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

677 lines
22 KiB
C++

//+-------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1993.
//
// File: dllhost.cxx
//
// Contents: code for activating inproc dlls of one threading model
// from apartments of a different threading model.
//
// History: 04-Mar-96 Rickhi Created
//
// Notes: the basic idea is to call over to an apartment of the
// appropriate type, get it to do the object creation, then
// marshal the object back to the calling apartment.
//
//+-------------------------------------------------------------------------
#include <ole2int.h>
#include <dllhost.hxx>
// globals for the various thread-model hosts
CDllHost gSTHost; // single-threaded host object for STA clients
CDllHost gSTMTHost; // single-threaded host object for MTA clients
CDllHost gATHost; // apartment-threaded host object fo MTA clients
CDllHost gMTHost; // mutli-threaded host object for STA client
ULONG gcHostProcessInits = 0; // count of DLL host threads.
extern void MakeCallableFromAnyApt(OBJREF &objref);
//+-------------------------------------------------------------------
//
// Member: CDllHost::QueryInterface, public
//
// Synopsis: returns supported interfaces
//
// History: 04-Mar-96 Rickhi Created
//
//--------------------------------------------------------------------
STDMETHODIMP CDllHost::QueryInterface(REFIID riid, void **ppv)
{
// only valid to call this from within the host apartment
Win4Assert(_dwHostAptId == GetCurrentApartmentId());
if (IsEqualIID(riid, IID_IDLLHost) ||
IsEqualIID(riid, IID_IUnknown))
{
*ppv = (IDLLHost *) this;
AddRef();
return S_OK;
}
*ppv = NULL;
return E_NOINTERFACE;
}
//+-------------------------------------------------------------------
//
// Member: CDllHost::AddRef, public
//
// Synopsis: we dont refcnt this object so this is a noop
//
// History: 04-Mar-96 Rickhi Created
//
//--------------------------------------------------------------------
ULONG CDllHost::AddRef(void)
{
// only valid to call this from within the host apartment
Win4Assert(_dwHostAptId == GetCurrentApartmentId());
return 1;
}
//+-------------------------------------------------------------------
//
// Member: CDllHost::Release, public
//
// Synopsis: we dont refcnt this object so this is a noop
//
// History: 04-Mar-96 Rickhi Created
//
//--------------------------------------------------------------------
ULONG CDllHost::Release(void)
{
// only valid to call this from within the host apartment
Win4Assert(_dwHostAptId == GetCurrentApartmentId());
return 1;
}
//+-------------------------------------------------------------------------
//
// Function: DoSTClassCreate / DoATClassCreate / DoMTClassCreate
//
// Synopsis: Package up get class object so that happens on the proper
// thread
//
// Arguments: [fnGetClassObject] - DllGetClassObject entry point
// [rclsid] - class id of class object
// [riid] - interface requested
// [ppunk] - where to put output interface.
//
// Returns: NOERROR - Successfully returned interface
// E_OUTOFMEMORY - could not allocate memory buffer.
//
// Algorithm: pass on to the CDllHost object for the correct apartment.
//
// History: 06-Nov-94 Ricksa Created
// 22-Feb-96 KevinRo Changed implementation drastically
// 06-Mar-96 Rickhi Use CDllHost
//
//--------------------------------------------------------------------------
HRESULT DoSTClassCreate(LPFNGETCLASSOBJECT pfnGetClassObject,
REFCLSID rclsid, REFIID riid, IUnknown **ppunk)
{
ComDebOut((DEB_DLL, "DoSTClassCreate rclsid:%I\n", &rclsid));
return gSTHost.GetClassObject(pfnGetClassObject, rclsid, riid, ppunk);
}
HRESULT DoSTMTClassCreate(LPFNGETCLASSOBJECT pfnGetClassObject,
REFCLSID rclsid, REFIID riid, IUnknown **ppunk)
{
ComDebOut((DEB_DLL, "DoSTClassCreate rclsid:%I\n", &rclsid));
return gSTMTHost.GetClassObject(pfnGetClassObject, rclsid, riid, ppunk);
}
HRESULT DoATClassCreate(LPFNGETCLASSOBJECT pfnGetClassObject,
REFCLSID rclsid, REFIID riid, IUnknown **ppunk)
{
ComDebOut((DEB_DLL, "DoATClassCreate rclsid:%I\n", &rclsid));
return gATHost.GetClassObject(pfnGetClassObject, rclsid, riid, ppunk);
}
HRESULT DoMTClassCreate(LPFNGETCLASSOBJECT pfnGetClassObject,
REFCLSID rclsid, REFIID riid, IUnknown **ppunk)
{
ComDebOut((DEB_DLL, "DoMTClassCreate rclsid:%I\n", &rclsid));
return gMTHost.GetClassObject(pfnGetClassObject, rclsid, riid, ppunk);
}
//+-------------------------------------------------------------------------
//
// Member: CDllHost::GetClassObject
//
// Synopsis: called by an apartment to get a class object from
// a host apartment.
//
// History: 04-Mar-96 Rickhi Created
//
//--------------------------------------------------------------------------
HRESULT CDllHost::GetClassObject(LPFNGETCLASSOBJECT pfnGetClassObject,
REFCLSID rclsid, REFIID riid, IUnknown **ppUnk)
{
ComDebOut((DEB_DLL,
"CDllHost::GetClassObject this:%x tid:%x pfn:%x rclsid:%I riid:%I ppUnk:%x\n",
this, GetCurrentThreadId(), pfnGetClassObject, &rclsid, &riid, ppUnk));
HRESULT hr = CO_E_DLLNOTFOUND;
IDLLHost *pIDLLHost = GetHostProxy();
if (pIDLLHost)
{
// call the proxy to get to the correct apartment.
hr = pIDLLHost->DllGetClassObject((DWORD)pfnGetClassObject,
rclsid, riid, ppUnk);
pIDLLHost->Release();
}
ComDebOut((DEB_DLL,
"CDllHost::GetClassObject this:%x hr:%x\n", this, hr));
return hr;
}
//+-------------------------------------------------------------------------
//
// Member: CDllHost::DllGetClassObject
//
// Synopsis: Calls the passed in DllGetClassObject on the current thread.
// Used by an apartment of one threading model to load a DLL
// of another threading model.
//
// History: 04-Mar-96 Rickhi Created
//
//--------------------------------------------------------------------------
STDMETHODIMP CDllHost::DllGetClassObject(DWORD pfnGetClassObject,
REFCLSID rclsid, REFIID riid, IUnknown **ppUnk)
{
ComDebOut((DEB_DLL,
"CDllHost::DllGetClassObject this:%x tid:%x pfn:%x rclsid:%I riid:%I ppUnk:%x\n",
this, GetCurrentThreadId(), pfnGetClassObject, &rclsid, &riid, ppUnk));
// only valid to call this from within the host apartment
Win4Assert(_dwHostAptId == GetCurrentApartmentId());
HRESULT hr = ((LPFNGETCLASSOBJECT)pfnGetClassObject)
(rclsid, riid, (void **) ppUnk);
ComDebOut((DEB_DLL,"CDllHost::DllGetClassObject this:%x hr:%x\n", this, hr));
return hr;
}
//+-------------------------------------------------------------------------
//
// Member: CDllHost::GetHostProxy
//
// Synopsis: returns the host proxy, AddRef'd. Creates it (and
// potentially the host apartment) if it does not yet exist.
//
// History: 04-Mar-96 Rickhi Created
//
//--------------------------------------------------------------------------
IDLLHost *CDllHost::GetHostProxy()
{
// we could be called from any thread, esp one that has not done any
// marshaling/unmarshaling yet, so we need to initialized the channel
// if not already done.
if (FAILED(InitChannelIfNecessary()))
{
return NULL;
}
// prevent two threads from creating the proxy simultaneously.
_mxs.Request();
IDLLHost *pIDH = _pIDllProxy;
if (pIDH == NULL)
{
// proxy does not yet exist, create it (and create the host apartment
// for apartment-threaded and multi-threaded.
DWORD dwType = _dwType;
if (dwType == HDLLF_SINGLETHREADED && gdwMainThreadId == 0)
{
// single threaded DLL and there is no main thread yet, so we
// create an apartment thread that will also act as the main
// thread.
dwType = HDLLF_APARTMENTTHREADED;
}
switch (dwType)
{
case HDLLF_SINGLETHREADED:
// send a message to the single-threaded host apartment (the
// OleMainThread) to marshal the host interface.
SSSendMessage(hwndOleMainThread, WM_OLE_GETCLASS,
WMSG_MAGIC_VALUE, (LPARAM) this);
break;
case HDLLF_MULTITHREADED:
// it is possible that we're comming through here twice if
// the first time through we got an error in the marshal or
// unmarshal, so dont create the event twice.
if (_hEventWakeUp == NULL)
{
_hEventWakeUp = CreateEvent(NULL, FALSE, FALSE, NULL);
if (_hEventWakeUp == NULL)
{
break;
}
}
// fallthru to common code for these two cases.
case HDLLF_APARTMENTTHREADED:
// create a thread to act as the apartment for the dll host. It
// will marshal the host interface and set an event when done.
// it is possible that we're comming through here twice if
// the first time through we got an error in the marshal or
// unmarshal, so dont create the event twice.
if (_hEvent == NULL)
{
_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
}
if (_hEvent)
{
DWORD tid;
HANDLE hThrd = CreateThread(NULL, 0, DLLHostThreadEntry,
(void *)this, 0, &tid);
if (hThrd)
{
WaitForSingleObject(_hEvent, 0xffffffff);
CloseHandle(hThrd);
}
}
// dont try to cleanup if there are errors from the other
// thread, we'll just try again later.
break;
}
// the other thread marshaled the interface pointer into _objref and
// placed the hresult into _hrMarshal. Check it and unmarshal to
// get the host interface proxy.
if (SUCCEEDED(_hrMarshal))
{
Win4Assert(_pIDllProxy == NULL);
Unmarshal();
pIDH = _pIDllProxy;
}
}
if (pIDH)
{
// AddRef the proxy before releasing the lock
pIDH->AddRef();
}
_mxs.Release();
return pIDH;
}
//+-------------------------------------------------------------------------
//
// Function: GetSingleThreadedHost
//
// Synopsis: Get the host interface for single threaded inproc class
// object creation.
//
// Arguments: [param] - pointer to the CDllHost for the single-threaded
// host apartment.
//
// History: 06-Mar-96 Rickhi Created
//
//--------------------------------------------------------------------------
HRESULT GetSingleThreadedHost(LPARAM param)
{
ComDebOut((DEB_DLL,
"GetSingleThreadedHost pDllHost:%x tid:%x\n", param, GetCurrentThreadId()));
// param is the ptr to the CDllHost object. Just tell it to marshal
// it's IDLLHost interface into it's _objref. The _objref will be
// unmarshaled by the calling apartment and the proxy placed in
// _pIDllProxy. Dont need to call Lock because we are already
// guarenteed to be the only thread accessing the state at this time.
CDllHost *pDllHost = (CDllHost *)param;
return pDllHost->GetSingleThreadHost();
}
//+-------------------------------------------------------------------------
//
// Member: CDllHost::GetSingleThreadedHost
//
// Synopsis: Get the host interface for single threaded inproc class
// object creation.
//
// History: 06-Apr-96 Rickhi Created
//
//--------------------------------------------------------------------------
HRESULT CDllHost::GetSingleThreadHost()
{
// set up the TID and apartment id, then marshal the interface
_dwHostAptId = GetCurrentApartmentId();
_dwTid = GetCurrentThreadId();
return Marshal();
}
//+-------------------------------------------------------------------------
//
// Function: DLLHostThreadEntry
//
// Synopsis: Worker thread entry point for AT & MT DLL loading
//
// History 06-Apr-96 Rickhi Created
//
//+-------------------------------------------------------------------------
DWORD _stdcall DLLHostThreadEntry(void *param)
{
ComDebOut((DEB_DLL, "DLLHostThreadEntry Tid:%x\n", GetCurrentThreadId()));
CDllHost *pDllHost = (CDllHost *)param;
HRESULT hr = pDllHost->WorkerThread();
ComDebOut((DEB_DLL, "DLLHostThreadEntry hr:%x\n", hr));
return hr;
}
//+-------------------------------------------------------------------------
//
// Member: WorkerThread
//
// Synopsis: Worker thread for STA and MTA DLL loading. Single threaded
// Dlls are loaded on the main thread (gdwOleMainThread).
//
// History 06-Apr-96 Rickhi Created
//
//+-------------------------------------------------------------------------
HRESULT CDllHost::WorkerThread()
{
ComDebOut((DEB_DLL, "WorkerThread pDllHost:%x\n", this));
Win4Assert(_hEvent);
HRESULT hr = CoInitializeEx(NULL, (_dwType == HDLLF_MULTITHREADED)
? COINIT_MULTITHREADED
: COINIT_APARTMENTTHREADED);
if (SUCCEEDED(hr))
{
// count 1 more host process
InterlockedIncrement((LONG *)&gcHostProcessInits);
// marshal the DllHost interface to pass back to the caller
_dwHostAptId = GetCurrentApartmentId();
_dwTid = GetCurrentThreadId();
hr = Marshal();
if (SUCCEEDED(hr))
{
// wake up the thread that started us
SetEvent(_hEvent);
if (_hEventWakeUp)
{
WaitForSingleObject(_hEventWakeUp, 0xffffffff);
}
else
{
// enter a message loop to process requests.
MSG msg;
while (SSGetMessage(&msg, NULL, 0, 0))
{
SSDispatchMessage(&msg);
}
}
}
// special uninitialize for the host threads that does not take
// the single thread mutex and does not check for process uninits.
COleTls tls;
wCoUninitialize(tls, TRUE);
// count 1 less host process *after* doing the Uninit.
InterlockedDecrement((LONG *)&gcHostProcessInits);
}
// Either CoInit/Marshal failed or we are exiting due to the last
// CoUninitialize by some other thread. Wake up the calling thread.
SetEvent(_hEvent);
ComDebOut((DEB_DLL, "WorkerThread pDllHost:%x hr:%x\n", this, hr));
return hr;
}
//+-------------------------------------------------------------------------
//
// Member: CDllHost::Marshal
//
// Synopsis: marshals IDLLHost interface on this object
//
// History: 04-Mar-96 Rickhi Created
//
//--------------------------------------------------------------------------
HRESULT CDllHost::Marshal()
{
ComDebOut((DEB_DLL,
"CDllHost::Marshal this:%x tid:%x\n", this, GetCurrentThreadId()));
// only valid to call this from inside the host apartment
Win4Assert(_dwHostAptId == GetCurrentApartmentId());
// marshal this objref so another apartment can unmarshal it.
_hrMarshal = MarshalInternalObjRef(_objref, IID_IDLLHost,
(IDLLHost*) this, MSHLFLAGS_NOPING, NULL);
// make the unmarshaled proxy callable from any apartment.
MakeCallableFromAnyApt(_objref);
ComDebOut((DEB_DLL, "CDllHost::Marshal this:%x hr:%x\n", this, _hrMarshal));
return _hrMarshal;
}
//+-------------------------------------------------------------------------
//
// Member: CDllHost::UnMarshal
//
// Synopsis: unmarshals the IDLLHost interface to create a proxy
//
// History: 04-Mar-96 Rickhi Created
//
//--------------------------------------------------------------------------
HRESULT CDllHost::Unmarshal()
{
ComDebOut((DEB_DLL,
"CDllHost::Unmarshal this:%x tid:%x\n", this, GetCurrentThreadId()));
// only valid to call this from outside the host apartment
Win4Assert(_dwHostAptId != GetCurrentApartmentId());
HRESULT hr = _hrMarshal;
if (SUCCEEDED(hr))
{
// unmarshal this objref so it can be used in this apartment
hr = UnmarshalInternalObjRef(_objref, (void **)&_pIDllProxy);
}
ComDebOut((DEB_DLL, "CDllHost::Unmarshal this:%x hr:%x\n", this, hr));
return hr;
}
//+-------------------------------------------------------------------------
//
// Member: CDllHost::Initialize
//
// Synopsis: initializer for Dll host object.
//
// History: 04-Mar-96 Rickhi Created
//
//--------------------------------------------------------------------------
void CDllHost::Initialize(DWORD dwType)
{
ComDebOut((DEB_DLL,"CDllHost::Initialize this:%x type:%x\n", this,dwType));
_dwType = dwType;
_dwHostAptId = 0;
_dwTid = 0;
_hrMarshal = E_UNSPEC; // never been marshaled yet, dont cleanup
_pIDllProxy = NULL;
_hEvent = NULL;
_hEventWakeUp = NULL;
}
//+-------------------------------------------------------------------------
//
// Member: CDllHost::ClientCleanup
//
// Synopsis: client-side cleanup for Dll host object.
//
// History: 04-Apr-96 Rickhi Created
//
//--------------------------------------------------------------------------
void CDllHost::ClientCleanup()
{
ComDebOut((DEB_DLL, "CDllHost::ClientCleanup this:%x AptId:%x\n",
this, GetCurrentApartmentId()));
_mxs.Request();
// the g_mxsSingleThreadOle mutex is already held in order to
// prevent simultaneous use/creation of the proxy while this thread
// is doing the cleanup. Since we own all the code that will run during
// cleanup, we know it is safe to hold the lock for the duration.
if (_pIDllProxy)
{
// proxy exists, release it.
_pIDllProxy->Release();
_pIDllProxy = NULL;
}
if (SUCCEEDED(_hrMarshal) && (_dwTid != GetCurrentThreadId()))
{
// wakeup host thread to tell it to exit, then wait for it
// to complete it's Uninitialization before continuing.
if (_dwType == HDLLF_MULTITHREADED)
{
SetEvent(_hEventWakeUp);
}
else
{
PostThreadMessage(_dwTid, WM_QUIT, 0, 0);
}
// wait for the server threads to uninitialize before continuing.
WaitForSingleObject(_hEvent, 0xffffffff);
}
// Close the event handles.
if (_hEvent)
{
CloseHandle(_hEvent);
_hEvent = NULL;
}
if (_hEventWakeUp)
{
CloseHandle(_hEventWakeUp);
_hEventWakeUp = NULL;
}
_mxs.Release();
}
//+-------------------------------------------------------------------------
//
// Member: CDllHost::ServerCleanup
//
// Synopsis: server-side cleanup for Dll host object.
//
// History: 04-Apr-96 Rickhi Created
//
//--------------------------------------------------------------------------
void CDllHost::ServerCleanup(DWORD dwAptId)
{
// only do cleanup if the apartment id's match
if (_dwHostAptId != dwAptId)
return;
ComDebOut((DEB_DLL, "CDllHost::ServerCleanup this:%x AptId:%x Type:%x\n",
this, GetCurrentApartmentId(), _dwType));
// the _mxs mutex is already held in order to
// prevent simultaneous use/creation of the proxy while this thread
// is doing the cleanup. Since we own all the code that will run during
// cleanup, we know it is safe to hold the lock for the duration.
if (SUCCEEDED(_hrMarshal))
{
// server side, marshal was successful so release via RMD
ReleaseMarshalObjRef(_objref);
FreeObjRef(_objref);
_hrMarshal = E_UNSPEC;
}
ComDebOut((DEB_DLL,
"CDllHost::Cleanup this:%x tid:%x\n", this, GetCurrentThreadId()));
}
//+-------------------------------------------------------------------------
//
// Function: DllHostProcessInitialize
//
// Synopsis: initializes the state for DllHost objects.
//
// History 06-Mar-96 Rickhi Created
//
//+-------------------------------------------------------------------------
void DllHostProcessInitialize()
{
gSTHost.Initialize(HDLLF_SINGLETHREADED);
gSTMTHost.Initialize(HDLLF_SINGLETHREADED);
gATHost.Initialize(HDLLF_APARTMENTTHREADED);
gMTHost.Initialize(HDLLF_MULTITHREADED);
}
//+-------------------------------------------------------------------------
//
// Function: DllHostProcessUninitialize
//
// Synopsis: Cleans up the state for DllHost objects. This is called when
// the process is going to uninitialize. It cleans up the
// client half of the objects, and wakes the worker threads to
// cleanup the server half of the objects.
//
// History 06-Mar-96 Rickhi Created
//
//+-------------------------------------------------------------------------
void DllHostProcessUninitialize()
{
// initiate the cleanup of the STA and MTA host objects
gSTHost.ClientCleanup();
gSTMTHost.ClientCleanup();
gATHost.ClientCleanup();
gMTHost.ClientCleanup();
}
//+-------------------------------------------------------------------------
//
// Function: DllHostThreadUninitialize
//
// Synopsis: Cleans up the server-side state for any DllHost objects
// that happen to live on this thread.
//
// History 06-Mar-96 Rickhi Created
//
//+-------------------------------------------------------------------------
void DllHostThreadUninitialize()
{
// call each DLL host to see if we need to do any per-thread cleanup.
DWORD dwAptId = GetCurrentApartmentId();
gSTHost.ServerCleanup(dwAptId);
gSTMTHost.ServerCleanup(dwAptId);
gATHost.ServerCleanup(dwAptId);
gMTHost.ServerCleanup(dwAptId);
}