mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2665 lines
79 KiB
2665 lines
79 KiB
//+---------------------------------------------------------------------
|
|
//
|
|
// 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;
|
|
}
|
|
|