summaryrefslogtreecommitdiffstats
path: root/private/ole32/com/dcomrem/channelb.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'private/ole32/com/dcomrem/channelb.cxx')
-rw-r--r--private/ole32/com/dcomrem/channelb.cxx2665
1 files changed, 2665 insertions, 0 deletions
diff --git a/private/ole32/com/dcomrem/channelb.cxx b/private/ole32/com/dcomrem/channelb.cxx
new file mode 100644
index 000000000..c602b2f40
--- /dev/null
+++ b/private/ole32/com/dcomrem/channelb.cxx
@@ -0,0 +1,2665 @@
+//+---------------------------------------------------------------------
+//
+// Microsoft Windows
+// Copyright (C) Microsoft Corporation, 1993 - 1994.
+//
+// File: d:\nt\private\cairole\com\remote\channelb.cxx
+//
+// Contents: This module contains thunking classes that allow proxies
+// and stubs to use a buffer interface on top of RPC for Cairo
+//
+// Classes: CRpcChannelBuffer
+//
+// Functions:
+// ChannelThreadInitialize
+// ChannelProcessInitialize
+// ChannelRegisterProtseq
+// ChannelThreadUninitialize
+// ChannelProcessUninitialize
+// CRpcChannelBuffer::AddRef
+// CRpcChannelBuffer::AppInvoke
+// CRpcChannelBuffer::CRpcChannelBuffer
+// CRpcChannelBuffer::FreeBuffer
+// CRpcChannelBuffer::GetBuffer
+// CRpcChannelBuffer::QueryInterface
+// CRpcChannelBuffer::Release
+// CRpcChannelBuffer::SendReceive
+// DebugCoSetRpcFault
+// DllDebugObjectRPCHook
+// ThreadInvoke
+// ThreadSendReceive
+//
+// History: 22 Jun 93 AlexMi Created
+// 31 Dec 93 ErikGav Chicago port
+// 15 Mar 94 JohannP Added call category support.
+// 09 Jun 94 BruceMa Get call category from RPC message
+// 19 Jul 94 CraigWi Added support for ASYNC calls
+// 01-Aug-94 BruceMa Memory sift fix
+//
+//----------------------------------------------------------------------
+
+#include <ole2int.h>
+#include <channelb.hxx>
+#include <hash.hxx> // CUUIDHashTable
+#include <riftbl.hxx> // gRIFTbl
+#include <callctrl.hxx> // CAptRpcChnl, AptInvoke
+#include <threads.hxx> // CRpcThreadCache
+#include <service.hxx> // StopListen
+#include <resolver.hxx> // CRpcResolver
+
+extern "C"
+{
+#include "orpc_dbg.h"
+}
+
+#include <rpcdcep.h>
+#include <rpcndr.h>
+
+#include <obase.h>
+#include <ipidtbl.hxx>
+#include <security.hxx>
+#include <chock.hxx>
+
+
+// This is needed for the debug hooks. See orpc_dbg.h
+#pragma code_seg(".orpc")
+
+/***************************************************************************/
+/* Defines. */
+
+#define MAKE_WIN32( status ) \
+ MAKE_SCODE( SEVERITY_ERROR, FACILITY_WIN32, (status) )
+
+// This should just return a status to runtime, but runtime does not
+// support both comm and fault status yet.
+#ifdef _CHICAGO_
+#define RETURN_COMM_STATUS( status ) return (status)
+#else
+#define RETURN_COMM_STATUS( status ) RpcRaiseException( status )
+#endif
+
+// Flags for local rpc header.
+// These are only valid on a request (in a ORPCTHIS header).
+const int ORPCF_INPUT_SYNC = ORPCF_RESERVED1;
+const int ORPCF_ASYNC = ORPCF_RESERVED2;
+
+// These are only valid on a reply (in a ORPCTHAT header).
+const int ORPCF_REJECTED = ORPCF_RESERVED1;
+const int ORPCF_RETRY_LATER = ORPCF_RESERVED2;
+
+// Default size of hash table.
+const int INITIAL_NUM_BUCKETS = 20;
+
+
+/***************************************************************************/
+/* Typedefs. */
+
+// This structure contains a copy of all the information needed to make a
+// call. It is copied so it can be canceled without stray pointer references.
+const DWORD CALLCACHE_SIZE = 8;
+struct working_call : public CChannelCallInfo
+{
+ working_call( CALLCATEGORY callcat,
+ RPCOLEMESSAGE *message,
+ DWORD flags,
+ REFIPID ipidServer,
+ DWORD destctx,
+ CRpcChannelBuffer *channel,
+ DWORD authn_level );
+ void *operator new ( size_t );
+ void operator delete( void * );
+ static void Cleanup ( void );
+ static void Initialize ( void );
+
+ RPCOLEMESSAGE message;
+
+private:
+ static void *list[CALLCACHE_SIZE];
+ static DWORD next;
+};
+
+void *working_call::list[CALLCACHE_SIZE] =
+ { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
+DWORD working_call::next = 0;
+
+
+/***************************************************************************/
+/* Macros. */
+
+// Compute the size needed for the implicit this pointer including the
+// various optional headers.
+inline DWORD SIZENEEDED_ORPCTHIS( BOOL local, DWORD debug_size )
+{
+ if (debug_size == 0)
+ return sizeof(WireThisPart1) + ((local) ? sizeof(LocalThis) : 0);
+ else
+ return sizeof(WireThisPart2) + ((local) ? sizeof(LocalThis) : 0) +
+ debug_size;
+}
+
+inline DWORD SIZENEEDED_ORPCTHAT( DWORD debug_size )
+{
+ if (debug_size == 0)
+ return sizeof(WireThatPart1);
+ else
+ return sizeof(WireThatPart2) + debug_size;
+}
+
+inline CALLCATEGORY GetCallCat( void *header )
+{
+ WireThis *inb = (WireThis *) header;
+ if (inb->c.flags & ORPCF_ASYNC)
+ return CALLCAT_ASYNC;
+ else if (inb->c.flags & ORPCF_INPUT_SYNC)
+ return CALLCAT_INPUTSYNC;
+ else
+ return CALLCAT_SYNCHRONOUS;
+}
+
+
+/***************************************************************************/
+/* Globals. */
+
+// Should the debugger hooks be called?
+BOOL DoDebuggerHooks = FALSE;
+LPORPC_INIT_ARGS DebuggerArg = NULL;
+
+// The extension identifier for debug data.
+const uuid_t DEBUG_EXTENSION =
+{ 0xf1f19680, 0x4d2a, 0x11ce, {0xa6, 0x6a, 0x00, 0x20, 0xaf, 0x6e, 0x72, 0xf4}};
+
+#if DBG == 1
+// strings that prefix the call
+WCHAR *wszDebugORPCCallPrefixString[4] = { L"--> [BEG]", // Invoke
+ L" --> [end]",
+ L"<-- [BEG]", // SendReceive
+ L" <-- [end]" };
+
+LONG ulDebugORPCCallNestingLevel[4] = {1, -1, 1, -1};
+#endif
+
+
+SHashChain OIDBuckets[23] = { {&OIDBuckets[0], &OIDBuckets[0]},
+ {&OIDBuckets[1], &OIDBuckets[1]},
+ {&OIDBuckets[2], &OIDBuckets[2]},
+ {&OIDBuckets[3], &OIDBuckets[3]},
+ {&OIDBuckets[4], &OIDBuckets[4]},
+ {&OIDBuckets[5], &OIDBuckets[5]},
+ {&OIDBuckets[6], &OIDBuckets[6]},
+ {&OIDBuckets[7], &OIDBuckets[7]},
+ {&OIDBuckets[8], &OIDBuckets[8]},
+ {&OIDBuckets[9], &OIDBuckets[9]},
+ {&OIDBuckets[10], &OIDBuckets[10]},
+ {&OIDBuckets[11], &OIDBuckets[11]},
+ {&OIDBuckets[12], &OIDBuckets[12]},
+ {&OIDBuckets[13], &OIDBuckets[13]},
+ {&OIDBuckets[14], &OIDBuckets[14]},
+ {&OIDBuckets[15], &OIDBuckets[15]},
+ {&OIDBuckets[16], &OIDBuckets[16]},
+ {&OIDBuckets[17], &OIDBuckets[17]},
+ {&OIDBuckets[18], &OIDBuckets[18]},
+ {&OIDBuckets[19], &OIDBuckets[19]},
+ {&OIDBuckets[20], &OIDBuckets[20]},
+ {&OIDBuckets[21], &OIDBuckets[21]},
+ {&OIDBuckets[22], &OIDBuckets[22]}
+ };
+
+CUUIDHashTable gClientRegisteredOIDs;
+
+
+// flag whether or not the channel has been initialized for current process
+BOOL gfChannelProcessInitialized = 0;
+BOOL gfMTAChannelInitialized = 0;
+
+// count of multi-threaded apartment inits (see CoInitializeEx)
+extern DWORD g_cMTAInits;
+
+
+// Channel debug hook object.
+CDebugChannelHook gDebugHook;
+
+// Channel error hook object.
+CErrorChannelHook gErrorHook;
+
+#if DBG==1
+//-------------------------------------------------------------------------
+//
+// Function: GetInterfaceName
+//
+// synopsis: Gets the human readable name of an Interface given it's IID.
+//
+// History: 12-Jun-95 Rickhi Created
+//
+//-------------------------------------------------------------------------
+LONG GetInterfaceName(REFIID riid, WCHAR *pwszName)
+{
+ // convert the iid to a string
+ CDbgGuidStr dbgsIID(riid);
+
+ // Read the registry entry for the interface to get the interface name
+ LONG ulcb=256;
+ WCHAR szKey[80];
+
+ szKey[0] = L'\0';
+ lstrcatW(szKey, L"Interface\\");
+ lstrcatW(szKey, dbgsIID._wszGuid);
+
+ LONG result = RegQueryValue(
+ HKEY_CLASSES_ROOT,
+ szKey,
+ pwszName,
+ &ulcb);
+
+ Win4Assert( result == 0 );
+ return result;
+}
+
+//---------------------------------------------------------------------------
+//
+// Function: DebugPrintORPCCall
+//
+// synopsis: Prints the interface name and method number to the debugger
+// to allow simple ORPC call tracing.
+//
+// History: 12-Jun-95 Rickhi Created
+//
+//---------------------------------------------------------------------------
+void DebugPrintORPCCall(DWORD dwFlag, REFIID riid, DWORD iMethod, DWORD Callcat)
+{
+ if (CairoleInfoLevel & DEB_USER15)
+ {
+ Win4Assert (dwFlag < 4);
+
+ // adjust the nesting level for this thread.
+ COleTls tls;
+ tls->cORPCNestingLevel += ulDebugORPCCallNestingLevel[dwFlag];
+
+
+ // set the indentation string according to the nesting level
+ CHAR szNesting[100];
+ memset(szNesting, 0x20, 100);
+
+ if (tls->cORPCNestingLevel > 99) // watch for overflow
+ szNesting[99] = '\0';
+ else
+ szNesting[tls->cORPCNestingLevel] = '\0';
+
+
+ // construct the debug strings
+ WCHAR *pwszDirection = wszDebugORPCCallPrefixString[dwFlag];
+ WCHAR wszName[100];
+ GetInterfaceName(riid, wszName);
+
+ ComDebOut((DEB_USER15, "%s%ws [%x] %ws:: %x\n",
+ szNesting, pwszDirection, Callcat, wszName, iMethod));
+ }
+}
+#endif
+
+/***************************************************************************/
+void ByteSwapThis( DWORD drep, WireThis *inb )
+{
+ if ((drep & NDR_LOCAL_DATA_REPRESENTATION) != NDR_LITTLE_ENDIAN)
+ {
+ // Extensions are swapped later. If we ever use the reserved field,
+ // swap it.
+ ByteSwapShort( inb->c.version.MajorVersion );
+ ByteSwapShort( inb->c.version.MinorVersion );
+ ByteSwapLong( inb->c.flags );
+ // ByteSwapLong( inb->c.reserved1 );
+ ByteSwapLong( inb->c.cid.Data1 );
+ ByteSwapShort( inb->c.cid.Data2 );
+ ByteSwapShort( inb->c.cid.Data3 );
+ }
+}
+
+/***************************************************************************/
+void ByteSwapThat( DWORD drep, WireThat *outb )
+{
+ if ((drep & NDR_LOCAL_DATA_REPRESENTATION) != NDR_LITTLE_ENDIAN)
+ {
+ // Extensions are swapped later.
+ ByteSwapLong( outb->c.flags );
+ }
+}
+
+//+-------------------------------------------------------------------
+//
+// Function: ChannelProcessInitialize, public
+//
+// Synopsis: Initializes the channel subsystem per process data.
+//
+// History: 23-Nov-93 AlexMit Created
+//
+//--------------------------------------------------------------------
+STDAPI ChannelProcessInitialize()
+{
+ TRACECALL(TRACE_RPC, "ChannelProcessInitialize");
+ ComDebOut((DEB_COMPOBJ, "ChannelProcessInitialize [IN]\n"));
+
+ Win4Assert( (sizeof(WireThisPart1) & 7) == 0 );
+ Win4Assert( (sizeof(WireThisPart2) & 7) == 0 );
+ Win4Assert( (sizeof(LocalThis) & 7) == 0 );
+ Win4Assert( (sizeof(WireThatPart1) & 7) == 0 );
+ Win4Assert( (sizeof(WireThatPart2) & 7) == 0 );
+
+ // we want to take the gComLock since that prevents other Rpc
+ // threads from accessing anything we are about to create, in
+ // particular, the event cache and working_call cache are accessed
+ // by Rpc worker threads of cancelled calls.
+
+ ASSERT_LOCK_RELEASED
+ LOCK
+
+ HRESULT hr = S_OK;
+
+ if (!gfChannelProcessInitialized)
+ {
+ // Initialize the interface hash tables, the OID hash table, and
+ // the MID hash table. We dont need to cleanup these on errors.
+
+ gMIDTbl.Initialize();
+ gOXIDTbl.Initialize();
+ gRIFTbl.Initialize();
+ gIPIDTbl.Initialize();
+ gSRFTbl.Initialize();
+ gClientRegisteredOIDs.Initialize(OIDBuckets);
+
+ // Register the debug channel hook.
+ hr = CoRegisterChannelHook( DEBUG_EXTENSION, &gDebugHook );
+
+ // Register the error channel hook.
+ if(SUCCEEDED(hr))
+ {
+ hr = CoRegisterChannelHook( ERROR_EXTENSION, &gErrorHook );
+ }
+
+ // Initialize security.
+ if (SUCCEEDED(hr))
+ {
+ hr = InitializeSecurity();
+ }
+
+ // always set to TRUE if we initialized ANYTHING, regardless of
+ // whether there were any errors. That way, ChannelProcessUninit
+ // will cleanup anything we have initialized.
+ gfChannelProcessInitialized = TRUE;
+ }
+
+ UNLOCK
+ ASSERT_LOCK_RELEASED
+
+ if (FAILED(hr))
+ {
+ // cleanup anything we have created thus far.
+ ChannelProcessUninitialize();
+ }
+
+ ComDebOut((DEB_COMPOBJ, "ChannelProcessInitialize [OUT] hr:%x\n", hr));
+ return hr;
+}
+
+//+-------------------------------------------------------------------
+//
+// Function: CleanupRegOIDs, public
+//
+// Synopsis: called to delete each node of the registered OID list.
+//
+//+-------------------------------------------------------------------
+void CleanupRegOIDs(SHashChain *pNode)
+{
+ delete pNode;
+}
+
+//+-------------------------------------------------------------------
+//
+// Function: ChannelProcessUninitialize, public
+//
+// Synopsis: Uninitializes the channel subsystem global data.
+//
+// History: 23-Nov-93 Rickhi Created
+//
+// Notes: This is called at process uninitialize, not thread
+// uninitialize.
+//
+//--------------------------------------------------------------------
+STDAPI_(void) ChannelProcessUninitialize(void)
+{
+ TRACECALL(TRACE_RPC, "ChannelProcessUninitialize");
+ ComDebOut((DEB_COMPOBJ, "ChannelProcessUninitialize [IN]\n"));
+
+ if (gfChannelProcessInitialized)
+ {
+ // Stop accepting calls from the object resolver and flag that service
+ // is no longer initialized. This can result in calls being
+ // dispatched. Do not hold the lock around this call.
+
+ UnregisterDcomInterfaces();
+ }
+
+ gResolver.ReleaseSCMProxy();
+
+ // we want to take the gComLock since that prevents other Rpc
+ // threads from accessing anything we are about to cleanup, in
+ // particular, the event cache and working_call are accessed by
+ // Rpc worker threaded for cancelled calls.
+ ASSERT_LOCK_RELEASED
+ LOCK
+
+ if (gfChannelProcessInitialized)
+ {
+ // Release the interface tables. This causes RPC to stop dispatching
+ // DCOM calls. This can result in calls being dispatched.
+ // UnRegisterServerInterface releases and reaquires the lock each
+ // time it is called.
+ gRIFTbl.Cleanup();
+
+ if (gpLocalMIDEntry)
+ {
+ // release the local MIDEntry
+ DecMIDRefCnt(gpLocalMIDEntry);
+ gpLocalMIDEntry = NULL;
+ }
+
+ // release the MTA apartment's OXIDEntry if there is one. Do this
+ // after the RIFTble cleanup so we are not processing any calls
+ // while it happens.
+ gOXIDTbl.ReleaseLocalMTAEntry();
+
+ if (gpsaCurrentProcess)
+ {
+ // delete the string bindings for the current process
+ PrivMemFree(gpsaCurrentProcess);
+ gpsaCurrentProcess = NULL;
+ }
+
+ // cleanup the IPID, OXID, and MID tables
+ gOXIDTbl.FreeExpiredEntries(GetTickCount()+1);
+ gIPIDTbl.Cleanup();
+ gOXIDTbl.Cleanup();
+ gMIDTbl.Cleanup();
+ gSRFTbl.Cleanup();
+
+ // Cleanup the OID registration table.
+ gClientRegisteredOIDs.Cleanup(CleanupRegOIDs);
+
+ // Cleanup the call cache.
+ working_call::Cleanup();
+
+ // Release all cached threads.
+ gRpcThreadCache.ClearFreeList();
+
+ // cleanup the event cache
+ gEventCache.Cleanup();
+
+ // Cleanup the channel hooks.
+ CleanupChannelHooks();
+ }
+
+ // Always cleanup the RPC OXID resolver since security may initialize it.
+ gResolver.Cleanup();
+
+ // Cleanup security.
+ UninitializeSecurity();
+
+ // mark the channel as no longer intialized for this process
+ gfChannelProcessInitialized = FALSE;
+ gfMTAChannelInitialized = FALSE;
+
+ UNLOCK
+ ASSERT_LOCK_RELEASED
+
+ // release the static unmarshaler
+ if (gpStdMarshal)
+ {
+ ((CStdIdentity *)gpStdMarshal)->UnlockAndRelease();
+ gpStdMarshal = NULL;
+ }
+
+ ComDebOut((DEB_COMPOBJ, "ChannelProcessUninitialize [OUT]\n"));
+ return;
+}
+
+//+-------------------------------------------------------------------
+//
+// Function: STAChannelInitialize, public
+//
+// Synopsis: Initializes the channel subsystem per thread data
+// for single-threaded apartments.
+//
+// History: 23-Nov-93 Rickhi Created
+//
+// Notes: This is called at thread initialize, not process
+// initialize. Cleanup is done in ChannelThreadUninitialize.
+//
+//--------------------------------------------------------------------
+STDAPI STAChannelInitialize(void)
+{
+ ComDebOut((DEB_COMPOBJ, "STAChannelInitialize [IN]\n"));
+ Win4Assert(IsSTAThread());
+
+ HRESULT hr = S_OK;
+
+ if (!gfChannelProcessInitialized)
+ {
+ // process initialization has not been done, do that now.
+ if (FAILED(hr = ChannelProcessInitialize()))
+ return hr;
+ }
+
+ // create the callctrl before calling ThreadStart, since the latter
+ // tries to register with the call controller. We might already have
+ // a callctrl if some DDE stuff has already run.
+
+ COleTls tls;
+
+ if (tls->pCallCtrl == NULL)
+ {
+ // assume OOM and try to create callctrl. ctor sets tls.
+ hr = E_OUTOFMEMORY;
+ CAptCallCtrl *pCallCtrl = new CAptCallCtrl();
+ }
+
+ if (tls->pCallCtrl)
+ {
+ // mark the channel as initialized now to prevent re-entracy in
+ // GetLocalEntry.
+
+ tls->dwFlags |= OLETLS_CHANNELTHREADINITIALZED;
+
+ // Precreate the thread window. The window is normally only used
+ // for requests (and thus created during marshalling). But in WOW
+ // it is used for responses (and thus created during initialization).
+ // We do it for normal cases here too in order to avoid recursion
+ // when marshaling the first interface.
+
+ ASSERT_LOCK_RELEASED
+ LOCK
+
+ OXIDEntry *pOxid;
+ hr = gOXIDTbl.GetLocalEntry( &pOxid );
+
+ UNLOCK
+ ASSERT_LOCK_RELEASED
+
+ if (SUCCEEDED(hr))
+ {
+ hr = ThreadStart();
+ }
+
+ // Clear the channel initialized flag if initialization fails.
+ // Everything gets cleaned up in uninitialize regardless of the
+ // channel flag.
+ if (FAILED(hr))
+ tls->dwFlags &= ~OLETLS_CHANNELTHREADINITIALZED;
+ }
+
+ ComDebOut((DEB_COMPOBJ, "STAChannelInitialize [OUT] hr:%x\n", hr));
+ return hr;
+}
+
+//+-------------------------------------------------------------------
+//
+// Function: MTAChannelInitialize, public
+//
+// Synopsis: Initializes the channel subsystem per thread data
+// for multi-threaded apartments.
+//
+// History: 19-Mar-96 Rickhi Created
+//
+// Notes: This is called at thread initialize, not process
+// initialize. Cleanup is done in ChannelThreadUninitialize.
+//
+//--------------------------------------------------------------------
+STDAPI MTAChannelInitialize(void)
+{
+ ComDebOut((DEB_COMPOBJ, "MTAChannelInitialize [IN]\n"));
+ Win4Assert(IsMTAThread());
+
+ HRESULT hr = S_OK;
+
+ if (!gfChannelProcessInitialized)
+ {
+ // process initialization has not been done, do that now.
+ if (FAILED(hr = ChannelProcessInitialize()))
+ return hr;
+ }
+
+ ASSERT_LOCK_RELEASED
+ LOCK
+
+ if (!gfMTAChannelInitialized)
+ {
+ // Create the OXID entry for this apartment. Do it now to avoid
+ // any races with two threads creating it simultaneously.
+
+ OXIDEntry *pOxid;
+ hr = gOXIDTbl.GetLocalEntry( &pOxid );
+ if (SUCCEEDED(hr))
+ {
+ pOxid->dwFlags &= ~ OXIDF_STOPPED;
+ gfMTAChannelInitialized = TRUE;
+ }
+
+ }
+
+ UNLOCK
+ ASSERT_LOCK_RELEASED
+
+ ComDebOut((DEB_COMPOBJ, "MTAChannelInitialize [OUT] hr:%x\n", hr));
+ return hr;
+}
+
+//+-------------------------------------------------------------------
+//
+// Function: ChannelThreadUninitialize, private
+//
+// Synopsis: Uninitializes the channel subsystem per thread data.
+//
+// History: 23-Nov-93 Rickhi Created
+//
+// Notes: This is called at thread uninitialize, not process
+// uninitialize.
+//
+//--------------------------------------------------------------------
+STDAPI_(void) ChannelThreadUninitialize(void)
+{
+ TRACECALL(TRACE_RPC, "ChannelThreadUninitialize");
+ ComDebOut((DEB_COMPOBJ, "ChannelThreadUninitialize [IN]\n"));
+
+ COleTls tls;
+
+ if (tls->dwFlags & OLETLS_APARTMENTTHREADED)
+ {
+ // Cleanup the window for this thread.
+ ThreadCleanup();
+
+ // Free the apartment call control.
+ delete tls->pCallCtrl;
+ tls->pCallCtrl = NULL;
+
+ // Free any registered MessageFilter that has not been picked
+ // up by the call ctrl.
+ if (tls->pMsgFilter)
+ {
+ tls->pMsgFilter->Release();
+ tls->pMsgFilter = NULL;
+ }
+
+ // release the OXIDEntry for this thread.
+ ASSERT_LOCK_RELEASED
+ LOCK
+
+ gOXIDTbl.ReleaseLocalSTAEntry();
+
+ UNLOCK
+ ASSERT_LOCK_RELEASED
+
+ // mark the thread as no longer intialized for the channel
+ tls->dwFlags &= ~OLETLS_CHANNELTHREADINITIALZED;
+ }
+ else
+ {
+ // the MTA channel is no longer initialized.
+ gfMTAChannelInitialized = FALSE;
+ }
+
+ ComDebOut((DEB_COMPOBJ, "ChannelThreadUninitialize [OUT]\n"));
+}
+
+// count of multi-threaded inits
+//+-------------------------------------------------------------------
+//
+// Function: InitChannelIfNecessary, private
+//
+// Synopsis: Checks if the ORPC channel has been initialized for
+// the current apartment and initializes if not. This is
+// required by the delayed initialization logic.
+//
+// History: 26-Oct-95 Rickhi Created
+//
+//--------------------------------------------------------------------
+INTERNAL InitChannelIfNecessary()
+{
+ HRESULT hr;
+ COleTls tls(hr);
+
+ if (FAILED(hr))
+ return hr;
+
+ if (!(tls->dwFlags & OLETLS_APARTMENTTHREADED))
+ {
+ if (!gfMTAChannelInitialized)
+ {
+ if (g_cMTAInits > 0)
+ {
+ // initialize the MTAChannel
+ return MTAChannelInitialize();
+ }
+
+ // CoInitializeEx(MULTITHREADED) has not been called
+ return CO_E_NOTINITIALIZED;
+ }
+ }
+ else if (!(tls->dwFlags & OLETLS_CHANNELTHREADINITIALZED))
+ {
+ if (tls->cComInits > 0 &&
+ !(tls->dwFlags & OLETLS_THREADUNINITIALIZING))
+ return STAChannelInitialize();
+
+ // CoInitializeEx(APARTMENTTHREADED) has not been called,
+ // or the thread is Uninitializing
+ return CO_E_NOTINITIALIZED;
+ }
+
+ return S_OK;
+}
+
+
+ /***************************************************************************/
+/*
+ Make a copy of a message for an asyncronous call. Also make a fake
+reply for the call.
+*/
+CChannelCallInfo *MakeAsyncCopy( CChannelCallInfo *original )
+{
+ void *pBuffer = NULL;
+ WireThat *outb;
+ BOOL success;
+
+ ASSERT_LOCK_HELD
+
+ working_call *copy = new working_call( original->category,
+ original->pmessage,
+ original->iFlags,
+ original->ipid,
+ original->iDestCtx,
+ original->pChannel,
+ original->lAuthnLevel );
+
+ if (copy != NULL)
+ {
+ original->hResult = S_OK;
+
+ if (original->pmessage->rpcFlags & RPCFLG_LOCAL_CALL)
+ {
+ // no need to duplicate the buffer, just use it as is.
+ copy->message.Buffer = original->pmessage->Buffer;
+ }
+ else
+ {
+ pBuffer = PrivMemAlloc8(original->pmessage->cbBuffer);
+ Win4Assert(((ULONG)pBuffer & 0x7) == 0 && "Buffer not aligned properly");
+
+ if (pBuffer != NULL)
+ {
+ copy->message.Buffer = pBuffer;
+ memcpy(pBuffer, original->pmessage->Buffer,
+ original->pmessage->cbBuffer);
+ }
+ else
+ {
+ original->hResult = RPC_E_OUT_OF_RESOURCES;
+ }
+ }
+
+ if (SUCCEEDED(original->hResult))
+ {
+ // pretend local so we don't touch rpc for more buffers, etc.
+ copy->message.rpcFlags |= RPCFLG_LOCAL_CALL;
+
+ // Create a fake reply containing a result even though the
+ // client will never see it.
+ original->pmessage->cbBuffer = SIZENEEDED_ORPCTHAT(0) + 4;
+
+ if (original->pmessage->rpcFlags & RPCFLG_LOCAL_CALL)
+ {
+ original->pmessage->Buffer = PrivMemAlloc8(original->pmessage->cbBuffer);
+ success = original->pmessage->Buffer != NULL;
+ }
+ else
+ {
+ success = I_RpcGetBuffer((RPC_MESSAGE *) original->pmessage) == RPC_S_OK;
+ }
+
+ // simulate success in method call
+ if (success)
+ {
+ outb = (WireThat *) original->pmessage->Buffer;
+ outb->c.flags = ORPCF_NULL;
+ outb->c.unique = 0;
+ *(SCODE *)((WireThatPart1 *)outb + 1) = S_OK;
+ return copy;
+ }
+ }
+
+ PrivMemFree(pBuffer);
+ delete copy;
+ }
+
+ return NULL;
+}
+
+
+/***************************************************************************/
+STDMETHODIMP_(ULONG) CRpcChannelBuffer::AddRef( THIS )
+{
+ // can't call AssertValid(FALSE) since it is used in asserts
+ InterlockedIncrement( (long *) &ref_count );
+ return ref_count;
+}
+
+/***************************************************************************/
+HRESULT CRpcChannelBuffer::AppInvoke( CChannelCallInfo *call,
+ IRpcStubBuffer *stub,
+ void *pv,
+ void *orig_stub_buffer,
+ LocalThis *localb )
+{
+ ASSERT_LOCK_RELEASED
+
+ RPC_MESSAGE *message = (RPC_MESSAGE *) call->pmessage;
+ void *orig_buffer = message->Buffer;
+ WireThat *outb = NULL;
+ HRESULT result;
+
+ // Save a pointer to the inbound header.
+ call->pHeader = message->Buffer;
+
+ // Adjust the buffer.
+ message->BufferLength -= (char *) orig_stub_buffer - (char *) message->Buffer;
+ message->Buffer = orig_stub_buffer;
+ message->ProcNum &= ~RPC_FLAGS_VALID_BIT;
+
+ // if the incoming call is from a non-NDR client, then set a bit in
+ // the message flags field so the stub can figure out how to dispatch
+ // the call. This allows a 32bit server to simultaneously service a
+ // 32bit client using NDR and a 16bit client using non-NDR, in particular,
+ // to support OLE Automation.
+ if (localb != NULL && localb->flags & LOCALF_NONNDR)
+ message->RpcFlags |= RPCFLG_NON_NDR;
+
+ if (IsMTAThread())
+ {
+ // do multi-threaded apartment invoke
+ result = MTAInvoke((RPCOLEMESSAGE *)message, GetCallCat( call->pHeader ),
+ stub, this, &call->server_fault);
+ }
+ else
+ {
+ // do single-threaded apartment invoke
+ result = STAInvoke((RPCOLEMESSAGE *)message, GetCallCat( call->pHeader ),
+ stub, this, pv, &call->server_fault);
+ }
+
+ // For local calls, just free the in buffer. For non-local calls,
+ // the RPC runtime does this for us.
+ if (message->RpcFlags & RPCFLG_LOCAL_CALL)
+ PrivMemFree( orig_buffer );
+
+ // If an exception occurred before a new buffer was allocated,
+ // set the Buffer field to point to the original buffer.
+ if (message->Buffer == orig_stub_buffer)
+ {
+ // The buffer pointer in the message must be correct so RPC can free it.
+ if (message->RpcFlags & RPCFLG_LOCAL_CALL)
+ message->Buffer = NULL;
+ else
+ message->Buffer = orig_buffer;
+ }
+ else if (message->Buffer != NULL)
+ {
+ // An out buffer exists, get the pointer to the channel header.
+ Win4Assert( call->pHeader != orig_buffer );
+ message->BufferLength += (char *) message->Buffer - (char *) call->pHeader;
+ message->Buffer = call->pHeader;
+ outb = (WireThat *) message->Buffer;
+ }
+
+ // If successful, adjust the buffer.
+ if (result == S_OK)
+ {
+ if (call->iDestCtx == MSHCTX_DIFFERENTMACHINE)
+ outb->c.flags = 0;
+ else
+ outb->c.flags = ORPCF_LOCAL;
+
+ // For asynchronous calls, MSWMSG will delete the out buffer. If MSWMSG
+ // is not the transport, delete it here. Non-Mswmsg async calls have
+ // been converted to local calls.
+ if (message->RpcFlags & RPCFLG_LOCAL_CALL &&
+ call->category == CALLCAT_ASYNC)
+ {
+ PrivMemFree( message->Buffer );
+ message->Buffer = NULL;
+ }
+ }
+ else if (result == RPC_E_CALL_REJECTED)
+ {
+ // Call was rejected. If the caller is on another machine, just fail the
+ // call.
+ if (call->iDestCtx != MSHCTX_DIFFERENTMACHINE && outb != NULL)
+ {
+ // Otherwise return S_OK so the buffer gets back, but set the flag
+ // to indicate it was rejected.
+ outb->c.flags = ORPCF_LOCAL | ORPCF_REJECTED;
+ result = S_OK;
+ }
+ }
+ else if (result == RPC_E_SERVERCALL_RETRYLATER)
+ {
+ // Call was rejected. If the caller is on another machine, just fail the
+ // call.
+ if (call->iDestCtx != MSHCTX_DIFFERENTMACHINE && outb != NULL)
+ {
+ // Otherwise return S_OK so the buffer gets back, but set the flag
+ // to indicate it was rejected with retry later.
+ outb->c.flags = ORPCF_LOCAL | ORPCF_RETRY_LATER;
+ result = S_OK;
+ }
+ }
+ else if (message->RpcFlags & RPCFLG_LOCAL_CALL)
+ {
+ // call failed and the call is local, free the out buffer. For
+ // non-local calls the RPC runtime does this for us.
+ PrivMemFree( message->Buffer );
+ message->Buffer = NULL;
+ }
+
+ ASSERT_LOCK_RELEASED
+ return result;
+}
+
+//+---------------------------------------------------------------------------
+//
+// Function: AppInvokeExceptionFilter
+//
+// Synopsis: Determine if the application as thrown an exception we want
+// to report. If it has, then print out enough information for
+// the 'user' to debug the problem
+//
+// Arguments: [lpep] -- Exception context records
+//
+// History: 6-20-95 kevinro Created
+//
+// Notes:
+//
+// At the moment, I was unable to get this to work for Win95, so I have
+// commented out the code.
+//
+//----------------------------------------------------------------------------
+
+
+#ifdef _CHICAGO_
+
+//
+// Win95 doesn't appear to support this functionality by default.
+//
+
+inline LONG
+AppInvokeExceptionFilter(
+ LPEXCEPTION_POINTERS lpep
+ )
+{
+ return(EXCEPTION_EXECUTE_HANDLER);
+}
+
+#else
+
+#include <imagehlp.h>
+
+#define SYM_HANDLE GetCurrentProcess()
+
+#if defined(_M_IX86)
+#define MACHINE_TYPE IMAGE_FILE_MACHINE_I386
+#elif defined(_M_MRX000)
+#define MACHINE_TYPE IMAGE_FILE_MACHINE_R4000
+#elif defined(_M_ALPHA)
+#define MACHINE_TYPE IMAGE_FILE_MACHINE_ALPHA
+#elif defined(_M_PPC)
+#define MACHINE_TYPE IMAGE_FILE_MACHINE_POWERPC
+#else
+#error( "unknown target machine" );
+#endif
+
+LONG
+AppInvokeExceptionFilter(
+ LPEXCEPTION_POINTERS lpep
+ )
+{
+#if DBG == 1
+ BOOL rVal;
+ STACKFRAME StackFrame;
+ CONTEXT Context;
+
+
+ SymSetOptions( SYMOPT_UNDNAME );
+ SymInitialize( SYM_HANDLE, NULL, TRUE );
+ ZeroMemory( &StackFrame, sizeof(StackFrame) );
+ Context = *lpep->ContextRecord;
+
+#if defined(_M_IX86)
+ StackFrame.AddrPC.Offset = Context.Eip;
+ StackFrame.AddrPC.Mode = AddrModeFlat;
+ StackFrame.AddrFrame.Offset = Context.Ebp;
+ StackFrame.AddrFrame.Mode = AddrModeFlat;
+ StackFrame.AddrStack.Offset = Context.Esp;
+ StackFrame.AddrStack.Mode = AddrModeFlat;
+#endif
+
+
+ ComDebOut((DEB_FORCE,"An Exception occurred while calling into app\n"));
+ ComDebOut((DEB_FORCE,
+ "Exception address = 0x%x Exception number 0x%x\n",
+ lpep->ExceptionRecord->ExceptionAddress,
+ lpep->ExceptionRecord->ExceptionCode ));
+
+ ComDebOut((DEB_FORCE,"The following stack trace is where the exception occured\n"));
+ ComDebOut((DEB_FORCE,"Frame RetAddr mod!symbol\n"));
+ do
+ {
+ rVal = StackWalk(MACHINE_TYPE,SYM_HANDLE,0,&StackFrame,&Context,ReadProcessMemory,
+ SymFunctionTableAccess,SymGetModuleBase,NULL);
+
+ if (rVal)
+ {
+ DWORD dump[200];
+ ULONG Displacement;
+ PIMAGEHLP_SYMBOL sym = (PIMAGEHLP_SYMBOL) &dump;
+ IMAGEHLP_MODULE ModuleInfo;
+ LPSTR pModuleName = "???";
+ BOOL fSuccess;
+
+ sym->SizeOfStruct = sizeof(dump);
+
+ fSuccess = SymGetSymFromAddr(SYM_HANDLE,StackFrame.AddrPC.Offset,
+ &Displacement,sym);
+
+ //
+ // If there is module name information available, then grab it.
+ //
+ if(SymGetModuleInfo(SYM_HANDLE,StackFrame.AddrPC.Offset,&ModuleInfo))
+ {
+ pModuleName = ModuleInfo.ModuleName;
+ }
+
+ if (fSuccess)
+ {
+ ComDebOut((DEB_FORCE,
+ "%08x %08x %s!%s + %x\n",
+ StackFrame.AddrFrame.Offset,
+ StackFrame.AddrReturn.Offset,
+ pModuleName,
+ sym->Name,
+ Displacement));
+ }
+ else
+ {
+ ComDebOut((DEB_FORCE,
+ "%08x %08x %s!%08x\n",
+ StackFrame.AddrFrame.Offset,
+ StackFrame.AddrReturn.Offset,
+ pModuleName,
+ StackFrame.AddrPC.Offset));
+ }
+ }
+ } while( rVal );
+
+ SymCleanup( SYM_HANDLE );
+
+#endif
+
+ return EXCEPTION_EXECUTE_HANDLER;
+}
+#endif // _CHICAGO_
+
+/***************************************************************************/
+#if DBG == 1
+DWORD AppInvoke_break = 0;
+DWORD AppInvoke_count = 0;
+#endif
+
+HRESULT StubInvoke(RPCOLEMESSAGE *pMsg, IRpcStubBuffer *pStub,
+ IRpcChannelBuffer *pChnl, DWORD *pdwFault)
+{
+ ComDebOut((DEB_CHANNEL, "StubInvoke pMsg:%x pStub:%x pChnl:%x pdwFault:%x\n",
+ pMsg, pStub, pChnl, pdwFault));
+ ASSERT_LOCK_RELEASED
+
+ HRESULT hr;
+
+#if DBG==1
+ DWORD dwMethod = pMsg->iMethod;
+ IID iidBeingCalled = ((RPC_SERVER_INTERFACE *) pMsg->reserved2[1])->InterfaceId.SyntaxGUID;
+#endif
+
+ _try
+ {
+ TRACECALL(TRACE_RPC, "StubInvoke");
+#if DBG == 1
+ //
+ // On a debug build, we are able to break on a call by serial number.
+ // This isn't really 100% thread safe, but is still extremely useful
+ // when debugging a problem.
+ //
+ DWORD dwBreakCount = ++AppInvoke_count;
+
+ ComDebOut((DEB_CHANNEL, "AppInvoke(0x%x) calling method 0x%x iid %I\n",
+ dwBreakCount,dwMethod, &iidBeingCalled));
+
+ if(AppInvoke_break == dwBreakCount)
+ {
+ DebugBreak();
+ }
+#endif
+
+#ifdef WX86OLE
+ if (! gcwx86.IsN2XProxy(pStub))
+ {
+ IUnknown *pActual;
+
+ hr = pStub->DebugServerQueryInterface((void **)&pActual);
+ if (SUCCEEDED(hr))
+ {
+ if (gcwx86.IsN2XProxy(pActual))
+ {
+ // If we are going to invoke a native stub that is
+ // connected to an object on the x86 side then
+ // set a flag in the Wx86 thread environment to
+ // let the thunk layer know that the call is a
+ // stub invoked call and allow any in or out
+ // custom interface pointers to be thunked as
+ // IUnknown rather than failing the interface thunking
+ gcwx86.SetStubInvokeFlag((BOOL)1);
+ }
+ pStub->DebugServerRelease(pActual);
+ }
+ }
+#endif
+
+ hr = pStub->Invoke(pMsg, pChnl);
+
+ }
+ _except(AppInvokeExceptionFilter( GetExceptionInformation()))
+ {
+ hr = RPC_E_SERVERFAULT;
+ *pdwFault = GetExceptionCode();
+
+#if DBG == 1
+ //
+ // OLE catches exceptions when the server generates them. This is so we can
+ // cleanup properly, and allow the client to continue.
+ //
+ if (*pdwFault == STATUS_ACCESS_VIOLATION ||
+ *pdwFault == STATUS_POSSIBLE_DEADLOCK ||
+ *pdwFault == STATUS_INSTRUCTION_MISALIGNMENT ||
+ *pdwFault == STATUS_DATATYPE_MISALIGNMENT )
+ {
+
+ WCHAR iidName[256];
+ iidName[0] = 0;
+ char achProgname[256];
+ achProgname[0] = 0;
+
+ GetModuleFileNameA(NULL,achProgname,sizeof(achProgname));
+
+ GetInterfaceName(iidBeingCalled,iidName);
+
+ ComDebOut((DEB_FORCE,
+ "OLE has caught a fault 0x%08x on behalf of the server %s\n",
+ *pdwFault,
+ achProgname));
+
+ ComDebOut((DEB_FORCE,
+ "The fault occured when OLE called the interface %I (%ws) method 0x%x\n",
+ &iidBeingCalled,iidName,dwMethod));
+
+ Win4Assert(!"The server application has faulted processing an inbound RPC request. Check the kernel debugger for useful output. OLE can continue but you probably want to stop and debug the application.");
+ }
+#endif
+ }
+
+ ASSERT_LOCK_RELEASED
+ ComDebOut((DEB_CHANNEL, "StubInvoke hr:%x dwFault:%x\n", hr, *pdwFault));
+ return hr;
+}
+
+/***************************************************************************/
+#if DBG==1
+LONG ComInvokeExceptionFilter( DWORD lCode,
+ LPEXCEPTION_POINTERS lpep )
+{
+ ComDebOut((DEB_ERROR, "Exception 0x%x in ComInvoke at address 0x%x\n",
+ lCode, lpep->ExceptionRecord->ExceptionAddress));
+ DebugBreak();
+ return EXCEPTION_EXECUTE_HANDLER;
+}
+#endif
+
+/***************************************************************************/
+HRESULT ComInvoke( CChannelCallInfo *call )
+{
+ TRACECALL(TRACE_RPC, "ComInvoke");
+ ASSERT_LOCK_RELEASED
+
+ RPC_MESSAGE *message = (RPC_MESSAGE *) call->pmessage;
+ LocalThis *localb;
+ void *saved_buffer;
+ RPC_STATUS status;
+ HRESULT result;
+ IPIDEntry *ipid_entry = NULL;
+ CRpcChannelBuffer *server_channel = NULL;
+ DWORD TIDCallerSaved;
+ BOOL fLocalSaved;
+ UUID saved_threadid;
+ IUnknown *save_context;
+ DWORD saved_authn_level;
+ char *stub_data;
+ WireThis *inb = (WireThis *) message->Buffer;
+ OXIDEntry *oxid;
+ HANDLE hWakeup = NULL;
+
+ ComDebOut((DEB_CHANNEL, "ComInvoke callinfo:%x header:%x\n",
+ call, message->Buffer));
+
+ COleTls tls(result);
+ if (FAILED(result))
+ return result;
+
+ // Catch exceptions that might keep the lock.
+#if DBG == 1
+ _try
+ {
+#endif
+
+ // Find the IPID entry. Fail if the IPID or the OXID are not ready.
+ LOCK
+ ipid_entry = gIPIDTbl.LookupIPID( call->ipid );
+ Win4Assert( ipid_entry == NULL || ipid_entry->pOXIDEntry != NULL );
+ if (ipid_entry == NULL || (ipid_entry->dwFlags & IPIDF_DISCONNECTED) ||
+ (ipid_entry->pOXIDEntry->dwFlags & OXIDF_STOPPED) ||
+ ipid_entry->pChnl == NULL)
+ result = RPC_E_DISCONNECTED;
+ else if (ipid_entry->pStub == NULL)
+ result = E_NOINTERFACE;
+
+ // Keep the server object and our associated objects alive during the call.
+ if (SUCCEEDED(result))
+ {
+ oxid = ipid_entry->pOXIDEntry;
+ server_channel = ipid_entry->pChnl;
+ Win4Assert( server_channel != NULL && server_channel->pStdId != NULL );
+ server_channel->pStdId->LockServer();
+ InterlockedIncrement( &oxid->cCalls );
+ }
+ UNLOCK
+ ASSERT_LOCK_RELEASED
+
+ if (FAILED(result))
+ {
+ return result;
+ }
+
+ // Create a new security call context;
+ CServerSecurity security( call );
+ save_context = tls->pCallContext;
+ tls->pCallContext = &security;
+
+ // save the original threadid & copy in the new one.
+ if (!(tls->dwFlags & OLETLS_UUIDINITIALIZED))
+ {
+ UuidCreate(&tls->LogicalThreadId);
+ tls->dwFlags |= OLETLS_UUIDINITIALIZED;
+ }
+ saved_threadid = tls->LogicalThreadId;
+ tls->LogicalThreadId = inb->c.cid;
+
+ ComDebOut((DEB_CALLCONT, "ComInvoke: LogicalThreads Old:%I New:%I\n",
+ &saved_threadid, &tls->LogicalThreadId));
+
+ // Save the call info in TLS.
+ call->pNext = (CChannelCallInfo *) tls->pCallInfo;
+ tls->pCallInfo = call;
+ saved_authn_level = tls->dwAuthnLevel;
+ tls->dwAuthnLevel = call->lAuthnLevel;
+
+ // Call the channel hooks. Set up as much TLS data as possible before
+ // calling the hooks so they can access it.
+ result = ServerNotify(
+ ((RPC_SERVER_INTERFACE *) message->RpcInterfaceInformation)->InterfaceId.SyntaxGUID,
+ (WireThis *) message->Buffer,
+ message->BufferLength,
+ (void **) &stub_data,
+ message->DataRepresentation );
+
+ // Find the local header.
+ if (inb->c.flags & ORPCF_LOCAL)
+ {
+ localb = (LocalThis *) stub_data;
+ stub_data += sizeof(LocalThis);
+ }
+ else
+ localb = NULL;
+
+ // Set the caller TID. This is needed by some interop code in order
+ // to do focus management via tying queues together. We first save the
+ // current one so we can restore later to deal with nested calls
+ // correctly.
+ TIDCallerSaved = tls->dwTIDCaller;
+ fLocalSaved = tls->dwFlags & OLETLS_LOCALTID;
+ tls->dwTIDCaller = localb != NULL ? localb->client_thread : 0;
+
+ if (call->iFlags & CF_PROCESS_LOCAL)
+ tls->dwFlags |= OLETLS_LOCALTID; // turn the local bit on
+ else
+ tls->dwFlags &= ~OLETLS_LOCALTID; // turn the local bit off
+
+ // Continue dispatching the call.
+ if (result == S_OK)
+ {
+ result = server_channel->AppInvoke(
+ call,
+ (IRpcStubBuffer *) ipid_entry->pStub,
+ ipid_entry->pv,
+ stub_data,
+ localb );
+ }
+
+ // Restore the original thread id, call info, dest context and thread id.
+ tls->LogicalThreadId = saved_threadid;
+ tls->pCallInfo = call->pNext;
+ tls->dwTIDCaller = TIDCallerSaved;
+ tls->dwAuthnLevel = saved_authn_level;
+
+ if (fLocalSaved)
+ tls->dwFlags |= OLETLS_LOCALTID;
+ else
+ tls->dwFlags &= ~OLETLS_LOCALTID;
+
+ // Restore the security context;
+ tls->pCallContext = save_context;
+ security.EndCall();
+
+ // Decrement the call count. If the MTA is waiting to uninitialize
+ // and this is the last call, wake up the uninitializing thread, but
+ // do this *after* calling UnLockServer so the other thread does not
+ // blow away the server.
+ if (InterlockedDecrement( &oxid->cCalls ) == 0 &&
+ (oxid->dwFlags & (OXIDF_MTASERVER | OXIDF_STOPPED)) == (OXIDF_MTASERVER | OXIDF_STOPPED))
+ hWakeup = oxid->hComplete;
+
+ // Release our hold on the object and channel.
+ server_channel->pStdId->UnLockServer();
+
+ if (hWakeup)
+ SetEvent(hWakeup);
+
+ // Catch exceptions that might keep the lock.
+#if DBG == 1
+ }
+ _except( ComInvokeExceptionFilter(GetExceptionCode(),
+ GetExceptionInformation()) )
+ {
+ }
+#endif
+
+ ASSERT_LOCK_RELEASED
+ return result;
+}
+
+/***************************************************************************/
+CRpcChannelBuffer *CRpcChannelBuffer::Copy(OXIDEntry *pOXIDEntry,
+ REFIPID ripid, REFIID riid)
+{
+ Win4Assert( !(state & server_cs) );
+
+ CRpcChannelBuffer *chan;
+
+ if (IsMTAThread())
+ {
+ // make client side multi-threaded apartment version of channel
+ chan = new CMTARpcChnl(pStdId, pOXIDEntry, state);
+ }
+ else
+ {
+ // make client side single-threaded apartment version of channel
+ chan = new CAptRpcChnl(pStdId, pOXIDEntry, state);
+ }
+
+ if (chan != NULL)
+ {
+ chan->state = proxy_cs | (state & ~client_cs);
+ chan->lAuthnLevel = lAuthnLevel;
+ }
+
+ return chan;
+}
+
+/***************************************************************************/
+HRESULT CRpcChannelBuffer::InitClientSideHandle()
+{
+ Win4Assert((state & proxy_cs));
+ ASSERT_LOCK_HELD
+
+ if (state & initialized_cs)
+ return S_OK;
+
+ // Lookup the interface info. This cant fail.
+ pInterfaceInfo = gRIFTbl.GetClientInterfaceInfo(pIPIDEntry->iid);
+
+ RPC_STATUS status;
+#ifndef _CHICAGO_
+ if (state & process_local_cs)
+ {
+ handle = NULL;
+ status = RPC_S_OK;
+ }
+ else
+#endif
+ {
+ status = RpcBindingCopy(pOXIDEntry->hServerSTA, &handle);
+
+ if (status == RPC_S_OK)
+
+ // If this is a single threaded apartment, give LRPC the blocking
+ // hook.
+ if (state & mswmsg_cs)
+ status = I_RpcBindingSetAsync(handle, OleModalLoopBlockFn);
+
+#ifndef CHICAGO
+ // If the server is a single threaded apartment, tell LRPC to
+ // use MSWMSG to dispatch.
+ else if (pOXIDEntry->dwTid != 0)
+ status = I_RpcBindingSetAsync(handle, NULL);
+#endif
+
+ if (status == RPC_S_OK)
+ status = RpcBindingSetObject(handle, (GUID *)&pIPIDEntry->ipid);
+ }
+
+ if (status == RPC_S_OK)
+ {
+ state |= initialized_cs;
+ return S_OK;
+ }
+
+ return MAKE_WIN32(status);
+}
+
+
+/***************************************************************************/
+CRpcChannelBuffer::CRpcChannelBuffer(CStdIdentity *standard_identity,
+ OXIDEntry *pOXID,
+ DWORD eState )
+{
+ ComDebOut((DEB_MARSHAL, "CRpcChannelBuffer %s Created this:%x pOXID:%x\n",
+ (eState & client_cs) ? "CLIENT" : "SERVER", this, pOXID));
+
+ // Fill in the easy fields first.
+ ref_count = 1;
+ pStdId = standard_identity;
+ handle = NULL;
+ pOXIDEntry = pOXID;
+ pIPIDEntry = NULL;
+ pInterfaceInfo = NULL;
+ hToken = NULL;
+ lAuthnLevel = gAuthnLevel;
+ state = eState;
+ state |= pOXID->dwPid == GetCurrentProcessId() ? process_local_cs : 0;
+ SetImpLevel( gImpLevel );
+
+ if ((pOXID->dwFlags & OXIDF_MSWMSG) && IsSTAThread())
+ {
+ // use MSWMSG protocol with the blocking hook
+ state |= mswmsg_cs;
+ }
+
+ if (state & (client_cs | proxy_cs))
+ {
+ // Determine the destination context.
+ if (pOXID->dwFlags & OXIDF_MACHINE_LOCAL)
+ if (!IsWOWThread() && (state & process_local_cs))
+ iDestCtx = MSHCTX_INPROC;
+ else
+ iDestCtx = MSHCTX_LOCAL;
+ else
+ iDestCtx = MSHCTX_DIFFERENTMACHINE;
+ }
+ else
+ {
+ // On the server side, the destination context isn't known
+ // untill a call arrives.
+ iDestCtx = 0;
+ }
+}
+
+/***************************************************************************/
+CRpcChannelBuffer::~CRpcChannelBuffer()
+{
+ ComDebOut((DEB_MARSHAL, "CRpcChannelBuffer %s Deleted this:%x\n",
+ (state & server_cs) ? "SERVER" : "CLIENT", this));
+
+ if (handle != NULL)
+ RpcBindingFree( &handle );
+ if (hToken != NULL)
+ CloseHandle( hToken );
+}
+
+/***************************************************************************/
+STDMETHODIMP CRpcChannelBuffer::FreeBuffer( RPCOLEMESSAGE *pMessage )
+{
+ TRACECALL(TRACE_RPC, "CRpcChannelBuffer::FreeBuffer");
+ ASSERT_LOCK_RELEASED
+ AssertValid(FALSE, TRUE);
+
+ if (pMessage->Buffer == NULL)
+ return S_OK;
+
+ // Pop the call stack.
+ COleTls tls;
+ Win4Assert( tls->pCallInfo != NULL );
+ working_call *pCall = (working_call *) tls->pCallInfo;
+ tls->pCallInfo = pCall->pNext;
+ tls->dwAuthnLevel = pCall->lSavedAuthnLevel;
+ pMessage->Buffer = pCall->pHeader;;
+
+ DeallocateBuffer(pCall->pmessage);
+
+ // Resume any outstanding impersonation.
+ ResumeImpersonate( tls->pCallContext, pCall->iFlags & CF_WAS_IMPERSONATING );
+
+ // Release the AddRef we did earlier. Note that we cant do this until
+ // after DeallocateBuffer since it may release a binding handle that
+ // I_RpcFreeBuffer needs.
+ if (pCall->Locked())
+ pStdId->UnLockClient();
+
+ pMessage->Buffer = NULL;
+ delete pCall;
+
+ ASSERT_LOCK_RELEASED
+ return S_OK;
+}
+
+//-------------------------------------------------------------------------
+//
+// Member: CRpcChannelBuffer::GetBuffer
+//
+// Synopsis: Calls ClientGetBuffer or ServerGetBuffer
+//
+//-------------------------------------------------------------------------
+STDMETHODIMP CRpcChannelBuffer::GetBuffer( RPCOLEMESSAGE *pMessage,
+ REFIID riid )
+{
+ gOXIDTbl.ValidateOXID();
+ if (state & proxy_cs)
+ return ClientGetBuffer( pMessage, riid );
+ else
+ return ServerGetBuffer( pMessage, riid );
+}
+
+//-------------------------------------------------------------------------
+//
+// Member: CRpcChannelBuffer::ClientGetBuffer
+//
+// Synopsis: Gets a buffer and sets up client side stuff
+//
+//-------------------------------------------------------------------------
+HRESULT CRpcChannelBuffer::ClientGetBuffer( RPCOLEMESSAGE *pMessage,
+ REFIID riid )
+{
+ TRACECALL(TRACE_RPC, "CRpcChannelBuffer::ClientGetBuffer");
+ ASSERT_LOCK_RELEASED
+
+ RPC_STATUS status;
+ CALLCATEGORY callcat = CALLCAT_SYNCHRONOUS;
+ ULONG debug_size;
+ ULONG num_extent;
+ WireThis *inb;
+ LocalThis *localb;
+ IID *logical_thread;
+ working_call *call;
+ DWORD flags;
+ BOOL resume;
+ DWORD orig_size = pMessage->cbBuffer;
+ COleTls tls;
+
+ Win4Assert(state & proxy_cs);
+ AssertValid(FALSE, TRUE);
+
+ // Don't allow remote calls if DCOM is disabled.
+ if (gDisableDCOM && iDestCtx == MSHCTX_DIFFERENTMACHINE)
+ return RPC_E_REMOTE_DISABLED;
+
+ // Fetch the call category from the RPC message structure
+ if (pMessage->rpcFlags & RPCFLG_ASYNCHRONOUS)
+ {
+ // only allow async for these two interfaces for now
+ if (riid != IID_IAdviseSink && riid != IID_IAdviseSink2)
+ return E_UNEXPECTED;
+ callcat = CALLCAT_ASYNC;
+ }
+ else
+ {
+ logical_thread = TLSGetLogicalThread();
+ if (logical_thread == NULL)
+ {
+ return RPC_E_OUT_OF_RESOURCES;
+ }
+ if (pMessage->rpcFlags & RPCFLG_INPUT_SYNCHRONOUS)
+ {
+ callcat = CALLCAT_INPUTSYNC;
+ }
+ }
+
+ // Set the buffer complete flag for local calls.
+ pMessage->rpcFlags |= RPC_BUFFER_COMPLETE;
+
+ // Note - RPC requires that the 16th bit of the proc num be set because
+ // we use the rpcFlags field of the RPC_MESSAGE struct.
+ pMessage->iMethod |= RPC_FLAGS_VALID_BIT;
+
+ // if service object of destination is in same process, definitely local
+ // calls; async calls are also forced to be local.
+ if (state & process_local_cs)
+ {
+ pMessage->rpcFlags |= RPCFLG_LOCAL_CALL;
+ flags = CF_PROCESS_LOCAL;
+ }
+ else
+ flags = 0;
+
+ // Find out if we need hook data.
+ debug_size = ClientGetSize( riid, &num_extent );
+
+ LOCK
+
+ // Complete the channel initialization if needed.
+ status = InitClientSideHandle();
+ if (status != RPC_S_OK)
+ {
+ UNLOCK;
+ ASSERT_LOCK_RELEASED;
+ return status;
+ }
+
+ // Fill in the binding handle. Adjust the size. Clear the transfer
+ // syntax. Set the interface identifier.
+ if ((pMessage->rpcFlags & RPCFLG_LOCAL_CALL) == 0)
+ pMessage->reserved1 = handle;
+ pMessage->cbBuffer += SIZENEEDED_ORPCTHIS( pOXIDEntry->dwFlags & OXIDF_MACHINE_LOCAL,
+ debug_size );
+ pMessage->reserved2[0] = 0;
+ pMessage->reserved2[1] = pInterfaceInfo;
+ Win4Assert( pMessage->reserved2[1] != NULL );
+
+ // Allocate a call record.
+ call = new working_call( callcat, pMessage, flags, pIPIDEntry->ipid,
+ iDestCtx, this, lAuthnLevel );
+ pMessage->cbBuffer = orig_size;
+ UNLOCK
+ ASSERT_LOCK_RELEASED
+
+ if (call == NULL)
+ return E_OUTOFMEMORY;
+
+ // Suspend any outstanding impersonation and ignore failures.
+ SuspendImpersonate( tls->pCallContext, &resume );
+
+ // Get a buffer.
+ if (call->pmessage->rpcFlags & RPCFLG_LOCAL_CALL)
+ {
+ // NDR_DREP_ASCII | NDR_DREP_LITTLE_ENDIAN | NDR_DREP_IEEE
+ call->pmessage->dataRepresentation = 0x00 | 0x10 | 0x0000;
+ call->pmessage->Buffer = PrivMemAlloc8( call->pmessage->cbBuffer );
+ if (call->pmessage->Buffer == NULL)
+ status = RPC_S_OUT_OF_MEMORY;
+ else
+ status = RPC_S_OK;
+ }
+ else
+ {
+ TRACECALL(TRACE_RPC, "I_RpcGetBuffer");
+ status = I_RpcGetBuffer( (RPC_MESSAGE *) call->pmessage );
+ }
+
+ if (status != RPC_S_OK)
+ {
+ // Resume any outstanding impersonation.
+ ResumeImpersonate( tls->pCallContext, resume );
+
+ // Cleanup.
+ pMessage->cbBuffer = 0;
+ tls->fault = MAKE_WIN32( status );
+ delete call;
+ return MAKE_WIN32( status );
+ }
+
+ // Save the impersonation flag.
+ if (resume)
+ call->iFlags |= CF_WAS_IMPERSONATING;
+
+ // Chain the call info in TLS.
+ call->pNext = (CChannelCallInfo *)tls->pCallInfo;
+ tls->pCallInfo = call;
+ call->pHeader = call->message.Buffer;
+
+ // Adjust the authentication level in TLS.
+ call->lSavedAuthnLevel = tls->dwAuthnLevel;
+ tls->dwAuthnLevel = lAuthnLevel;
+
+ // Fill in the COM header.
+ pMessage->Buffer = call->message.Buffer;
+ inb = (WireThis *) pMessage->Buffer;
+ inb->c.version.MajorVersion = COM_MAJOR_VERSION;
+ inb->c.version.MinorVersion = COM_MINOR_VERSION;
+ inb->c.reserved1 = 0;
+
+ // Generate a new logical thread for async calls.
+ if (callcat == CALLCAT_ASYNC)
+ UuidCreate( &inb->c.cid );
+ // Find the logical thread id.
+ else
+ inb->c.cid = *logical_thread;
+
+ if (pOXIDEntry->dwFlags & OXIDF_MACHINE_LOCAL)
+ inb->c.flags = ORPCF_LOCAL;
+ else
+ inb->c.flags = ORPCF_NULL;
+
+ // Fill in any hook data and adjust the buffer pointer.
+ if (debug_size != 0)
+ {
+ pMessage->Buffer = FillBuffer( riid, &inb->d.ea, debug_size, num_extent,
+ TRUE );
+ inb->c.unique = 0x77646853; // Any non-zero value.
+ }
+ else
+ {
+ pMessage->Buffer = (void *) &inb->d.ea;
+ inb->c.unique = FALSE;
+ }
+
+ // Fill in the local header.
+ if (pOXIDEntry->dwFlags & OXIDF_MACHINE_LOCAL)
+ {
+ localb = (LocalThis *) pMessage->Buffer;
+ localb->client_thread = GetCurrentApartmentId();
+ localb->flags = 0;
+ pMessage->Buffer = localb + 1;
+ if (callcat == CALLCAT_ASYNC)
+ inb->c.flags |= ORPCF_ASYNC;
+ else if (callcat == CALLCAT_INPUTSYNC)
+ inb->c.flags |= ORPCF_INPUT_SYNC;
+
+ // if the caller is using a non-NDR proxy, set a bit in the local
+ // header flags so that server side stub knows which way to unmarshal
+ // the parameters. This lets a 32bit server simultaneously service calls
+ // from 16bit non-NDR clients and 32bit NDR clients, in particular, to
+ // support OLE Automation.
+
+ if (pIPIDEntry->dwFlags & (IPIDF_NONNDRPROXY | IPIDF_NONNDRSTUB))
+ localb->flags |= LOCALF_NONNDR;
+ }
+
+ ComDebOut((DEB_CALLCONT, "ClientGetBuffer: LogicalThreadId:%I\n",
+ &(tls->LogicalThreadId)));
+
+ ASSERT_LOCK_RELEASED
+ return S_OK;
+}
+
+//-------------------------------------------------------------------------
+//
+// Member: CRpcChannelBuffer::ServerGetBuffer
+//
+// Synopsis: Gets a buffer and sets up server side stuff
+//
+//-------------------------------------------------------------------------
+HRESULT CRpcChannelBuffer::ServerGetBuffer( RPCOLEMESSAGE *pMessage,
+ REFIID riid )
+{
+ TRACECALL(TRACE_RPC, "CRpcChannelBuffer::ServerGetBuffer");
+ ASSERT_LOCK_RELEASED
+
+ RPC_STATUS status;
+ ULONG debug_size;
+ ULONG num_extent;
+ HRESULT result = S_OK;
+ WireThis *inb;
+ WireThat *outb;
+ CChannelCallInfo *call;
+ void *stub_data;
+ DWORD orig_size = pMessage->cbBuffer;
+
+ Win4Assert( state & server_cs );
+
+ AssertValid(FALSE, TRUE);
+
+ // Get the call info from TLS.
+ COleTls tls;
+ call = (CChannelCallInfo *) tls->pCallInfo;
+ Win4Assert( call != NULL );
+
+ // Find out if we need debug data.
+ pMessage->Buffer = call->pHeader;
+ debug_size = ServerGetSize( riid, &num_extent );
+
+ // Adjust the buffer size.
+ pMessage->cbBuffer += SIZENEEDED_ORPCTHAT( debug_size );
+
+ // Get a buffer.
+ if (pMessage->rpcFlags & RPCFLG_LOCAL_CALL)
+ {
+ // NDR_DREP_ASCII | NDR_DREP_LITTLE_ENDIAN | NDR_DREP_IEEE
+ pMessage->dataRepresentation = 0x00 | 0x10 | 0x0000;
+ pMessage->Buffer = PrivMemAlloc8( pMessage->cbBuffer );
+ if (pMessage->Buffer == NULL)
+ status = RPC_S_OUT_OF_MEMORY;
+ else
+ status = RPC_S_OK;
+ }
+ else
+ {
+ TRACECALL(TRACE_RPC, "I_RpcGetBuffer");
+ status = I_RpcGetBuffer( (RPC_MESSAGE *) pMessage );
+ Win4Assert( call->pHeader != pMessage->Buffer || status != RPC_S_OK );
+ }
+
+ if (status != RPC_S_OK)
+ {
+ pMessage->cbBuffer = 0;
+ pMessage->Buffer = NULL;
+ tls->fault = MAKE_WIN32( status );
+ return MAKE_WIN32( status );
+ }
+
+ // Fill in the outbound COM header.
+ call->pHeader = pMessage->Buffer;
+ outb = (WireThat *) pMessage->Buffer;
+ outb->c.flags = ORPCF_NULL;
+ pMessage->cbBuffer = orig_size;
+ if (debug_size != 0)
+ {
+ stub_data = FillBuffer( riid, &outb->d.ea, debug_size, num_extent, FALSE );
+ outb->c.unique = 0x77646853; // Any non-zero value.
+ pMessage->Buffer = stub_data;
+ }
+ else
+ {
+ outb->c.unique = 0;
+ pMessage->Buffer = &outb->d.ea;
+ }
+
+ ComDebOut((DEB_CALLCONT, "ServerGetBuffer: LogicalThreadId:%I\n",
+ &(tls->LogicalThreadId)));
+ ASSERT_LOCK_RELEASED
+ return S_OK;
+}
+
+/***************************************************************************/
+STDMETHODIMP CRpcChannelBuffer::GetDestCtx( DWORD FAR* lpdwDestCtx,
+ LPVOID FAR* lplpvDestCtx )
+{
+ TRACECALL(TRACE_RPC, "CRpcChannelBuffer::GetDestCtx");
+ AssertValid(FALSE, FALSE);
+
+ // On the client side, get the destination context from the channel.
+ if (state & (client_cs | proxy_cs))
+ {
+ *lpdwDestCtx = iDestCtx;
+ }
+
+ // On the server side, get the destination context from TLS.
+ else
+ {
+ COleTls tls;
+ Win4Assert( tls->pCallInfo != NULL );
+ *lpdwDestCtx = ((CChannelCallInfo *) tls->pCallInfo)->iDestCtx;
+ }
+
+ if (lplpvDestCtx != NULL)
+ *lplpvDestCtx = NULL;
+
+ return S_OK;
+}
+
+/***************************************************************************/
+STDMETHODIMP CRpcChannelBuffer::IsConnected( THIS )
+{
+ // must be on right thread because it is only called by proxies and stubs.
+ AssertValid(FALSE, TRUE);
+
+ // Server channels never know if they are connected. The only time the
+ // client side knows it is disconnected is after the standard identity
+ // has disconnected the proxy from the channel. In that case it doesn't
+ // matter.
+ return S_OK;
+}
+
+/***************************************************************************/
+STDMETHODIMP CRpcChannelBuffer::QueryInterface( THIS_ REFIID riid, LPVOID FAR* ppvObj)
+{
+ AssertValid(FALSE, FALSE);
+
+ // IMarshal is queried more frequently than any other interface, so
+ // check for that first.
+
+ if (IsEqualIID(riid, IID_IMarshal))
+ {
+ *ppvObj = (IMarshal *) this;
+ }
+ else if (IsEqualIID(riid, IID_IUnknown) ||
+ IsEqualIID(riid, IID_IRpcChannelBuffer))
+ {
+ *ppvObj = (IRpcChannelBuffer *) this;
+ }
+ else if (IsEqualIID(riid, IID_INonNDRStub) &&
+ (state & proxy_cs) && pIPIDEntry &&
+ (pIPIDEntry->dwFlags & IPIDF_NONNDRSTUB))
+ {
+ // this interface is used to tell proxies whether the server side speaks
+ // NDR or not. Returns S_OK if NOT NDR.
+ *ppvObj = (IUnknown *) this;
+ }
+ else
+ {
+ *ppvObj = NULL;
+ return E_NOINTERFACE;
+ }
+
+ AddRef();
+ return S_OK;
+}
+
+/***************************************************************************/
+STDMETHODIMP_(ULONG) CRpcChannelBuffer::Release( THIS )
+{
+ // can't call AssertValid(FALSE) since it is used in asserts
+ ULONG lRef = ref_count - 1;
+
+ if (InterlockedDecrement( (long*) &ref_count ) == 0)
+ {
+ delete this;
+ return 0;
+ }
+ else
+ {
+ return lRef;
+ }
+}
+
+/***************************************************************************/
+STDMETHODIMP CRpcChannelBuffer::SendReceive( THIS_ RPCOLEMESSAGE *pMessage,
+ ULONG *status )
+{
+ return CRpcChannelBuffer::SendReceive2(pMessage, status);
+}
+
+/***************************************************************************/
+STDMETHODIMP CRpcChannelBuffer::SendReceive2( THIS_ RPCOLEMESSAGE *pMessage,
+ ULONG *status )
+{
+ TRACECALL(TRACE_RPC, "CRpcChannelBuffer::SendReceive");
+ ComDebOut((DEB_CHANNEL, "CRpcChannelBuffer::SendReceive pChnl:%x pMsg:%x\n",
+ this, pMessage));
+
+ AssertValid(FALSE, TRUE);
+ Win4Assert( state & proxy_cs );
+ gOXIDTbl.ValidateOXID();
+ ASSERT_LOCK_RELEASED
+
+ HRESULT result;
+ working_call *call;
+ working_call *next_call;
+ IID iid;
+ WireThis *inb;
+ WireThat *outb;
+ DWORD saved_authn_level;
+ BOOL resume;
+ char *stub_data;
+
+ // Get the information about the call stored in TLS
+ COleTls tls;
+ call = (working_call *) tls->pCallInfo;
+ Win4Assert( call != NULL );
+ next_call = (working_call *) call->pNext;
+ saved_authn_level = call->lSavedAuthnLevel;
+ resume = call->iFlags & CF_WAS_IMPERSONATING;
+
+ // Set up the header pointers.
+ inb = (WireThis *) call->pHeader;
+ iid =
+ ((RPC_CLIENT_INTERFACE *) ((RPC_MESSAGE *) call->pmessage)->RpcInterfaceInformation)->InterfaceId.SyntaxGUID;
+
+ // we must ensure that we dont go away during this call. we will Release
+ // ourselves in the FreeBuffer call, or in the error handling at the
+ // end of this function.
+ pStdId->LockClient();
+
+#if DBG==1
+ DWORD CallCat = GetCallCat( inb );
+ DebugPrintORPCCall(ORPC_SENDRECEIVE_BEGIN, iid, call->message.iMethod, CallCat);
+ RpcSpy((CALLOUT_BEGIN, inb, iid, call->message.iMethod, 0));
+#endif
+
+ // Send the request.
+ if ((state & mswmsg_cs) || (IsMTAThread() && !call->Local()))
+ {
+ // For MSWMSG or non-local MTA, call ThreadSendReceive directly.
+ result = ThreadSendReceive( call );
+ }
+ else
+ {
+ if (call->Local())
+ call->message.reserved2[3] = NULL;
+
+ if (IsMTAThread())
+ {
+ LOCK
+ result = GetToSTA( pOXIDEntry, call);
+ UNLOCK
+ }
+ else
+ {
+ result = SwitchSTA( pOXIDEntry, (CChannelCallInfo **) &call );
+ }
+ }
+
+#if DBG==1
+ DebugPrintORPCCall(ORPC_SENDRECEIVE_END, iid, pMessage->iMethod, CallCat);
+ RpcSpy((CALLOUT_END, inb, iid, pMessage->iMethod, result));
+#endif
+
+ // We can't look at the call structure if the call was canceled.
+ if (result != RPC_E_CALL_CANCELED)
+ {
+ // Get the reply header if there is a reply buffer.
+ if ((state & mswmsg_cs) && (pMessage->rpcFlags & RPCFLG_ASYNCHRONOUS))
+ outb = NULL;
+ else
+ outb = (WireThat *) call->message.Buffer;
+
+ // Local calls reuse pNext on the server side.
+ call->pNext = next_call;
+
+ // Save the real buffer pointer for FreeBuffer.
+ call->pHeader = call->message.Buffer;
+ }
+ else
+ outb = NULL;
+
+ // Figure out when to retry.
+ // FreeThreaded - treat retry as a failure.
+ // Apartment - return the buffer and let call control decide.
+
+ if (result == S_OK)
+ {
+ // No buffer was returned for async calls on MSWMSG.
+ if (outb == NULL)
+ *status = S_OK;
+ else if (IsMTAThread())
+ {
+ if (outb->c.flags & ORPCF_REJECTED)
+ result = RPC_E_CALL_REJECTED;
+ else if (outb->c.flags & ORPCF_RETRY_LATER)
+ result = RPC_E_SERVERCALL_RETRYLATER;
+ else
+ *status = S_OK;
+ }
+ else if (outb->c.flags & ORPCF_REJECTED)
+ *status = (ULONG) RPC_E_CALL_REJECTED;
+ else if (outb->c.flags & ORPCF_RETRY_LATER)
+ *status = (ULONG) RPC_E_SERVERCALL_RETRYLATER;
+ else
+ *status = S_OK;
+ }
+
+ // Check the packet extensions.
+ if (result != RPC_E_CALL_CANCELED)
+ {
+ stub_data = (char *) call->message.Buffer;
+ result = ClientNotify( iid, outb, call->message.cbBuffer,
+ (void **) &stub_data,
+ call->message.dataRepresentation,
+ result );
+ }
+ else
+ result = ClientNotify( iid, outb, 0, (void **) &stub_data, 0, result );
+
+ // Call succeeded.
+ if (result == S_OK && outb != NULL)
+ {
+ // The locked flag lets FreeBuffer know that it has to call
+ // RH->UnlockClient.
+ call->iFlags |= CF_LOCKED;
+ pMessage->Buffer = stub_data;
+ pMessage->cbBuffer = call->message.cbBuffer -
+ (stub_data - (char *) call->message.Buffer);
+ pMessage->dataRepresentation = call->message.dataRepresentation;
+ result = *status;
+
+ // Copy a portion of the message structure that RPC updated on SendReceive.
+ // This is needed to free the buffer. Note that we still have to free
+ // the buffer in certain failure cases (reject).
+ pMessage->reserved2[2] = call->message.reserved2[2];
+
+ }
+ else
+ {
+ // Resume any outstanding impersonation.
+ ResumeImpersonate( tls->pCallContext, resume );
+
+ // Clean up the call.
+ pStdId->UnLockClient();
+ tls->pCallInfo = next_call;
+ tls->dwAuthnLevel = saved_authn_level;
+ delete call;
+
+ // Make sure FreeBuffer doesn't try to free the in buffer.
+ pMessage->Buffer = NULL;
+
+ // If the result is server fault, get the exception code from the CChannelCallInfo.
+ if (result == RPC_E_SERVERFAULT)
+ {
+ *status = call->server_fault;
+ }
+ // Everything else is a comm fault.
+ else if (result != S_OK)
+ {
+ *status = result;
+ result = RPC_E_FAULT;
+ }
+ tls->fault = *status;
+
+ // Since result is almost always mapped to RPC_E_FAULT, display the
+ // real error here to assist debugging.
+ if (*status != S_OK)
+ ComDebOut((DEB_CHANNEL, "ORPC call failed. status = %x\n", *status));
+ }
+
+ ASSERT_LOCK_RELEASED
+ gOXIDTbl.ValidateOXID();
+ ComDebOut((DEB_CHANNEL, "CRpcChannelBuffer::SendReceive hr:%x\n", result));
+ return result;
+}
+
+/***************************************************************************/
+HANDLE CRpcChannelBuffer::SwapSecurityToken( HANDLE hNew )
+{
+ HANDLE hOld = hToken;
+ hToken = hNew;
+ return hOld;
+}
+
+#if DBG == 1
+//+-------------------------------------------------------------------
+//
+// Member: CRpcChannelBuffer::AssertValid
+//
+// Synopsis: Validates that the state of the object is consistent.
+//
+// History: 25-Jan-94 CraigWi Created.
+//
+// DCOMWORK - Put in some asserts.
+//
+//--------------------------------------------------------------------
+void CRpcChannelBuffer::AssertValid(BOOL fKnownDisconnected,
+ BOOL fMustBeOnCOMThread)
+{
+ Win4Assert(state & (proxy_cs | client_cs | server_cs ));
+
+ if (state & (client_cs | proxy_cs))
+ {
+ ;
+ }
+ else if (state & server_cs)
+ {
+ Win4Assert( !(state & freethreaded_cs) );
+ if (fMustBeOnCOMThread && IsSTAThread())
+ Win4Assert(IsMTAThread() || pOXIDEntry->dwTid == GetCurrentThreadId());
+ // ref count can be 0 in various stages of connection and disconnection
+ Win4Assert(ref_count < 0x7fff && "Channel ref count unreasonably high");
+
+ // the pStdId pointer can not be NULL
+ // Win4Assert(IsValidInterface(pStdId));
+ }
+}
+#endif // DBG == 1
+
+
+/***************************************************************************/
+STDAPI_(ULONG) DebugCoGetRpcFault()
+{
+ HRESULT hr;
+ COleTls tls(hr);
+
+ if (SUCCEEDED(hr))
+ return tls->fault;
+
+ return 0;
+}
+
+/***************************************************************************/
+STDAPI_(void) DebugCoSetRpcFault( ULONG fault )
+{
+ HRESULT hr;
+ COleTls tls(hr);
+
+ if (SUCCEEDED(hr))
+ tls->fault = fault;
+}
+
+/***************************************************************************/
+extern "C"
+BOOL _stdcall DllDebugObjectRPCHook( BOOL trace, LPORPC_INIT_ARGS pass_through )
+{
+ if (!IsWOWThread())
+ {
+ DoDebuggerHooks = trace;
+ DebuggerArg = pass_through;
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+/***************************************************************************/
+BOOL LocalCall()
+{
+ CChannelCallInfo *call;
+
+ // Get the call info from TLS.
+ COleTls tls;
+ call = (CChannelCallInfo *) tls->pCallInfo;
+ Win4Assert( call != NULL );
+ return call->iFlags & CF_PROCESS_LOCAL;
+}
+
+/***************************************************************************/
+LONG ThreadInvokeExceptionFilter( DWORD lCode,
+ LPEXCEPTION_POINTERS lpep )
+{
+ ComDebOut((DEB_ERROR, "Exception 0x%x in ThreadInvoke at address 0x%x\n",
+ lCode, lpep->ExceptionRecord->ExceptionAddress));
+ DebugBreak();
+ return EXCEPTION_EXECUTE_HANDLER;
+}
+
+/***************************************************************************/
+/* This routine returns both comm status and server faults to the runtime
+ by raising exceptions. If FreeThreading is true, ComInvoke will throw
+ exceptions to indicate server faults. These will not be caught and will
+ propogate directly to the runtime. If FreeThreading is false, ComInvoke
+ will return the result and fault in the CChannelCallInfo record.
+
+ NOTE:
+ This function switches to the 32 bit stack under WIN95.
+ An exception has to be caught while switched to the 32 bit stack.
+ The exceptions has to be pass as a value and rethrown again on the
+ 16 bit stack (see SSInvoke in stkswtch.cxx)
+*/
+
+#ifdef _CHICAGO_
+DWORD
+#else
+void
+#endif
+SSAPI(ThreadInvoke)(RPC_MESSAGE *message )
+{
+ HRESULT result = S_OK;
+
+ TRACECALL(TRACE_RPC, "ThreadInvoke");
+ ComDebOut((DEB_CHANNEL,"ThreadInvoke pMsg:%x\n", message));
+ gOXIDTbl.ValidateOXID();
+ ASSERT_LOCK_RELEASED
+
+ BOOL success;
+ WireThis *inb = (WireThis *) message->Buffer;
+ IPID ipid;
+ RPC_STATUS status;
+ OXIDEntry *pOxid;
+ unsigned int transport_type;
+ DWORD authn_level;
+
+ // Byte swap the header.
+ ByteSwapThis( message->DataRepresentation, inb );
+
+ // Validate several things:
+ // The packet size is larger then the first header size.
+ // No extra flags are set.
+ // The procedure number is greater then 2 (not QI, AddRef, Release).
+ if (sizeof(WireThisPart1) > message->BufferLength ||
+ (inb->c.flags & ~(ORPCF_LOCAL | ORPCF_RESERVED1 |
+ ORPCF_RESERVED2 | ORPCF_RESERVED3 | ORPCF_RESERVED4)) != 0 ||
+ message->ProcNum < 3)
+ RETURN_COMM_STATUS( RPC_E_INVALID_HEADER );
+
+ // Validate the version.
+ if (inb->c.version.MajorVersion != COM_MAJOR_VERSION ||
+ inb->c.version.MinorVersion > COM_MINOR_VERSION)
+ RETURN_COMM_STATUS( RPC_E_VERSION_MISMATCH );
+
+ // Get the transport the call arrived on.
+ status = I_RpcServerInqTransportType( &transport_type );
+ if (status != RPC_S_OK)
+ RETURN_COMM_STATUS( RPC_E_SYS_CALL_FAILED );
+
+ // Don't accept the local header on remote calls.
+ if (inb->c.flags & ORPCF_LOCAL)
+ {
+ if (transport_type != TRANSPORT_TYPE_LPC &&
+ transport_type != TRANSPORT_TYPE_WMSG)
+ RETURN_COMM_STATUS( RPC_E_INVALID_HEADER );
+
+ // For local calls the authentication level will always be encrypt.
+ authn_level = RPC_C_AUTHN_LEVEL_PKT_PRIVACY;
+ }
+
+ // Don't accept remote calls if DCOM is diabled.
+ else if (gDisableDCOM &&
+ (transport_type == TRANSPORT_TYPE_CN || transport_type == TRANSPORT_TYPE_DG))
+ RETURN_COMM_STATUS( RPC_E_CALL_REJECTED );
+
+ // Lookup the authentication level.
+ else
+ {
+ result = RpcBindingInqAuthClient( message->Handle, NULL,
+ NULL, &authn_level, NULL, NULL );
+ if (result == RPC_S_BINDING_HAS_NO_AUTH)
+ authn_level = RPC_C_AUTHN_LEVEL_NONE;
+ else if (result != RPC_S_OK)
+ {
+ Win4Assert( result == RPC_S_OUT_OF_RESOURCES );
+ RETURN_COMM_STATUS( MAKE_WIN32( result ) );
+ }
+
+ // Verify the authentication level.
+ if (gAuthnLevel > RPC_C_AUTHN_LEVEL_NONE ||
+ gImpLevel > 0)
+ {
+ if (authn_level < gAuthnLevel)
+ RETURN_COMM_STATUS( RPC_E_ACCESS_DENIED );
+ }
+ }
+
+#if DBG==1
+ _try
+ {
+#endif
+
+ // Find the ipid entry from the ipid.
+ status = RpcBindingInqObject( message->Handle, &ipid );
+ if (status == RPC_S_OK)
+ {
+ // The CChannelCallInfo is created in a nested scope so that it
+ // is destroyed before the calls to throw an exception at the
+ // end of this function.
+ CChannelCallInfo call(
+ GetCallCat( inb ),
+ (RPCOLEMESSAGE *) message,
+ 0,
+ ipid,
+ (inb->c.flags & ORPCF_LOCAL) ? MSHCTX_LOCAL : MSHCTX_DIFFERENTMACHINE,
+ NULL,
+ authn_level );
+
+
+ // Find the OXIDEntry of the server apartment.
+
+ ASSERT_LOCK_RELEASED
+ LOCK
+
+ IPIDEntry *ipid_entry = gIPIDTbl.LookupIPID( ipid );
+
+ if (ipid_entry == NULL || (ipid_entry->dwFlags & IPIDF_DISCONNECTED)
+ || ipid_entry->pChnl == NULL )
+ {
+ UNLOCK
+ ASSERT_LOCK_RELEASED
+ result = RPC_E_DISCONNECTED;
+ }
+ else
+ {
+ pOxid = ipid_entry->pOXIDEntry;
+
+ // NCALRPC always gets the thread right (except on Chicago).
+ // For MTAs, any thread will do.
+ if (transport_type == TRANSPORT_TYPE_WMSG ||
+#ifndef _CHICAGO_
+ transport_type == TRANSPORT_TYPE_LPC ||
+#endif
+ (pOxid->dwFlags & OXIDF_MTASERVER))
+ {
+ UNLOCK
+ ASSERT_LOCK_RELEASED
+ result = ComInvoke( &call );
+ }
+ else
+ {
+ // Pass the message to the app thread.
+
+ IncOXIDRefCnt( pOxid );
+ result = GetToSTA( pOxid, &call );
+ DecOXIDRefCnt( pOxid );
+
+ UNLOCK
+ ASSERT_LOCK_RELEASED
+ }
+ }
+ }
+ else
+ {
+ result = MAKE_WIN32( status );
+ }
+
+#if DBG==1
+ }
+ _except( ThreadInvokeExceptionFilter(GetExceptionCode(),
+ GetExceptionInformation()) )
+ {
+ }
+#endif
+
+ // For comm and server faults, generate an exception. Otherwise the buffer
+ // is set up correctly.
+ gOXIDTbl.ValidateOXID();
+ if (result == RPC_E_SERVERFAULT)
+ {
+ ASSERT_LOCK_RELEASED
+ RETURN_COMM_STATUS( RPC_E_SERVERFAULT );
+ }
+ else if (result != S_OK)
+ {
+ ASSERT_LOCK_RELEASED
+ RETURN_COMM_STATUS( result );
+ }
+
+#ifdef _CHICAGO_
+ return 0;
+#endif //_CHICAGO_
+}
+
+
+/***************************************************************************/
+HRESULT ThreadSendReceive( CChannelCallInfo *call )
+{
+ TRACECALL(TRACE_RPC, "ThreadSendReceive");
+ ComDebOut((DEB_CHANNEL, "ThreadSendReceive pCall:%x\n", call));
+
+ ASSERT_LOCK_RELEASED
+
+ HRESULT result;
+ RPCOLEMESSAGE *message = call->pmessage;
+ WireThat *outb;
+
+ // Call the runtime. In the future, detect server faults and
+ // change the value of result to RPC_E_SERVERFAULT.
+ if (call->pChannel->state & mswmsg_cs)
+ {
+ CAptCallCtrl *pACC = GetAptCallCtrl();
+ CCliModalLoop *pCML = (pACC) ? pACC->GetTopCML() : NULL;
+ OXIDEntry *pOxidClient;
+ HWND hwnd = NULL;
+
+ if (IsWOWThread())
+ {
+ LOCK
+ result = gOXIDTbl.GetLocalEntry( &pOxidClient );
+ UNLOCK
+ Win4Assert( result == S_OK );
+ hwnd = (HWND) pOxidClient->hServerSTA;
+ }
+ TRACECALL(TRACE_RPC, "I_RpcAsyncSendReceive");
+ result = I_RpcAsyncSendReceive( (RPC_MESSAGE *) message, pCML, hwnd );
+
+ // If the call was canceled, the rest of the code path assumes that
+ // the call was deleted (by SwitchComThread). So delete it.
+ if (result == RPC_S_CALL_CANCELLED)
+ {
+ // Convert the win32 error to a hresult.
+ result = RPC_E_CALL_CANCELED;
+ delete call;
+ }
+ }
+ else
+ {
+ TRACECALL(TRACE_RPC, "I_RpcSendReceive");
+ result = I_RpcSendReceive( (RPC_MESSAGE *) message );
+ }
+
+ // If the result is small, it is probably a Win32 code.
+ if (result != 0)
+ {
+ message->Buffer = NULL;
+ if ((ULONG) result > 0xfffffff7 || (ULONG) result < 0x2000)
+ result = MAKE_WIN32( result );
+ }
+ else
+ {
+ // No buffer is returned for asynchronous calls on MSWMSG.
+ if ((call->pChannel->state & mswmsg_cs) == 0 ||
+ (message->rpcFlags & RPCFLG_ASYNCHRONOUS) == 0)
+ {
+ // Byte swap the reply header. Fail the call if the buffer is too
+ // small.
+ outb = (WireThat *) message->Buffer;
+ if (message->cbBuffer >= sizeof(WireThatPart1))
+ ByteSwapThat( message->dataRepresentation, outb);
+ else
+ result = RPC_E_INVALID_HEADER;
+ }
+ }
+
+ ComDebOut((DEB_CHANNEL, "ThreadSendReceive pCall:%x hr:%x\n", call, result));
+ return result;
+}
+
+/***************************************************************************/
+/* static */
+
+void working_call::Cleanup()
+{
+ ASSERT_LOCK_HELD
+
+ DWORD i;
+
+ // Release everything.
+ if (next <= CALLCACHE_SIZE)
+ {
+ for (i = 0; i < next; i++)
+ if (list[i] != NULL)
+ {
+ PrivMemFree( list[i] );
+ list[i] = NULL;
+ }
+
+ next = 0;
+ }
+}
+
+/***************************************************************************/
+/* static */
+
+void working_call::Initialize()
+{
+ ASSERT_LOCK_HELD
+ next = 0;
+}
+
+//---------------------------------------------------------------------------
+//
+// Method: working_call:: operator delete
+//
+// Synopsis: Cache or actually free a working call.
+//
+// Notes: gComLock need not be held before calling this function.
+//
+//---------------------------------------------------------------------------
+void working_call::operator delete( void *call )
+{
+ // Add the structure to the list if the list is not full and
+ // if the process is still initialized (since latent threads may try
+ // to return stuff).
+
+ LOCK
+ if (next < CALLCACHE_SIZE && gfChannelProcessInitialized)
+ {
+ list[next] = call;
+ next += 1;
+ }
+
+ // Otherwise just free it.
+ else
+ {
+ PrivMemFree( call );
+ }
+ UNLOCK
+}
+
+//---------------------------------------------------------------------------
+//
+// Method: working_call:: operator new
+//
+// Synopsis: Keep a cache of working_calls. Since the destructor is
+// virtual, the correct delete will be called if any base
+// class is deleted.
+//
+// Notes: gComLock must be held before calling this function.
+//
+//---------------------------------------------------------------------------
+void *working_call::operator new( size_t size )
+{
+ ASSERT_LOCK_HELD
+
+ void *call;
+
+ // Get the last entry from the cache.
+ Win4Assert( size == sizeof( working_call ) );
+ if (next > 0 && next < CALLCACHE_SIZE+1)
+ {
+ next -= 1;
+ call = list[next];
+ list[next] = NULL;
+ }
+
+ // If there are none, allocate a new one.
+ else
+ call = PrivMemAlloc(size);
+ return call;
+}
+
+ /**********************************************************************/
+working_call::working_call( CALLCATEGORY callcat,
+ RPCOLEMESSAGE *original_msg,
+ DWORD flags,
+ REFIPID ipidServer,
+ DWORD destctx,
+ CRpcChannelBuffer *channel,
+ DWORD authn_level ) :
+ CChannelCallInfo( callcat, &message, flags, ipidServer, destctx, channel,
+ authn_level )
+{
+ message = *original_msg;
+}
+