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.
1820 lines
62 KiB
1820 lines
62 KiB
//+-------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1992 - 1993.
|
|
//
|
|
// File: callctrl.cxx
|
|
//
|
|
// Contents: Contains the ORPC CallControl code
|
|
//
|
|
// History: 21-Dec-93 Johannp Original Version
|
|
// 04-Nov-94 Rickhi ReWrite as layer over channel
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
#include <ole2int.h>
|
|
#include <thkreg.h> // OLETHK_ defines
|
|
#include <dde.h>
|
|
#include <callctrl.hxx> // Class Definition
|
|
#include <objsrv.h> // IID_IObjServer
|
|
|
|
|
|
// private defines used only in this file
|
|
#define WM_SYSTIMER 0x0118
|
|
#define SYS_ALTDOWN 0x2000
|
|
#define WM_NCMOUSEFIRST WM_NCMOUSEMOVE
|
|
#define WM_NCMOUSELAST WM_NCMBUTTONDBLCLK
|
|
|
|
|
|
// empty slot in window registration
|
|
#define WD_EMPTY (HWND)-1
|
|
|
|
// the following table is used to quickly determine what windows
|
|
// message queue inputflag to specify for the various categories of
|
|
// outgoing calls in progress. The table is indexed by CALLCATEGORY.
|
|
|
|
DWORD gMsgQInputFlagTbl[4] = {
|
|
QS_ALLINPUT | QS_TRANSFER | QS_ALLPOSTMESSAGE, // NOCALL
|
|
QS_ALLINPUT | QS_TRANSFER | QS_ALLPOSTMESSAGE, // SYNCHRONOUS
|
|
QS_ALLINPUT | QS_TRANSFER | QS_ALLPOSTMESSAGE, // ASYNC
|
|
QS_SENDMESSAGE}; // INPUTSYNC
|
|
|
|
|
|
// the following table is used to map bit flags in the Rpc Message to
|
|
// the equivalent OLE CALLCATEGORY.
|
|
|
|
DWORD gRpcFlagToCallCatMap[3] = {
|
|
CALLCAT_SYNCHRONOUS, // no flags set
|
|
CALLCAT_INPUTSYNC, // RPCFLG_INPUT_SYNCHRONOUS
|
|
CALLCAT_ASYNC}; // RPCFLG_ASYNCHRONOUS
|
|
|
|
|
|
// prototype
|
|
HRESULT CopyMsgForRetry(RPCOLEMESSAGE *pMsg,
|
|
IRpcChannelBuffer *pChnl,
|
|
HRESULT hrIn);
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: CoRegisterMessageFilter, public
|
|
//
|
|
// Synopsis: registers an applications message filter with the call control
|
|
//
|
|
// Arguments: [pMsgFilter] - message filter to register
|
|
// [ppMsgFilter] - optional, where to return previous IMF
|
|
//
|
|
// Returns: S_OK - registered successfully
|
|
//
|
|
// History: 21-Dec-93 JohannP Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
STDAPI CoRegisterMessageFilter(LPMESSAGEFILTER pMsgFilter,
|
|
LPMESSAGEFILTER *ppMsgFilter)
|
|
{
|
|
ComDebOut((DEB_MFILTER, "CoRegisterMessageFilter pMF:%x ppMFOld:%x\n",
|
|
pMsgFilter, ppMsgFilter));
|
|
CALLHOOKOBJECT(S_OK,CLSID_NULL,IID_IMessageFilter,(IUnknown **)&pMsgFilter);
|
|
|
|
// validate the parameters. NULL acceptable for either or both parameters.
|
|
if (pMsgFilter != NULL && !IsValidInterface(pMsgFilter))
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if(ppMsgFilter != NULL && !IsValidPtrOut(ppMsgFilter, sizeof(ppMsgFilter)))
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
// this operation is not allowed on MTA Threads
|
|
if (IsMTAThread())
|
|
return CO_E_NOT_SUPPORTED;
|
|
|
|
// find the callcontrol for this apartment and replace the existing
|
|
// message filter. if no callctrl has been created yet, just stick
|
|
// the pMsgFilter in tls.
|
|
|
|
COleTls tls;
|
|
CAptCallCtrl *pACC = tls->pCallCtrl;
|
|
|
|
IMessageFilter *pOldMF;
|
|
|
|
if (pACC)
|
|
{
|
|
pOldMF = pACC->InstallMsgFilter(pMsgFilter);
|
|
}
|
|
else
|
|
{
|
|
pOldMF = tls->pMsgFilter;
|
|
|
|
if (pMsgFilter)
|
|
{
|
|
pMsgFilter->AddRef();
|
|
}
|
|
tls->pMsgFilter = pMsgFilter;
|
|
}
|
|
if (ppMsgFilter)
|
|
{
|
|
// return old MF to the caller
|
|
*ppMsgFilter = pOldMF;
|
|
}
|
|
else if (pOldMF)
|
|
{
|
|
// release the old MF
|
|
pOldMF->Release();
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CAptCallCtrl::InstallMsgFilter
|
|
//
|
|
// Synopsis: called to install a new application provided message filter
|
|
//
|
|
// Arguments: [pMF] - new message filter to install (or NULL)
|
|
//
|
|
// Returns: previous message filter if there was one
|
|
//
|
|
// History: 20-Dec-93 JohannP Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
INTERNAL_(IMessageFilter *) CAptCallCtrl::InstallMsgFilter(IMessageFilter *pMF)
|
|
{
|
|
IMessageFilter *pMFOld = _pMF; // save the old one to return
|
|
|
|
_pMF = pMF; // install the new one
|
|
if (_pMF)
|
|
{
|
|
_pMF->AddRef();
|
|
}
|
|
|
|
return pMFOld;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CAptCallCtrl::CAptCallCtrl
|
|
//
|
|
// Synopsis: constructor for per apartment call control state
|
|
//
|
|
// History: 11-Nov-94 Rickhi Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
CAptCallCtrl::CAptCallCtrl() :
|
|
_fInMsgFilter(FALSE),
|
|
_pTopCML(NULL)
|
|
{
|
|
// The first one is reserved for ORPC. An hWnd value of WD_EMPTY
|
|
// means the slot is available.
|
|
_WD[0].hWnd = WD_EMPTY;
|
|
|
|
// The second slot has fixed values for DDE
|
|
_WD[1].hWnd = NULL;
|
|
_WD[1].wFirstMsg = WM_DDE_FIRST;
|
|
_WD[1].wLastMsg = WM_DDE_LAST;
|
|
|
|
// put our pointer into thread local storage, and retrieve any previously
|
|
// registered message filter.
|
|
|
|
COleTls tls;
|
|
tls->pCallCtrl = this;
|
|
|
|
_pMF = tls->pMsgFilter;
|
|
tls->pMsgFilter = NULL;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CAptCallCtrl::~CAptCallCtrl
|
|
//
|
|
// Synopsis: destructor for per apartment call control state
|
|
//
|
|
// History: 11-Nov-94 Rickhi Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
CAptCallCtrl::~CAptCallCtrl()
|
|
{
|
|
Win4Assert(_pTopCML == NULL); // no outgoing calls.
|
|
|
|
if (_pMF)
|
|
{
|
|
_pMF->Release();
|
|
}
|
|
|
|
// remove our pointer from thread local storage
|
|
COleTls tls;
|
|
tls->pCallCtrl = NULL;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CAptCallCtrl::Register/Revoke
|
|
//
|
|
// Synopsis: register or revoke RPC window data
|
|
//
|
|
// Arguments: [hWnd] - window handle to look for calls on
|
|
// [wFirstMsg] - msgid of first message in range to look for
|
|
// [wLastMsg] - msgid of last message in range to look for
|
|
//
|
|
// Returns: nothing
|
|
//
|
|
// Notes: This code is only ever called by the RpcChannel and by
|
|
// the DDE layer, and so error checking is kept to a minimum.
|
|
//
|
|
// History: 30-Apr-95 Rickhi Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
void CAptCallCtrl::Register(HWND hWnd, UINT wFirstMsg, UINT wLastMsg)
|
|
{
|
|
Win4Assert(_WD[0].hWnd == WD_EMPTY && "Register Out of Space");
|
|
|
|
_WD[0].hWnd = hWnd;
|
|
_WD[0].wFirstMsg = wFirstMsg;
|
|
_WD[0].wLastMsg = wLastMsg;
|
|
}
|
|
|
|
void CAptCallCtrl::Revoke(HWND hWnd)
|
|
{
|
|
Win4Assert(_WD[0].hWnd == hWnd && "Revoke not found");
|
|
_WD[0].hWnd = WD_EMPTY;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: GetSlowTimeFactor
|
|
//
|
|
// Synopsis: Get the time slowing factor for Wow apps
|
|
//
|
|
// Returns: The factor by which we need to slow time down.
|
|
//
|
|
// Algorithm: If there is a factor in the registry, we open and read the
|
|
// registry. Otherwise we just set it to the default.
|
|
//
|
|
// History: 22-Jul-94 Ricksa Created
|
|
// 09-Jun-95 Susia ANSI Chicago optimization
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
#ifdef _CHICAGO_
|
|
#undef RegOpenKeyEx
|
|
#define RegOpenKeyEx RegOpenKeyExA
|
|
#undef RegQueryValueEx
|
|
#define RegQueryValueEx RegQueryValueExA
|
|
#endif
|
|
DWORD GetSlowTimeFactor(void)
|
|
{
|
|
// Default slowing time so we can just exit if there is no key which
|
|
// is assumed to be the common case.
|
|
DWORD dwSlowTimeFactor = OLETHK_DEFAULT_SLOWRPCTIME;
|
|
|
|
// Key for reading the value from the registry
|
|
HKEY hkeyOleThk;
|
|
|
|
// Get the Ole Thunk special value key
|
|
LONG lStatus = RegOpenKeyEx(HKEY_CLASSES_ROOT, OLETHK_KEY, 0, KEY_READ,
|
|
&hkeyOleThk);
|
|
|
|
if (lStatus == ERROR_SUCCESS)
|
|
{
|
|
DWORD dwType;
|
|
DWORD dwSizeData = sizeof(dwSlowTimeFactor);
|
|
|
|
lStatus = RegQueryValueEx(hkeyOleThk, OLETHK_SLOWRPCTIME_VALUE, NULL,
|
|
&dwType, (LPBYTE) &dwSlowTimeFactor, &dwSizeData);
|
|
|
|
if ((lStatus != ERROR_SUCCESS) || dwType != REG_DWORD)
|
|
{
|
|
// Guarantee that value is reasonable if something went wrong.
|
|
dwSlowTimeFactor = OLETHK_DEFAULT_SLOWRPCTIME;
|
|
}
|
|
|
|
// Close the key since we are done with it.
|
|
RegCloseKey(hkeyOleThk);
|
|
}
|
|
|
|
return dwSlowTimeFactor;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: CanMakeOutCall
|
|
//
|
|
// Synopsis: called when the client app wants to make an outgoing call to
|
|
// determine if it is OK to do it now or not. Common subroutine
|
|
// to CAptRpcChnl::GetBuffer and RemoteReleaseRifRef.
|
|
//
|
|
// Arguments: [dwCallCatOut] - call category of call the app wants to make
|
|
// [pChnl] - ptr to channel call is being made on
|
|
// [riid] - interface call is being made on
|
|
//
|
|
// Returns: S_OK - ok to make the call
|
|
// RPC_E_CANTCALLOUT_INEXTERNALCALL - inside IMessageFilter
|
|
// RPC_E_CANTCALLOUT_INASYNCCALL - inside async call
|
|
// RPC_E_CANTCALLOUT_ININPUTSYNCCALL - inside input sync or SendMsg
|
|
//
|
|
// History: 21-Dec-93 Johannp Original Version
|
|
// 04-Nov-94 Rickhi ReWrite
|
|
// 03-Oct-95 Rickhi Made into common subroutine
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
INTERNAL CanMakeOutCall(DWORD dwCallCatOut, REFIID riid)
|
|
{
|
|
// get the topmost incoming call state from Tls.
|
|
|
|
HRESULT hr;
|
|
COleTls tls(hr);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
CSrvCallState *pSCS = tls->pTopSCS;
|
|
|
|
DWORD dwCallCatIn = (pSCS) ? pSCS->GetCallCatIn() : CALLCAT_NOCALL;
|
|
|
|
// if handling an incoming ASYNC call, only allow ASYNC outgoing calls,
|
|
// and local calls on IRemUnknown (which locally is actually IRundown).
|
|
|
|
if (dwCallCatIn == CALLCAT_ASYNC &&
|
|
dwCallCatOut != CALLCAT_ASYNC &&
|
|
!IsEqualGUID(riid, IID_IRundown))
|
|
{
|
|
return RPC_E_CANTCALLOUT_INASYNCCALL;
|
|
}
|
|
|
|
// if handling an incoming INPUTSYNC call, or if we are handling a
|
|
// SendMessage, dont allow SYNCHRONOUS calls out or we could deadlock
|
|
// since SYNC uses PostMessage and INPUTSYNC uses SendMessage.
|
|
|
|
if (dwCallCatOut == CALLCAT_SYNCHRONOUS &&
|
|
(dwCallCatIn == CALLCAT_INPUTSYNC || InSendMessage()))
|
|
{
|
|
return RPC_E_CANTCALLOUT_ININPUTSYNCCALL;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CMTARpcChnl::CMTARpcChnl/~CMTARpcChnl
|
|
//
|
|
// Synopsis: constructor/destructor
|
|
//
|
|
// Parameters: [pStdId] - std identity for the object
|
|
// [pOXIDEntry] - OXIDEntry for the object server
|
|
// [eState] - state flags passed thru to CRpcChannelBuffer
|
|
// (ignored by CMTARpcCnl).
|
|
//
|
|
// History: 11-Nov-94 Rickhi Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
CMTARpcChnl::CMTARpcChnl(CStdIdentity *pStdId,
|
|
OXIDEntry *pOXIDEntry,
|
|
DWORD eState) :
|
|
CRpcChannelBuffer(pStdId, pOXIDEntry, eState),
|
|
_dwTIDCallee(pOXIDEntry->dwTid),
|
|
_dwAptId(GetCurrentApartmentId())
|
|
{
|
|
ComDebOut((DEB_CALLCONT,"CMTARpcChnl::CMTARpcChnl this:%x\n", this));
|
|
}
|
|
|
|
CMTARpcChnl::~CMTARpcChnl()
|
|
{
|
|
ComDebOut((DEB_CALLCONT,"CMTARpcChnl::~CMTARpcChnl this:%x\n", this));
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CMTARpcChnl::GetBuffer
|
|
//
|
|
// Synopsis: Ensure it is legal to call out now, then get a buffer.
|
|
//
|
|
// Parameters: [pMsg] - ptr to message structure
|
|
// [riid] - interface call is being made on
|
|
//
|
|
// History: 11-Nov-94 Rickhi Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
STDMETHODIMP CMTARpcChnl::GetBuffer(RPCOLEMESSAGE *pMsg, REFIID riid)
|
|
{
|
|
HRESULT hr;
|
|
COleTls tls(hr); // use this form incase no calls made on this thread yet
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
if (!IsMTAThread())
|
|
{
|
|
ComDebOut((DEB_WARN,"CMTARpcChnl::GetBuffer - MTA proxy called on apartment thread, this: 0x%x\n",
|
|
this));
|
|
return RPC_E_WRONG_THREAD;
|
|
}
|
|
|
|
// Make sure we are allowed to make this outgoing call. We do that here
|
|
// so that we dont marshal all the parameters only to discover that we
|
|
// cant call out and then have to free all the marshalled parameters
|
|
// (especially the ones where marshalling has side effects).
|
|
|
|
if (!(_dwAptId == GetCurrentApartmentId() || CallableOnAnyApt()))
|
|
{
|
|
// we are not being called on a thread in the MTA apartment
|
|
return RPC_E_WRONG_THREAD;
|
|
}
|
|
|
|
if (pMsg->rpcFlags & RPCFLG_INPUT_SYNCHRONOUS)
|
|
{
|
|
// dont allow INPUTSYNC calls from an MTA apartment to anybody.
|
|
return RPC_E_CANTCALLOUT_ININPUTSYNCCALL;
|
|
}
|
|
|
|
// All ASYNC calls from an MTA apartment are treated as SYNCHRONOUS,
|
|
// so convert the call category here before proceeding.
|
|
|
|
pMsg->rpcFlags &= ~RPCFLG_ASYNCHRONOUS;
|
|
|
|
// ask the real channel for a buffer.
|
|
return CRpcChannelBuffer::ClientGetBuffer(pMsg, riid);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CAptRpcChnl::CAptRpcChnl/~CAptRpcChnl
|
|
//
|
|
// Synopsis: constructor/destructor
|
|
//
|
|
// Parameters: [pStdId] - std identity for the object
|
|
// [pOXIDEntry] - OXIDEntry for the object server
|
|
// [eState] - state flags passed thru to CRpcChannelBuffer
|
|
// (ignored by CAptRpcCnl).
|
|
//
|
|
// History: 11-Nov-94 Rickhi Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
CAptRpcChnl::CAptRpcChnl(CStdIdentity *pStdId,
|
|
OXIDEntry *pOXIDEntry,
|
|
DWORD eState) :
|
|
CRpcChannelBuffer(pStdId, pOXIDEntry, eState),
|
|
_dwTIDCallee(pOXIDEntry->dwTid),
|
|
_dwAptId(GetCurrentApartmentId())
|
|
{
|
|
ComDebOut((DEB_CALLCONT,"CAptRpcChnl::CAptRpcChnl this:%x\n", this));
|
|
}
|
|
|
|
CAptRpcChnl::~CAptRpcChnl()
|
|
{
|
|
ComDebOut((DEB_CALLCONT,"CAptRpcChnl::~CAptRpcChnl this:%x\n", this));
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CAptRpcChnl::GetBuffer
|
|
//
|
|
// Synopsis: Ensure it is legal to call out now, then get a buffer.
|
|
//
|
|
// Parameters: [pMsg] - ptr to message structure
|
|
// [riid] - interface call is being made on
|
|
//
|
|
// History: 11-Nov-94 Rickhi Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
STDMETHODIMP CAptRpcChnl::GetBuffer(RPCOLEMESSAGE *pMsg, REFIID riid)
|
|
{
|
|
HRESULT hr;
|
|
COleTls tls(hr); // use this form incase no calls made on this thread yet
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// first, make sure that we are being called on the correct thread
|
|
// so that we are sure tls->pCallCtrl is set.
|
|
|
|
if (!IsSTAThread() || !(_dwAptId == GetCurrentApartmentId() ||
|
|
CallableOnAnyApt()))
|
|
{
|
|
return RPC_E_WRONG_THREAD;
|
|
}
|
|
|
|
// dont allow the application to call out while handling an
|
|
// IMessageFilter call because it screws up the call sequencing.
|
|
|
|
CAptCallCtrl *pACC = tls->pCallCtrl;
|
|
|
|
if (pACC && pACC->InMsgFilter())
|
|
{
|
|
ComDebOut((DEB_ERROR, "Illegal callout from within IMessageFilter\n"));
|
|
return RPC_E_CANTCALLOUT_INEXTERNALCALL;
|
|
}
|
|
|
|
// if the call is async and remote, or async and to an MTA apartment,
|
|
// then change the category to sync, since we dont support async remotely
|
|
// or to MTA apartments locally. This must be done before calling
|
|
// CanMakeOutCall in order to avoid deadlocks. If the call is input sync
|
|
// and remote or to an MTA apartment, dissallow the call.
|
|
|
|
if (pMsg->rpcFlags & (RPCFLG_ASYNCHRONOUS | RPCFLG_INPUT_SYNCHRONOUS))
|
|
{
|
|
DWORD dwCtx;
|
|
CRpcChannelBuffer::GetDestCtx(&dwCtx, NULL);
|
|
|
|
if (dwCtx == MSHCTX_DIFFERENTMACHINE ||
|
|
(GetOXIDEntry()->dwFlags & OXIDF_MTASERVER))
|
|
{
|
|
if (pMsg->rpcFlags & RPCFLG_INPUT_SYNCHRONOUS)
|
|
return RPC_E_CANTCALLOUT_ININPUTSYNCCALL;
|
|
|
|
// turn off the async flag so that the call looks (and acts)
|
|
// like it is synchronous.
|
|
|
|
pMsg->rpcFlags &= ~RPCFLG_ASYNCHRONOUS;
|
|
}
|
|
}
|
|
|
|
// Make sure we are allowed to make this outgoing call. We do that here
|
|
// so that we dont marshal all the parameters only to discover that we
|
|
// cant call out and then have to free all the marshalled parameters
|
|
// (especially the ones where marshalling has side effects).
|
|
|
|
// figure out the call category of this call by looking at bit
|
|
// values in the rpc message flags.
|
|
|
|
DWORD dwCallCatOut = RpcFlagToCallCat(pMsg->rpcFlags);
|
|
|
|
// check other outgoing call restrictions common to multi and single
|
|
// threaded apartments
|
|
|
|
hr = CanMakeOutCall(dwCallCatOut, riid);
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
// ask the real channel for a buffer.
|
|
hr = CRpcChannelBuffer::ClientGetBuffer(pMsg, riid);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CAptRpcChnl::SendReceive
|
|
//
|
|
// Synopsis: instantiate a modal loop object and then transmit the call
|
|
//
|
|
// Parameters: [pMsg] - ptr to message structure
|
|
// [pulStatus] - place to return a status code
|
|
//
|
|
// History: 11-Nov-94 Rickhi Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
STDMETHODIMP CAptRpcChnl::SendReceive(RPCOLEMESSAGE *pMsg, ULONG *pulStatus)
|
|
{
|
|
// Figure out the call category of this call by looking at the bit
|
|
// values in the rpc message flags.
|
|
|
|
DWORD dwCallCatOut = RpcFlagToCallCat(pMsg->rpcFlags);
|
|
DWORD dwMsgQInputFlag = gMsgQInputFlagTbl[dwCallCatOut];
|
|
|
|
// Now for a spectacular hack. IRemUnknown::Release had slightly
|
|
// different dwMsgQInputFlag semantic in the old code base, so we
|
|
// check for that one case here and set the flag accordingly. Not
|
|
// doing this would allow SYSCOMMAND calls in during Release which
|
|
// we throw away, thus preventing an app from shutting down correctly.
|
|
// SimpSvr.exe is a good example of this.
|
|
|
|
if ((pMsg->iMethod & ~RPC_FLAGS_VALID_BIT) == 5 &&
|
|
(IsEqualIID(IID_IRundown, *MSG_TO_IIDPTR(pMsg)) ||
|
|
IsEqualIID(IID_IRemUnknown, *MSG_TO_IIDPTR(pMsg))))
|
|
{
|
|
dwMsgQInputFlag = (QS_POSTMESSAGE | QS_SENDMESSAGE | QS_TRANSFER |
|
|
QS_ALLPOSTMESSAGE);
|
|
}
|
|
|
|
|
|
// Now construct a modal loop object for the call that is about to
|
|
// be made. It maintains the call state and exits when the call has
|
|
// been completed, cancelled, or rejected.
|
|
|
|
HRESULT hr;
|
|
CCliModalLoop CML(_dwTIDCallee, dwMsgQInputFlag);
|
|
|
|
do
|
|
{
|
|
hr = CML.SendReceive(pMsg, pulStatus, this);
|
|
|
|
if (hr == RPC_E_SERVERCALL_RETRYLATER)
|
|
{
|
|
// the call was rejected by the server and the client Msg Filter
|
|
// decided to retry the call. We have to make a copy of the
|
|
// message and re-send it.
|
|
|
|
hr = CopyMsgForRetry(pMsg);
|
|
}
|
|
else if (hr == RPC_E_CALL_REJECTED)
|
|
{
|
|
// the call was rejected by the server and the client Msg Filter
|
|
// decided NOT to retry the call. We have to free the buffer
|
|
// that was returned since the proxy is not expecting it.
|
|
|
|
FreeBuffer(pMsg);
|
|
}
|
|
|
|
} while (hr == RPC_E_SERVERCALL_RETRYLATER);
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CAptRpcChnl::CopyMsgForRetry
|
|
//
|
|
// Synopsis: Makes a copy of the message we sent. We have to ask Rpc
|
|
// for another buffer and then copy the original buffer into
|
|
// the new one so we can make another call.
|
|
//
|
|
// Parameters: [pMsg] - ptr to message structure to copy
|
|
//
|
|
// History: 11-Nov-94 Rickhi Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
HRESULT CAptRpcChnl::CopyMsgForRetry(RPCOLEMESSAGE *pMsg)
|
|
{
|
|
ComDebOut((DEB_CALLCONT,"CAptRpcChnl::CopyMsgForRetry pMsg:%x\n", pMsg));
|
|
|
|
// CODEWORK: this is dumb, but the channel blows chunks in FreeBuffer
|
|
// if i dont do this double copy.
|
|
|
|
void *pTmpBuf = PrivMemAlloc(pMsg->cbBuffer);
|
|
if (pTmpBuf)
|
|
{
|
|
memcpy(pTmpBuf, pMsg->Buffer, pMsg->cbBuffer);
|
|
}
|
|
|
|
// save copy of the contents of the old message so we can free it later
|
|
|
|
HRESULT hr = E_OUTOFMEMORY;
|
|
RPCOLEMESSAGE MsgToFree = *pMsg;
|
|
FreeBuffer(&MsgToFree);
|
|
|
|
if (pTmpBuf)
|
|
{
|
|
// allocate a new message, dont have to worry about checking the
|
|
// CanMakeOutCall again, so we just ask the Rpc channel directly.
|
|
|
|
hr = CRpcChannelBuffer::GetBuffer(pMsg, *MSG_TO_IIDPTR(pMsg));
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// copy the temp buffer into the new buffer
|
|
memcpy(pMsg->Buffer, pTmpBuf, pMsg->cbBuffer);
|
|
hr = RPC_E_SERVERCALL_RETRYLATER;
|
|
}
|
|
|
|
PrivMemFree(pTmpBuf);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CCliModalLoop::SendReceive
|
|
//
|
|
// Synopsis: called to transmit a call to the server and enter a modal
|
|
// loop.
|
|
//
|
|
// Arguments: [pMsg] - message to send
|
|
// [pulStatus] - place to return status code
|
|
// [pChnl] - IRpcChannelBuffer pointer
|
|
//
|
|
// Returns: result of the call. May return RETRYLATER if the call should
|
|
// be retransmitted.
|
|
//
|
|
// History: 11-Nov-94 Rickhi Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
INTERNAL CCliModalLoop::SendReceive(RPCOLEMESSAGE *pMsg, ULONG *pulStatus,
|
|
IRpcChannelBuffer2 *pChnl)
|
|
{
|
|
// SendReceive is a blocking call. The channel will transmit the call
|
|
// asynchronously then call us back in BlockFn where we wait for an
|
|
// event such as the call completing, or a windows message arriving,
|
|
// or the user cancelling the call. Because of the callback, we need
|
|
// to set _hr before calling SR.
|
|
|
|
_hr = RPC_S_CALLPENDING;
|
|
_hr = pChnl->SendReceive2(pMsg, pulStatus);
|
|
|
|
// By this point the call has completed. Now check if it was rejected
|
|
// and if so, whether we need to retry immediately, later, or never.
|
|
// Handling of Rejected calls must occur here, not in the BlockFn, due
|
|
// to the fact that some calls and some protocols are synchronous, and
|
|
// other calls and protocols are asynchronous.
|
|
|
|
if (_hr == RPC_E_CALL_REJECTED || _hr == RPC_E_SERVERCALL_RETRYLATER)
|
|
{
|
|
// this function decides on 1 of 3 different courses of action
|
|
// 1. fail the call - sets the state to Call_Rejected
|
|
// 2. retry immediately - sets _hr to RETRYLATER, fall out
|
|
// 3. retry later - starts the timer, we block below
|
|
|
|
_hr = HandleRejectedCall(pChnl);
|
|
|
|
// if a timer was installed to retry the call later, then we have
|
|
// to go into modal loop until the timer expires. if the call is
|
|
// cancelled while in this loop, the loop will be exited.
|
|
|
|
while (!IsTimerAtZero())
|
|
{
|
|
BlockFn(NULL);
|
|
}
|
|
|
|
// Either it is time to retransmit the call, or the call was
|
|
// cancelled or rejected.
|
|
}
|
|
|
|
return _hr;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CCliModalLoop::HandleRejectedCall
|
|
//
|
|
// Synopsis: called when the response to a remote call is rejected or
|
|
// retry later.
|
|
//
|
|
// Arguments: [pChnl] - channel we are calling on.
|
|
//
|
|
// Returns: RPC_E_CALL_REJECTED - call is rejected
|
|
// RPC_E_SERVERCALL_RETRYLATER - the call should be retried
|
|
// (Timer is set if retry is to be delayed)
|
|
//
|
|
// Algorithm: Calls the app's message filter (if there is one) to
|
|
// determine whether the call should be failed, retried
|
|
// immediately, or retried at some later time. If there is
|
|
// no message filter, or the client is on a different machine,
|
|
// then the call is always rejected.
|
|
//
|
|
// History: 21-Dec-93 Johannp Created
|
|
// 30-Apr-95 Rickhi ReWrite
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
INTERNAL CCliModalLoop::HandleRejectedCall(IRpcChannelBuffer2 *pChnl)
|
|
{
|
|
// default return value - rejected
|
|
DWORD dwRet = 0xffffffff;
|
|
|
|
DWORD dwDestCtx;
|
|
HRESULT hr = pChnl->GetDestCtx(&dwDestCtx, NULL);
|
|
|
|
if (SUCCEEDED(hr) && dwDestCtx != MSHCTX_DIFFERENTMACHINE)
|
|
{
|
|
// the call is local to this machine, ask the message filter
|
|
// what to do. For remote calls we never allow retry, since
|
|
// the parameters were not sent back to us in the packet.
|
|
|
|
IMessageFilter *pMF = _pACC->GetMsgFilter();
|
|
if (pMF)
|
|
{
|
|
ComDebOut((DEB_MFILTER,
|
|
"pMF->RetryRejectedCall(dwTIDCallee:%x ElapsedTime:%x Type:%x)\n",
|
|
_dwTIDCallee, GetElapsedTime(),
|
|
(_hr == RPC_E_CALL_REJECTED) ? SERVERCALL_REJECTED
|
|
: SERVERCALL_RETRYLATER));
|
|
|
|
dwRet = pMF->RetryRejectedCall((MF_HTASK)_dwTIDCallee, GetElapsedTime(),
|
|
(_hr == RPC_E_CALL_REJECTED) ? SERVERCALL_REJECTED
|
|
: SERVERCALL_RETRYLATER);
|
|
|
|
ComDebOut((DEB_MFILTER,"pMF->RetryRejected() dwRet:%x\n", dwRet));
|
|
|
|
_pACC->ReleaseMsgFilter();
|
|
}
|
|
}
|
|
|
|
if (dwRet == 0xffffffff)
|
|
{
|
|
// Really rejected. Mark it as such incase it was actually
|
|
// Call_RetryLater, also ensures that IsWaiting returns FALSE
|
|
return RPC_E_CALL_REJECTED;
|
|
}
|
|
else if (dwRet >= 100)
|
|
{
|
|
// Retry Later. Start the timer. This ensures that IsTimerAtZero
|
|
// returns FALSE and IsWaiting returns TRUE
|
|
return StartTimer(dwRet);
|
|
}
|
|
else
|
|
{
|
|
// Retry Immediately. The state is set so that IsTimerAtZero
|
|
// returns TRUE.
|
|
|
|
Win4Assert(IsTimerAtZero());
|
|
return RPC_E_SERVERCALL_RETRYLATER;
|
|
}
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: OleModalLoopBlockFn
|
|
//
|
|
// Synopsis: Called by the RpcChannel during an outgoing call while
|
|
// waiting for the reply message.
|
|
//
|
|
// Arguments: [pvWnd] - Window handle to expect the reply on
|
|
// [pvCtx] - Call Context (the CCliModalLoop)
|
|
// [hCallWaitEvent] - optional event to have CallControl wait on
|
|
//
|
|
// Returns: result of the call
|
|
//
|
|
// Algorithm: pvCtx is the topmost modal loop for the current apartment.
|
|
// Just call it's block function.
|
|
//
|
|
// History: Dec-93 JohannP Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
RPC_STATUS OleModalLoopBlockFn(void *pvWnd, void *pvCtx, HANDLE hCallWaitEvent)
|
|
{
|
|
Win4Assert( pvCtx != NULL );
|
|
return ((CCliModalLoop *) pvCtx)->BlockFn(hCallWaitEvent);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CCliModalLoop::BlockFn (private)
|
|
//
|
|
// Synopsis: Implements the blocking part of the modal loop. This function
|
|
// blocks until an event of interest occurs, then it goes and
|
|
// processes that event and returns.
|
|
//
|
|
// Arguments: [hCallWaitEvent] - event to wait on (optional)
|
|
//
|
|
// Returns: RPC_S_CALLPENDING - the call is still pending a reply
|
|
// RPC_E_CALL_CANCELLED - the call was cancelled.
|
|
// RPC_E_SERVERCALL_RETRYLATER - the call should be retried later
|
|
//
|
|
// History: Dec-93 JohannP Created
|
|
// 30-Apr-95 Rickhi ReWrite
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
HRESULT CCliModalLoop::BlockFn(HANDLE hEventCallComplete)
|
|
{
|
|
ComDebOut((DEB_CALLCONT,
|
|
"CCliModalLoop::BlockFn this:%x dwMsgQInputFlag:%x hEvent:%x\n",
|
|
this, _dwMsgQInputFlag, hEventCallComplete));
|
|
|
|
Win4Assert(IsWaiting() && "ModalLoop::BlockFn - not waiting on call");
|
|
|
|
// First, we wait for an event of interest to occur, either for the call
|
|
// to complete, or a new windows message to arrive on the queue.
|
|
|
|
DWORD dwWakeReason = WAIT_TIMEOUT;
|
|
HANDLE rgEvents[1] = {hEventCallComplete};
|
|
DWORD cEvents = 0;
|
|
|
|
if (hEventCallComplete != NULL)
|
|
{
|
|
// Check if the event is already signalled. This ensures that
|
|
// when we return from nested calls and the upper calls have already
|
|
// been acknowledged, that no windows messages can come in.
|
|
|
|
ComDebOut((DEB_CALLCONT, "WaitForSingleObject hEvent:%x\n", rgEvents[0]));
|
|
cEvents = 1;
|
|
dwWakeReason = WaitForSingleObject(rgEvents[0], 0);
|
|
}
|
|
|
|
if (dwWakeReason == WAIT_TIMEOUT)
|
|
{
|
|
DWORD dwWaitTime = TicksToWait();
|
|
|
|
// If we want to wake up for a posted message, we need to make
|
|
// sure that we haven't missed any because of the queue status
|
|
// being affected by prior PeekMessages. We don't worry about
|
|
// QS_SENDMESSAGE because if PeekMessage got called, the pending
|
|
// send got dispatched. Further, if we are in an input sync call,
|
|
// we don't want to start dispatching regular RPC calls here by
|
|
// accident.
|
|
|
|
if (_dwMsgQInputFlag & QS_POSTMESSAGE)
|
|
{
|
|
DWORD dwStatus = GetQueueStatus(_dwMsgQInputFlag);
|
|
|
|
// We care about any message on the queue not just new messages
|
|
// because PeekMessage affects the queue state. It resets the
|
|
// state so even if a message is not processed, the queue state
|
|
// represents this as an old message even though no one has
|
|
// ever looked at it. So even though the message queue tells us
|
|
// there are no new messages in the queue. A new message we are
|
|
// interested in could be in the queue.
|
|
|
|
WORD wNew = (WORD) dwStatus | HIWORD(dwStatus);
|
|
|
|
// Note that we look for send as well as post because our
|
|
// queue status could have reset the state of the send message
|
|
// bit and therefore, MsgWaitForMultipleObject below will not
|
|
// wake up to dispatch the send message.
|
|
|
|
if (wNew & (QS_POSTMESSAGE | QS_SENDMESSAGE))
|
|
{
|
|
// the acknowledge message might be already in the queue
|
|
if (PeekRPCAndDDEMessage())
|
|
{
|
|
// we know that *some* RPC message came in and was
|
|
// processed. It could have been the Reply we were waiting
|
|
// for OR some other incoming call. Since we cant tell
|
|
// which, we return to RPC land. If it was not our Reply
|
|
// then RPC will call our modal loop again.
|
|
return _hr;
|
|
}
|
|
}
|
|
|
|
#ifdef _CHICAGO_
|
|
//Note:POSTPPC
|
|
WORD wOld = HIWORD(dwStatus);
|
|
|
|
if (wOld & (QS_POSTMESSAGE))
|
|
{
|
|
ComDebOut((DEB_CALLCONT, "Set timeout time to 100\n"));
|
|
dwWaitTime = 100;
|
|
}
|
|
#endif //_CHICAGO_
|
|
}
|
|
|
|
ComDebOut((DEB_CALLCONT,
|
|
"Call MsgWaitForMultiple time:%ld, cEvents:%x hEvent:%x,\n",
|
|
dwWaitTime, cEvents, rgEvents[0] ));
|
|
|
|
dwWakeReason = MsgWaitForMultipleObjects(cEvents, rgEvents, FALSE,
|
|
dwWaitTime, _dwMsgQInputFlag);
|
|
|
|
ComDebOut((DEB_CALLCONT,
|
|
"MsgWaitForMultipleObjects hr:%ld\n", dwWakeReason));
|
|
}
|
|
|
|
|
|
// OK, we've done whatever blocking we were going to do and now we have
|
|
// been woken up, so figure out what event of interest occured to wake
|
|
// us up and go handle it.
|
|
|
|
if (dwWakeReason == (WAIT_OBJECT_0 + cEvents))
|
|
{
|
|
// Windows message came in - go process it
|
|
ComDebOut((DEB_CALLCONT, "BlockFn: Windows Message Arrived\n"));
|
|
HandleWakeForMsg();
|
|
}
|
|
else if (dwWakeReason == WAIT_TIMEOUT)
|
|
{
|
|
if (_hr == RPC_S_WAITONTIMER && IsTimerAtZero())
|
|
{
|
|
// The Retrytimer timed out - just exit and retransmit the call
|
|
ComDebOut((DEB_CALLCONT, "BlockFn: Timer at zero\n"));
|
|
_hr = RPC_E_SERVERCALL_RETRYLATER;
|
|
}
|
|
else
|
|
{
|
|
// we may have missed a message before we called MsgWaitForMult...
|
|
// so we go check now for any incoming messages.
|
|
ComDebOut((DEB_CALLCONT, "BlockFn: Timeout-Look for msgs\n"));
|
|
HandleWakeForMsg();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// CallComplete signalled - the call is done.
|
|
Win4Assert(rgEvents[dwWakeReason - WAIT_OBJECT_0] == hEventCallComplete);
|
|
ComDebOut((DEB_CALLCONT, "BlockFn: CallComplete Event Signaled\n"));
|
|
_hr = S_OK;
|
|
}
|
|
|
|
ComDebOut((DEB_CALLCONT, "CCliModalLoop::BlockFn this:%x returns:%x\n",
|
|
this, _hr));
|
|
return _hr;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CCliModalLoop::HandleWakeForMsg (private)
|
|
//
|
|
// Synopsis: Handle wake for the arrival of some kind of message
|
|
//
|
|
// Returns: nothing
|
|
// fClearedQueue flag set if appropriate
|
|
//
|
|
// Algorithm: If this is called to wake up for a posted message, we
|
|
// check the queue status. If the message queue status indicates
|
|
// that there is some kind of a modal loop going on, then we
|
|
// clear all the keyboard and mouse messages in our queue. Then
|
|
// if we wake up for all input, we check the message queue to
|
|
// see whether we need to notify the application that a message
|
|
// has arrived. Then, we dispatch any messages that have to do
|
|
// with the ORPC system. Finally we yield just in case we need
|
|
// to dispatch a send message in the VDM. For an input sync
|
|
// RPC, all we do is a call that will yield to get the pending
|
|
// send message dispatched.
|
|
//
|
|
// History: Dec-93 JohannP Created
|
|
// 13-Aug-94 Ricksa Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
INTERNAL_(void) CCliModalLoop::HandleWakeForMsg()
|
|
{
|
|
MSG msg; // Used for various peeks.
|
|
|
|
// Is this an input sync call?
|
|
if (_dwMsgQInputFlag != QS_SENDMESSAGE)
|
|
{
|
|
// No, so we have to worry about the state of the message queue.
|
|
// We have to be careful that we aren't holding the input focus
|
|
// on an input synchronized queue.
|
|
|
|
// So what is the state of the queue? - note we or QS_TRANSFER because
|
|
// this an undocumented flag which tells us the the input focus has
|
|
// changed to us.
|
|
|
|
DWORD dwQueueFlags = GetQueueStatus(QS_ALLINPUT | QS_TRANSFER);
|
|
ComDebOut((DEB_CALLCONT, "Queue Status %lx\n", dwQueueFlags));
|
|
|
|
// Call through to the application if we are going to. We do this here
|
|
// so that the application gets a chance to process any
|
|
// messages that it wants to and also allows the call control to
|
|
// dispatch certain messages that it knows how to, thus making the
|
|
// queue more empty.
|
|
|
|
if (((_dwMsgQInputFlag & QS_ALLINPUT) == QS_ALLINPUT) &&
|
|
FindMessage(dwQueueFlags))
|
|
{
|
|
// pending message in the queue
|
|
HandlePendingMessage();
|
|
}
|
|
|
|
// Did the input focus change to us?
|
|
if ((LOWORD(dwQueueFlags) & QS_TRANSFER) || _dwFlags & CMLF_CLEAREDQUEUE)
|
|
{
|
|
ComDebOut((DEB_CALLCONT, "Message Queue is being cleared\n"));
|
|
_dwFlags |= CMLF_CLEAREDQUEUE;
|
|
|
|
// Try to clear the queue as best we can of any messages that
|
|
// might be holding off some other modal loop from executing.
|
|
// So we eat all mouse and key events.
|
|
if (HIWORD(dwQueueFlags) & QS_KEY)
|
|
{
|
|
while (MyPeekMessage(&msg, NULL, WM_KEYFIRST, WM_KEYLAST,
|
|
PM_REMOVE | PM_NOYIELD))
|
|
{
|
|
;
|
|
}
|
|
}
|
|
|
|
// Clear mouse releated messages if there are any
|
|
if (HIWORD(dwQueueFlags) & QS_MOUSE)
|
|
{
|
|
while (MyPeekMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST,
|
|
PM_REMOVE | PM_NOYIELD))
|
|
{
|
|
;
|
|
}
|
|
|
|
while (MyPeekMessage(&msg, NULL, WM_NCMOUSEFIRST,
|
|
WM_NCMOUSELAST, PM_REMOVE | PM_NOYIELD))
|
|
{
|
|
;
|
|
}
|
|
|
|
while (MyPeekMessage(&msg, NULL, WM_QUEUESYNC, WM_QUEUESYNC,
|
|
PM_REMOVE | PM_NOYIELD))
|
|
{
|
|
;
|
|
}
|
|
}
|
|
|
|
// Get rid of paint message if we can as well -- this makes
|
|
// the screen look so much better.
|
|
if (HIWORD(dwQueueFlags) & QS_PAINT)
|
|
{
|
|
if (MyPeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE | PM_NOYIELD))
|
|
{
|
|
ComDebOut((DEB_CALLCONT, "Dispatch paint\n"));
|
|
DispatchMessage(&msg);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (!IsWOWThread() || !IsWOWThreadCallable())
|
|
{
|
|
// We need to give user control so that the send message
|
|
// can get dispatched. Thus the following is simply a no-op
|
|
// which gets into user to let it dispatch the message.
|
|
PeekMessage(&msg, 0, WM_NULL, WM_NULL, PM_NOREMOVE);
|
|
}
|
|
|
|
if (IsWOWThread() && IsWOWThreadCallable())
|
|
{
|
|
// In WOW, a genuine yield is the only thing to guarantee
|
|
// that SendMessage will get through
|
|
g_pOleThunkWOW->YieldTask16();
|
|
}
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CCliModalLoop::PeekRPCAndDDEMessage
|
|
//
|
|
// Synopsis: Called when a windows message arrives to look for incoming
|
|
// Rpc messages which might be the reply to an outstanding call
|
|
// or may be new incoming request messages. Also looks for
|
|
// DDE messages.
|
|
//
|
|
// Returns: TRUE - found and processed an RPC message
|
|
// FALSE - did not find an RPC message
|
|
//
|
|
// History: 21-Dec-93 JohannP Created
|
|
// 30-Apr-95 Rickhi ReWrite
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
BOOL CCliModalLoop::PeekRPCAndDDEMessage()
|
|
{
|
|
// loop over all windows looking for incoming Rpc messages. Note that
|
|
// it is possible for a dispatch here to cause one of the windows to
|
|
// be deregistered or another to be registered, so our loop has to account
|
|
// for that, hence the check for NULL hWnd.
|
|
|
|
BOOL fRet = FALSE;
|
|
MSG Msg;
|
|
|
|
for (UINT i = 0; i < 2; i++)
|
|
{
|
|
// get window info and peek on it if the hWnd is still OK
|
|
SWindowData *pWD = _pACC->GetWindowData(i);
|
|
|
|
if (pWD->hWnd != WD_EMPTY)
|
|
{
|
|
if (MyPeekMessage(&Msg, pWD->hWnd, pWD->wFirstMsg, pWD->wLastMsg,
|
|
PM_REMOVE | PM_NOYIELD))
|
|
{
|
|
Win4Assert(IsWaiting());
|
|
DispatchMessage(&Msg);
|
|
|
|
// exit on the first dispatched message. If the message was
|
|
// not the reply we were waiting for, then the channel will
|
|
// call us back again.
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CCliModalLoop::FindMessage
|
|
//
|
|
// Synopsis: Called by HandleWakeForMsg when a message arrives on the
|
|
// windows msg queue. Determines if there is something of
|
|
// interest to us, and pulls timer msgs. Dispatches RPC, DDE,
|
|
// and RPC timer messages.
|
|
//
|
|
// Arguments: [dwStatus] - current Queue status (from GetQueueStatus)
|
|
//
|
|
// Returns: TRUE - there is a message to process
|
|
// FALSE - no messages to process
|
|
//
|
|
// Algorithm: Find the next message in the queue by using the following
|
|
// priority list:
|
|
//
|
|
// 1. RPC and DDE messages
|
|
// 2. mouse and keyboard messages
|
|
// 3. other messages
|
|
//
|
|
// History: 21-Dec-93 Johannp Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
INTERNAL_(BOOL) CCliModalLoop::FindMessage(DWORD dwStatus)
|
|
{
|
|
WORD wOld = HIWORD(dwStatus);
|
|
WORD wNew = (WORD) dwStatus;
|
|
|
|
if (!wNew)
|
|
{
|
|
if (!(wOld & QS_POSTMESSAGE))
|
|
return FALSE; // no messages to take care of
|
|
else
|
|
wNew |= QS_POSTMESSAGE;
|
|
}
|
|
|
|
MSG Msg;
|
|
|
|
// Priority 1: look for RPC and DDE messages
|
|
if (wNew & (QS_POSTMESSAGE | QS_SENDMESSAGE | QS_TIMER))
|
|
{
|
|
if (PeekRPCAndDDEMessage())
|
|
{
|
|
// we know that *some* RPC message came in, might be our
|
|
// reply or may be some incoming call. In any case, return to
|
|
// the modal loop to guy so we can figure out if we need to
|
|
// keep going.
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (wNew & QS_TIMER)
|
|
{
|
|
// throw the system timer messages away
|
|
while (MyPeekMessage(&Msg, 0, WM_SYSTIMER, WM_SYSTIMER, PM_REMOVE | PM_NOYIELD))
|
|
;
|
|
}
|
|
|
|
// Priority 2: messages from the hardware queue
|
|
if (wNew & (QS_KEY | QS_MOUSEMOVE | QS_MOUSEBUTTON))
|
|
{
|
|
return TRUE; // these messages are always removed
|
|
}
|
|
else if (wNew & QS_TIMER)
|
|
{
|
|
if (MyPeekMessage(&Msg, 0, WM_TIMER, WM_TIMER, PM_NOREMOVE | PM_NOYIELD) )
|
|
return TRUE;
|
|
}
|
|
else if (wNew & QS_PAINT)
|
|
{
|
|
return TRUE; // this message might not get removed
|
|
}
|
|
else if (wNew & (QS_POSTMESSAGE | QS_SENDMESSAGE))
|
|
{
|
|
if (MyPeekMessage(&Msg, 0, 0, 0, PM_NOREMOVE))
|
|
return TRUE; // Priority 3: all other messages
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CCliModalLoop::HandlePendingMessage
|
|
//
|
|
// Synopsis: this function is called for system messages and other
|
|
// pending messages
|
|
//
|
|
// Arguments: none
|
|
//
|
|
// Returns: nothing, _hr may be updated if call is cancelled.
|
|
//
|
|
// Algorithm:
|
|
//
|
|
// History: 21-Dec-93 Johannp Created
|
|
// 30-Apr-95 Rickhi ReWrite
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
INTERNAL_(void) CCliModalLoop::HandlePendingMessage()
|
|
{
|
|
// get and call the message filter if there is one
|
|
IMessageFilter *pMF = _pACC->GetMsgFilter();
|
|
|
|
if (pMF)
|
|
{
|
|
ComDebOut((DEB_MFILTER,
|
|
"pMF->MessagePending(dwTIDCallee:%x ElapsedTime:%x Type:%x)\n",
|
|
_dwTIDCallee, GetElapsedTime(),
|
|
(_pPrev) ? PENDINGTYPE_NESTED : PENDINGTYPE_TOPLEVEL));
|
|
|
|
DWORD dwRet = pMF->MessagePending((MF_HTASK)_dwTIDCallee,
|
|
GetElapsedTime(),
|
|
(_pPrev) ? PENDINGTYPE_NESTED
|
|
: PENDINGTYPE_TOPLEVEL);
|
|
|
|
ComDebOut((DEB_MFILTER,"pMF->MessagePending() dwRet:%x\n", dwRet));
|
|
|
|
|
|
_pACC->ReleaseMsgFilter();
|
|
|
|
if (dwRet == PENDINGMSG_CANCELCALL)
|
|
{
|
|
_hr = RPC_E_CALL_CANCELED;
|
|
return;
|
|
}
|
|
|
|
Win4Assert((dwRet == PENDINGMSG_WAITDEFPROCESS ||
|
|
dwRet == PENDINGMSG_WAITNOPROCESS) &&
|
|
"Invalid return value from pMF->MessagePending");
|
|
}
|
|
|
|
// if we get here we are going to do the default message processing.
|
|
// Default Processing: Continue to wait for the call return and
|
|
// don't dispatch the new message. Perform default processing on
|
|
// special system messages.
|
|
|
|
MSG msg;
|
|
|
|
// we have to take out all syscommand messages
|
|
if (MyPeekMessage(&msg, 0, WM_SYSCOMMAND, WM_SYSCOMMAND, PM_REMOVE | PM_NOYIELD))
|
|
{
|
|
// only dispatch some syscommands
|
|
if (msg.wParam == SC_HOTKEY || msg.wParam == SC_TASKLIST)
|
|
{
|
|
ComDebOut((DEB_CALLCONT,">>>> Dispatching SYSCOMMAND message: %x; wParm: %x \r\n",msg.message, msg.wParam));
|
|
DispatchMessage(&msg);
|
|
}
|
|
else
|
|
{
|
|
ComDebOut((DEB_CALLCONT,">>>> Received/discarded SYSCOMMAND message: %x; wParm: %x \r\n",msg.message, msg.wParam));
|
|
MessageBeep(0);
|
|
}
|
|
}
|
|
else if (MyPeekMessage(&msg, 0, WM_SYSKEYDOWN, WM_SYSKEYDOWN, PM_NOREMOVE | PM_NOYIELD))
|
|
{
|
|
if (msg.message == WM_KEYDOWN)
|
|
{
|
|
if (msg.wParam != VK_CONTROL && msg.wParam != VK_SHIFT)
|
|
MessageBeep(0);
|
|
}
|
|
else if (msg.message == WM_SYSKEYDOWN && msg.lParam & SYS_ALTDOWN &&
|
|
(msg.wParam == VK_TAB || msg.wParam == VK_ESCAPE))
|
|
{
|
|
MyPeekMessage(&msg, 0, WM_SYSKEYDOWN, WM_SYSKEYDOWN, PM_REMOVE | PM_NOYIELD);
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
}
|
|
else if (MyPeekMessage(&msg, 0, WM_ACTIVATE, WM_ACTIVATE, PM_REMOVE | PM_NOYIELD)
|
|
|| MyPeekMessage(&msg, 0, WM_ACTIVATEAPP, WM_ACTIVATEAPP, PM_REMOVE | PM_NOYIELD)
|
|
|| MyPeekMessage(&msg, 0, WM_NCACTIVATE, WM_NCACTIVATE, PM_REMOVE | PM_NOYIELD) )
|
|
{
|
|
DispatchMessage(&msg);
|
|
}
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CCliModalLoop::MyPeekMessage
|
|
//
|
|
// Synopsis: This function is called whenever we want to do a PeekMessage.
|
|
// It intercepts WM_QUIT messages and remembers them so that
|
|
// they can be reposted when the modal loop is exited.
|
|
//
|
|
// Arguments: [pMsg] - message structure
|
|
// [hWnd] - window to peek on
|
|
// [min/max] - min and max message numbers
|
|
// [wFlag] - peek flags
|
|
//
|
|
// Returns: TRUE - a message is available
|
|
// FALSE - no messages available
|
|
//
|
|
// History: 21-Dec-93 Johannp Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
INTERNAL_(BOOL) CCliModalLoop::MyPeekMessage(MSG *pMsg, HWND hwnd,
|
|
UINT min, UINT max, WORD wFlag)
|
|
{
|
|
BOOL fRet = PeekMessage(pMsg, hwnd, min, max, wFlag);
|
|
|
|
while (fRet)
|
|
{
|
|
ComDebOut((DEB_CALLCONT, "MyPeekMessage: hwnd:%x msg:%d time:%ld\n",
|
|
pMsg->hwnd, pMsg->message, pMsg->time));
|
|
|
|
if (pMsg->message != WM_QUIT)
|
|
{
|
|
// it is not a QUIT message so exit the loop and return TRUE
|
|
break;
|
|
}
|
|
|
|
// just remember that we saw a QUIT message. we will ignore it for
|
|
// now and repost it after our call has completed.
|
|
|
|
ComDebOut((DEB_CALLCONT, "WM_QUIT received.\n"));
|
|
_wQuitCode = pMsg->wParam;
|
|
_dwFlags |= CMLF_QUITRECEIVED;
|
|
|
|
if (!(wFlag & PM_REMOVE)) // NOTE: dont use PM_NOREMOVE
|
|
{
|
|
// quit message is still on queue so pull it off
|
|
PeekMessage(pMsg, hwnd, WM_QUIT, WM_QUIT, PM_REMOVE | PM_NOYIELD);
|
|
}
|
|
|
|
// peek again to see if there is another message
|
|
fRet = PeekMessage(pMsg, hwnd, min, max, wFlag);
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
#if DBG==1
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CCliModalLoop::DispatchMessage
|
|
//
|
|
// Synopsis: This function is called whenever we want to dispatch a
|
|
// message we have peeked. It is just a debug wrapper to provide
|
|
// debug out statements about dispatched messages.
|
|
//
|
|
// Arguments: [pMsg] - message structure
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
INTERNAL_(void) CCliModalLoop::DispatchMessage(MSG *pMsg)
|
|
{
|
|
ComDebOut((DEB_CALLCONT, "Dispatching Message hWnd:%x msg:%d wParam:%x\n",
|
|
pMsg->hwnd, pMsg->message, pMsg->wParam));
|
|
|
|
::DispatchMessage(pMsg);
|
|
}
|
|
#endif
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CCliModalLoop::GetElapsedTime
|
|
//
|
|
// Synopsis: Get the elapsed time for an RPC call
|
|
//
|
|
// Returns: Elapsed time of current call
|
|
//
|
|
// Algorithm: This checks whether we have the slow time factor. If not,
|
|
// and we are in WOW we read it from the registry. Otherwise,
|
|
// it is just set to one. Then we calculate the time of the
|
|
// RPC call and divide it by the slow time factor.
|
|
//
|
|
// History: 22-Jul-94 Ricksa Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
INTERNAL_(DWORD) CCliModalLoop::GetElapsedTime()
|
|
{
|
|
// Define slow time factor to something invalid
|
|
static dwSlowTimeFactor = 0;
|
|
|
|
if (dwSlowTimeFactor == 0)
|
|
{
|
|
if (IsWOWProcess())
|
|
{
|
|
// Get time factor from registry otherwise set to the default
|
|
dwSlowTimeFactor = GetSlowTimeFactor();
|
|
}
|
|
else
|
|
{
|
|
// Time is unmodified for 32 bit apps
|
|
dwSlowTimeFactor = 1;
|
|
}
|
|
}
|
|
|
|
DWORD dwTickCount = GetTickCount();
|
|
DWORD dwElapsedTime = dwTickCount - _dwTimeOfCall;
|
|
if (dwTickCount < _dwTimeOfCall)
|
|
{
|
|
// the timer wrapped
|
|
dwElapsedTime = 0xffffffff - _dwTimeOfCall + dwTickCount;
|
|
}
|
|
|
|
return (dwElapsedTime / dwSlowTimeFactor);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CCliModalLoop::FindPrevCallOnLID [server side]
|
|
//
|
|
// Synopsis: When an incoming call arrives this is used to find any
|
|
// previous call for the same logical thread, ignoring
|
|
// INTERNAL calls. The result is used to determine if this
|
|
// is a nested call or not.
|
|
//
|
|
// Arguments: [lid] - logical threadid of incoming call
|
|
//
|
|
// Returns: pCML - if a previous CliModalLoop found for this lid
|
|
// NULL - otherwise
|
|
//
|
|
// Algorithm: just walk backwards on the _pPrev chain
|
|
//
|
|
// History: 17-Dec-93 JohannP Created
|
|
// 30-Apr-95 Rickhi ReWrite
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
CCliModalLoop *CCliModalLoop::FindPrevCallOnLID(REFLID lid)
|
|
{
|
|
CCliModalLoop *pCML = this;
|
|
|
|
do
|
|
{
|
|
if (pCML->_lid == lid)
|
|
{
|
|
break; // found a match, return it
|
|
}
|
|
|
|
} while ((pCML = pCML->_pPrev) != NULL);
|
|
|
|
return pCML;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: STAInvoke
|
|
//
|
|
// Synopsis: Called whenever an incoming call arrives in a single-threaded
|
|
// apartment. It asks the apps message filter (if there is one)
|
|
// whether it wants to handle the call or not, and dispatches
|
|
// the call if OK.
|
|
//
|
|
// Arguments: [pMsg] - Incoming Rpc message
|
|
// [pStub] - stub to call if MF says it is OK
|
|
// [pChnl] - channel ptr to give to stub
|
|
// [pv] - real interface being called
|
|
// [pdwFault] - where to store fault code if there is a fault
|
|
//
|
|
// Returns: result for MF or from call to stub
|
|
//
|
|
// History: 21-Dec-93 Johannp Original Version
|
|
// 22-Jul-94 Rickhi ReWrite
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
INTERNAL STAInvoke(RPCOLEMESSAGE *pMsg, DWORD CallCatIn, IRpcStubBuffer *pStub,
|
|
IRpcChannelBuffer *pChnl, void *pv, DWORD *pdwFault)
|
|
{
|
|
ComDebOut((DEB_CALLCONT,
|
|
"STAInvoke pMsg:%x CallCatIn:%x pStub:%x pChnl:%x\n",
|
|
pMsg, CallCatIn, pStub, pChnl));
|
|
|
|
HRESULT hr = HandleIncomingCall(*MSG_TO_IIDPTR(pMsg),
|
|
(WORD)pMsg->iMethod,
|
|
CallCatIn, pv);
|
|
if (hr == S_OK)
|
|
{
|
|
// the message filter says its OK to invoke the call.
|
|
|
|
// construct a server call state. This puts the current incoming
|
|
// call's CallCat in Tls so we can check it if the server tries to
|
|
// make an outgoing call while handling this call. See CanMakeOutCall.
|
|
CSrvCallState SCS(CallCatIn);
|
|
|
|
// invoke the call
|
|
hr = MTAInvoke(pMsg, CallCatIn, pStub, pChnl, pdwFault);
|
|
}
|
|
else if (hr == RPC_E_CALL_REJECTED || hr == RPC_E_SERVERCALL_RETRYLATER)
|
|
{
|
|
// server is rejecting the call, try to copy the incomming buffer so
|
|
// that the client has the option of retrying the call.
|
|
hr = CopyMsgForRetry(pMsg, pChnl, hr);
|
|
}
|
|
|
|
ComDebOut((DEB_CALLCONT,"STAInvoke returns:%x\n",hr));
|
|
return hr;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: HandleIncomingCall, internal
|
|
//
|
|
// Synopsis: Called whenever an incoming call arrives in a single-threaded
|
|
// apartment. It asks the app's message filter (if there is one)
|
|
// whether it wants to handle the call or not
|
|
//
|
|
// Arguments: [piid] - ptr to interface the call is being made on
|
|
// [iMethod] - method number being called
|
|
// [CallCatIn] - category of incoming call
|
|
// [pv] - real interface being called
|
|
//
|
|
// Returns: result from MF
|
|
//
|
|
// History: 11-Oct-96 Rickhi Separated from STAInvoke
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
INTERNAL HandleIncomingCall(REFIID riid, WORD iMethod, DWORD CallCatIn, void *pv)
|
|
{
|
|
ComDebOut((DEB_CALLCONT,
|
|
"HandleIncomingCall iid:%I iMethod:%x CallCatIn:%x pv:%x:%x\n",
|
|
&riid, iMethod, CallCatIn, pv));
|
|
|
|
COleTls tls;
|
|
if (!(tls->dwFlags & OLETLS_APARTMENTTHREADED))
|
|
{
|
|
// free-threaded apartments don't have a message filter
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
CAptCallCtrl *pACC = tls->pCallCtrl;
|
|
|
|
|
|
// We dont call the message filter for IUnknown since older versions
|
|
// of OLE did not, and doing so (unfortunately) breaks compatibility.
|
|
// Also check for IRundown since local clients call on it instead of
|
|
// IRemUnknown.
|
|
|
|
IMessageFilter *pMF = (riid == IID_IRundown || riid == IID_IRemUnknown)
|
|
? NULL : pACC->GetMsgFilter();
|
|
|
|
if (pMF)
|
|
{
|
|
// the app has installed a message filter, call it.
|
|
|
|
INTERFACEINFO IfInfo;
|
|
IfInfo.pUnk = (IUnknown *)pv;
|
|
IfInfo.iid = riid;
|
|
IfInfo.wMethod = iMethod;
|
|
|
|
ComDebOut((DEB_CALLCONT, "Calling iMethod:%x riid:%I\n",
|
|
IfInfo.wMethod, &IfInfo.iid));
|
|
|
|
CCliModalLoop *pCML = NULL;
|
|
REFLID lid = tls->LogicalThreadId;
|
|
DWORD TIDCaller = tls->dwTIDCaller;
|
|
|
|
DWORD dwCallType = pACC->GetCallTypeForInCall(&pCML, lid, CallCatIn);
|
|
DWORD dwElapsedTime = (pCML) ? pCML->GetElapsedTime() : 0;
|
|
|
|
// The DDE layer doesn't provide any interface information. This
|
|
// was true on the 16-bit implementation, and has also been
|
|
// brought forward into this implementation to insure
|
|
// compatibility. However, the CallCat of the IfInfo is still
|
|
// provided.
|
|
//
|
|
// Therefore, if pIfInfo has its pUnk member set to NULL, then
|
|
// we are going to send a NULL pIfInfo to the message filter.
|
|
|
|
ComDebOut((DEB_MFILTER,
|
|
"pMF->HandleIncomingCall(dwCallType:%x TIDCaller:%x dwElapsedTime:%x IfInfo:%x)\n",
|
|
dwCallType, TIDCaller, dwElapsedTime, (IfInfo.pUnk) ? &IfInfo : NULL));
|
|
|
|
DWORD dwRet = pMF->HandleInComingCall(dwCallType,
|
|
(MF_HTASK)TIDCaller,
|
|
dwElapsedTime,
|
|
IfInfo.pUnk ? &IfInfo : NULL);
|
|
|
|
ComDebOut((DEB_MFILTER,"pMF->HandleIncomingCall() dwRet:%x\n", dwRet));
|
|
|
|
pACC->ReleaseMsgFilter();
|
|
|
|
// strict checking of app return code for win32
|
|
Win4Assert(dwRet == SERVERCALL_ISHANDLED ||
|
|
dwRet == SERVERCALL_REJECTED ||
|
|
dwRet == SERVERCALL_RETRYLATER ||
|
|
IsWOWThread() && "Invalid Return code from App IMessageFilter");
|
|
|
|
|
|
if (dwRet != SERVERCALL_ISHANDLED)
|
|
{
|
|
if (CallCatIn == CALLCAT_ASYNC || CallCatIn == CALLCAT_INPUTSYNC)
|
|
{
|
|
// Note: input-sync and async calls can not be rejected
|
|
// Even though they can not be rejected, we still have to
|
|
// call the MF above to maintain 16bit compatability.
|
|
hr = S_OK;
|
|
}
|
|
else if (dwRet == SERVERCALL_REJECTED)
|
|
{
|
|
hr = RPC_E_CALL_REJECTED;
|
|
}
|
|
else if (dwRet == SERVERCALL_RETRYLATER)
|
|
{
|
|
hr = RPC_E_SERVERCALL_RETRYLATER;
|
|
}
|
|
else
|
|
{
|
|
// 16bit OLE let bogus return codes go through and of course
|
|
// apps rely on that behaviour so we let them through too, but
|
|
// we are more strict on 32bit.
|
|
hr = (IsWOWThread()) ? S_OK : RPC_E_UNEXPECTED;
|
|
}
|
|
}
|
|
}
|
|
|
|
ComDebOut((DEB_CALLCONT, "HandleIncomingCall hr:%x\n", hr));
|
|
return hr;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: MTAInvoke
|
|
//
|
|
// Synopsis: Multi-Threaded Apartment Invoke. Called whenever an incoming
|
|
// call arrives in the MTA apartment (or as a subroutine to
|
|
// STAInvoke). It just dispatches to a common sub-routine.
|
|
//
|
|
// Arguments: [pMsg] - Incoming Rpc message
|
|
// [pStub] - stub to call if MF says it is OK
|
|
// [pChnl] - channel ptr to give to stub
|
|
// [pdwFault] - where to store fault code if there is a fault
|
|
//
|
|
// Returns: result from calling the stub
|
|
//
|
|
// History: 03-Oct-95 Rickhi Made into subroutine from STAInvoke
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
INTERNAL MTAInvoke(RPCOLEMESSAGE *pMsg, DWORD CallCatIn, IRpcStubBuffer *pStub,
|
|
IRpcChannelBuffer *pChnl, DWORD *pdwFault)
|
|
{
|
|
#if DBG==1
|
|
ComDebOut((DEB_CALLCONT,
|
|
"MTAInvoke pMsg:%x CallCatIn:%x pStub:%x pChnl:%x\n",
|
|
pMsg, CallCatIn, pStub, pChnl));
|
|
IID *piid = MSG_TO_IIDPTR(pMsg);
|
|
DebugPrintORPCCall(ORPC_INVOKE_BEGIN, *piid, pMsg->iMethod, CallCatIn);
|
|
RpcSpy((CALLIN_BEGIN, NULL, *piid, pMsg->iMethod, 0));
|
|
#endif
|
|
|
|
// call a common subroutine to do the dispatch. The subroutine also
|
|
// catches exceptions and provides some debug help.
|
|
|
|
HRESULT hr = StubInvoke(pMsg, pStub, pChnl, pdwFault);
|
|
|
|
#if DBG==1
|
|
RpcSpy((CALLIN_END, NULL, *piid, pMsg->iMethod, hr));
|
|
DebugPrintORPCCall(ORPC_INVOKE_END, *piid, pMsg->iMethod, CallCatIn);
|
|
ComDebOut((DEB_CALLCONT,"MTAInvoke returns:%x\n",hr));
|
|
#endif
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: CopyMsgForRetry
|
|
//
|
|
// Synopsis: Makes a copy of the server-side message buffer to return to
|
|
// the client so that the client can retry the call later.
|
|
// Returns an error if the client is on a different machine.
|
|
//
|
|
// Parameters: [pMsg] - ptr to message to copy
|
|
// [pChnl] - ptr to channel call is being made on
|
|
// [hr] - result code
|
|
//
|
|
// History: 30-05-95 Rickhi Created
|
|
//
|
|
//+-------------------------------------------------------------------------
|
|
HRESULT CopyMsgForRetry(RPCOLEMESSAGE *pMsg, IRpcChannelBuffer *pChnl, HRESULT hrIn)
|
|
{
|
|
ComDebOut((DEB_CALLCONT,"CopyMsgForRetry pMsg:%x pChnl:%x pBuffer:%x\n",
|
|
pMsg, pChnl, pMsg->Buffer));
|
|
|
|
DWORD dwDestCtx;
|
|
HRESULT hr = pChnl->GetDestCtx(&dwDestCtx, NULL);
|
|
|
|
if (SUCCEEDED(hr) && dwDestCtx != MSHCTX_DIFFERENTMACHINE &&
|
|
!IsEqualGUID(IID_IObjServer, *MSG_TO_IIDPTR(pMsg)))
|
|
{
|
|
// client on same machine as server.
|
|
void *pSavedBuffer = pMsg->Buffer;
|
|
hr = pChnl->GetBuffer(pMsg, *MSG_TO_IIDPTR(pMsg));
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// copy original buffer to the new buffer
|
|
memcpy(pMsg->Buffer, pSavedBuffer, pMsg->cbBuffer);
|
|
hr = hrIn;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// client on different machine than server, or the call was on
|
|
// the activation interface, fail the call and dont send back
|
|
// a copy of the parameter packet.
|
|
hr = RPC_E_CALL_REJECTED;
|
|
}
|
|
|
|
ComDebOut((DEB_CALLCONT,"CopyMsgForRetry pBuffer:%x hr:%x\n",
|
|
pMsg->Buffer, hr));
|
|
return hr;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CAptCallCtrl::GetCallTypeForInCall
|
|
//
|
|
// Synopsis: called when an incoming call arrives in order to determine
|
|
// what CALLTYPE to pass to the applications message filter.
|
|
//
|
|
// Arguments: [ppCML] - Client Modal Loop of prev call on same lid (if any)
|
|
// [lid] - logical thread id of this call
|
|
// [dwCallCat] - call category of incoming call
|
|
//
|
|
// Returns: the CALLTYPE to give to the message filter
|
|
//
|
|
// History: 21-Dec-93 Johannp Created
|
|
// 30-Apr-95 Rickhi ReWrite
|
|
//
|
|
// Notes:
|
|
//
|
|
// 1 = CALLTYPE_TOPLEVEL // sync or inputsync call - no outgoing call
|
|
// 2 = CALLTYPE_NESTED // callback on behalf of previous outgoing call
|
|
// 3 = CALLTYPE_ASYNC // asynchronous call - no outstanding call
|
|
// 4 = CALLTYPE_TOPLEVEL_CALLPENDING // call with new LID - outstand call
|
|
// 5 = CALLTYPE_ASYNC_CALLPENDING // async call - outstanding call
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
DWORD CAptCallCtrl::GetCallTypeForInCall(CCliModalLoop **ppCML,
|
|
REFLID lid, DWORD dwCallCatIn)
|
|
{
|
|
DWORD CallType;
|
|
CCliModalLoop *pCML = GetTopCML();
|
|
|
|
if (dwCallCatIn == CALLCAT_ASYNC) // asynchronous call has arrived
|
|
{
|
|
if (pCML == NULL)
|
|
CallType = CALLTYPE_ASYNC; // no outstanding calls
|
|
else
|
|
CallType = CALLTYPE_ASYNC_CALLPENDING; // outstanding call
|
|
}
|
|
else // non-async call has arrived
|
|
{
|
|
if (pCML == NULL)
|
|
CallType = CALLTYPE_TOPLEVEL; // no outstanding call
|
|
else if ((*ppCML = pCML->FindPrevCallOnLID(lid)) != NULL)
|
|
CallType = CALLTYPE_NESTED; // outstanding call on same lid
|
|
else
|
|
CallType = CALLTYPE_TOPLEVEL_CALLPENDING; // different lid
|
|
}
|
|
|
|
ComDebOut((DEB_CALLCONT,"GetCallTypeForInCall return:%x\n", CallType));
|
|
return CallType;
|
|
}
|
|
|