NT4/private/ole32/com/remote/callmain.hxx
2020-09-30 17:12:29 +02:00

506 lines
13 KiB
C++

//+-------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1993.
//
// File: CallCont.hxx (32 bit target)
//
// Contents: Contains the CallControl interface
//
// Functions:
//
// History: 23-Dec-93 Johann Posch (johannp) Created
//
//--------------------------------------------------------------------------
#ifndef _CALLMAIN_HXX_
#define _CALLMAIN_HXX_
#include <dde.h>
#include <scode.h>
#include <objerror.h>
#include <sem.hxx>
#include <chancont.hxx>
// Private definition for change of input focus
#define QS_TRANSFER 0x4000
// Maximum time we will wait for a response on message wait for multiple.
// Note: this must be less than infinite because MsgWaitForMultipleObjects
// will not wake up on messages posted before it is called.
#define MAX_TICKS_TO_WAIT 1000
typedef enum tagCallState
{
// BUGBUG: rickhi: make these bitmasks and we can do computations faster.
// the following codes map with SERVERCALL
Call_Ok = 0, // call was successful
Call_Rejected = 1, // call was rejected by callee
Call_RetryLater = 2, // call was busy ack by callee
Call_Error = 3, // call was ack with errror
// internal call state codes
Call_WaitOnCall = 4, // call was transmited, wait for return
Call_Canceled = 5, // call was cancelled by caller
Call_WaitOnTimer = 6, // call is waiting on running timer
} CallState;
//
// to pass back an error from the callee
//
#define SERVERCALL_FIRST SERVERCALL_RETRYLATER
#define SERVERCALL_ERROR SERVERCALL_FIRST+1
#define SERVERCALL_REPOSTED SERVERCALL_FIRST+2
#define CALLTYPE_NOCALL (CALLTYPE)0
// bit values for call info flags
typedef enum tagCALLFLAGS
{
CIF_QUITRECEIVED = 1, // WM_QUIT was received
CIF_CLEAREDQUEUE = 2 // Cleared the Msg queue in the past
} CALLFLAGS;
class CCallInfo{
public:
CCallInfo(PCALLDATA pCalldata, PORIGINDATA pOrigindata)
{
Win4Assert(pCalldata && pOrigindata);
_pCalldata = pCalldata;
_pOrigindata = pOrigindata;
_dwFlags = 0; // all flags start FALSE
_tid = GetCurrentThreadId(); // used in MT case
_dwTimeOfCall = GetTickCount();
#if 0 // the following fields are initialized when the
// call is first xmited
_id = 0; // set by SetupModalLoop
_CallState = Call_WaitOnCall; // set by SetState
_hresult = S_OK;
// the following fields are initialized only if we go into
// a wait state
_dwWakeup = 0; // set by StartTimer
_dwMillSecToWait = 0;
// the following field is initialized only if we receive a quit msg
_wQuitCode = 0;
#endif
}
INTERNAL_(VOID) SetCallState (CallState callstate, SCODE scode)
{
_hresult = scode;
_CallState = callstate;
}
INTERNAL_(DWORD) GetCallState()
{
return _CallState;
}
// CODEWORK: ensure all callers of SetState do the right thing and this
// function can be a noop.
INTERNAL GetHresultOfCall()
{
switch (_CallState)
{
case Call_Ok:
case Call_Error:
break;
case Call_Canceled:
_hresult = RPC_E_CALL_CANCELED;
break;
case Call_Rejected:
case Call_RetryLater:
_hresult = RPC_E_CALL_REJECTED;
break;
default:
Win4Assert(!"CallInfo: invalid state at return");
break;
}
return _hresult;
}
INTERNAL_(BOOL) IsWaiting()
{
return (_CallState == Call_WaitOnCall || _CallState == Call_WaitOnTimer);
}
INTERNAL_(BOOL) IsRejected()
{
return (_CallState == Call_Rejected || _CallState == Call_RetryLater);
}
INTERNAL_(void) SetClearedQueue(void)
{
_dwFlags |= CIF_CLEAREDQUEUE;
}
INTERNAL_(BOOL) GetClearedQueue(void)
{
return _dwFlags & CIF_CLEAREDQUEUE;
}
INTERNAL_(void) SetQuitCode(UINT wParam)
{
_wQuitCode = wParam;
_dwFlags |= CIF_QUITRECEIVED;
}
INTERNAL_(void) HandleQuitCode()
{
if (_dwFlags & CIF_QUITRECEIVED)
{
CairoleDebugOut((DEB_CALLCONT, "posting WM_QUIT\n"));
PostQuitMessage(_wQuitCode);
}
}
INTERNAL_(BOOL) LookForAllMsg()
{
return _pCalldata->CallCat != CALLCAT_INPUTSYNC;
}
INTERNAL_(DWORD) GetTaskIdServer()
{
return _pCalldata->TIDCallee;
}
INTERNAL_(REFLID) GetLID()
{
return _pCalldata->lid;
}
INTERNAL_(DWORD) GetTID()
{
return _tid;
}
INTERNAL_(CALLCATEGORY) GetCallCat()
{
return _pCalldata->CallCat;
}
INTERNAL_(void) StartTimer(DWORD dwMilliSecToWait)
{
// set time when we should awake and retry the call. note that
// if the GetTickCount + dwMilliSecToWait wraps the timer, then
// we may wakeup earlier than expected, but at least we wont
// deadlock.
CairoleDebugOut((DEB_ERROR,
"Timer insstalled for %lu msec.\r\n", dwMilliSecToWait));
_dwMillSecToWait = dwMilliSecToWait;
_dwWakeup = GetTickCount() + dwMilliSecToWait;
SetCallState(Call_WaitOnTimer, S_OK);
}
INTERNAL_(void) ClearTimer()
{
_dwMillSecToWait = _dwWakeup = 0;
}
INTERNAL_(BOOL) IsTimerAtZero()
{
// if no timer installed, return TRUE
if (_CallState != Call_WaitOnTimer)
return TRUE;
DWORD dwTickCount = GetTickCount();
// the second test is in case GetTickCount wrapped during
// the call. see also the comment in StartTimer.
return (dwTickCount > _dwWakeup ||
dwTickCount < _dwWakeup - _dwMillSecToWait);
}
INTERNAL_(DWORD) TicksToWait()
{
if (_CallState != Call_WaitOnTimer)
return MAX_TICKS_TO_WAIT;
// waiting to retry a rejected call
DWORD dwTick = GetTickCount();
return (_dwWakeup < dwTick) ? 0 : _dwWakeup - dwTick;
}
INTERNAL_(DWORD) GetElapsedTime();
INTERNAL_(EVENT) GetEvent()
{
return _pCalldata->Event;
}
INTERNAL_(void) SetEvent(EVENT Event)
{
_pCalldata->Event = Event;
}
INTERNAL_(UINT) GetId()
{
return _id;
}
INTERNAL_(void) SetId(UINT id)
{
_pCalldata->id = _id = id;
}
INTERNAL Transmit()
{
CairoleDebugOut((DEB_CALLCONT, "CCallInfo::TransmitCall TIDCallee:%x called\n",
_pCalldata->TIDCallee));
_pCalldata->Event = NULL;
HRESULT hres = _pOrigindata->pChCont->TransmitCall(_pCalldata);
CairoleDebugOut((DEB_CALLCONT, "CCallInfo::TransmitCall TIDCallee:%x returned :%x\n",
_pCalldata->TIDCallee, hres));
return hres;
}
INTERNAL OnEvent()
{
CairoleDebugOut((DEB_CALLCONT, "CCallInfo::OnEvent returned\n"));
return _pOrigindata->pChCont->OnEvent(_pCalldata);
}
INTERNAL_(DWORD) GetMsgQInputFlag()
{
DWORD dwInput;
// CODEWORK: optimize this using a table lookup instead of
// a switch statement
switch (_pCalldata->CallCat)
{
case CALLCAT_INTERNALSYNC:
#ifdef _CHICAGO_
// BUGBUG: Chicago needs user update for QS_TRANSFER
dwInput = QS_POSTMESSAGE | QS_SENDMESSAGE;
#else
dwInput = QS_POSTMESSAGE | QS_SENDMESSAGE | QS_TRANSFER;
#endif // _CHICAGO_
break;
case CALLCAT_INTERNALINPUTSYNC:
case CALLCAT_INPUTSYNC:
dwInput = QS_SENDMESSAGE;
break;
default:
#ifdef _CHICAGO_
// BUGBUG: Chicago needs user update for QS_TRANSFER
dwInput = QS_ALLINPUT;
#else
dwInput = QS_ALLINPUT | QS_TRANSFER;
#endif // _CHICAGO_
break;
}
return dwInput;
}
INTERNAL_(BOOL) DoDirectedYieldIfNeeded()
{
if (_pCalldata->fDirectedYield)
{
CairoleDebugOut((DEB_CALLCONT, "DoDirectedYield\n"));
Win4Assert(IsWOWThread());
g_pOleThunkWOW->DirectedYield(_pCalldata->TIDCallee);
return TRUE;
}
else
{
CairoleDebugOut((DEB_CALLCONT, "Skip DoDirectedYield\n"));
return FALSE;
}
}
private:
UINT _id; // callinfo id for the table lookup
DWORD _tid; // threadid where call is made on
// call state
CallState _CallState; // our current call state
DWORD _dwFlags; // internal flags
UINT _wQuitCode; // quit code if WM_QUIT received
HRESULT _hresult; // the return value of this call
// timer status for this callinfo
DWORD _dwTimeOfCall; // time when call was made
DWORD _dwWakeup; // absolute time to wake up
DWORD _dwMillSecToWait; // relative time
// caller specific information
PCALLDATA _pCalldata;
PORIGINDATA _pOrigindata;
};
typedef CCallInfo *PCALLINFO;
class FAR CCallMainControl
{
public:
INTERNAL_(ULONG) AddRef();
INTERNAL_(ULONG) Release();
// wrapper of messagfilter methods
INTERNAL CanHandleIncomingCall( DWORD TIDCaller, REFLID lid,
PINTERFACEINFO32 pIfInfo);
INTERNAL_(DWORD) GetElapsedTimeOfLastOutCall();
INTERNAL CanMakeOutCall (CALLCATEGORY callcat, REFIID iid);
INTERNAL_(void) HandleRejectedCall (PCALLINFO pCI);
INTERNAL_(DWORD) HandlePendingMessage (PCALLINFO pCI);
INTERNAL_(BOOL) DispatchSystemMessage (MSG msg, BOOL fBeep = TRUE);
// modal loop functions - Origin is who called the loop
INTERNAL TransmitAndRunModalLoop (PCALLINFO pCI);
INTERNAL_(BOOL) FindMessage (PCALLINFO pCI, DWORD dwStatus);
INTERNAL_(void) PeekOriginAndDDEMessage(PCALLINFO pCI, BOOL fDDEMsg);
INTERNAL TransmitCall(PCALLINFO pCI);
INTERNAL_(void) CallOnEvent(PCALLINFO pCI);
INTERNAL_(BOOL) MyPeekMessage(PCALLINFO pCI ,MSG *pMsg, HWND hwnd, UINT min, UINT max, WORD wFlag);
//
// the main call type stat
//
INTERNAL_(CALLTYPE) GetCallType () { return _CallType; }
INTERNAL_(void) SetCallType (CALLTYPE calltype)
{
CairoleDebugOut((DEB_CALLCONT, "Changing _CallType from:%x to:%x\n", _CallType, calltype));
_CallType = calltype;
}
INTERNAL_(CALLTYPE) SetCallTypeOfCall (PCALLINFO pCI, CALLCATEGORY callcat);
INTERNAL_(CALLCATEGORY) SetCallCatOfInCall (CALLCATEGORY callcat)
{
CALLCATEGORY ccold = _CallCat;
_CallCat = callcat;
return ccold;
}
INTERNAL_(CALLCATEGORY) GetCallCatOfInCall ()
{
return _CallCat;
}
// install/ remove the message filter - by app
INTERNAL_(PMESSAGEFILTER32) GetMessageFilter()
{
#ifdef _CAIRO_
// cairo allows multithreading, so one thread could change the MF
// while another thread is using it. To prevent that we AddRef the
// MF then Release it when done. It is the callers responsibility
// to take the mutex when calling this function.
if (_pMF)
{
_pMF->AddRef();
}
#endif
return _pMF;
};
INTERNAL_(void) ReleaseMessageFilter(PMESSAGEFILTER32 pMF)
{
#ifdef _CAIRO_
// cairo allows multithreading, so one thread could release while
// another is using the MF. To prevent that we AddRef and Release
// the pMF when using it
pMF->Release();
#endif
};
INTERNAL_(PMESSAGEFILTER32) SetMessageFilter (PMESSAGEFILTER32 pMF);
INTERNAL_(BOOL)Register (PORIGINDATA pOrigindata);
INTERNAL_(BOOL)Unregister (PORIGINDATA pOrigindata);
// Aysnc Rpc Block callback function
INTERNAL BlockFn(void);
// bookkeeping for running callinfos on the stack - used for timer stuff
INTERNAL_(PCALLINFO) GetPrevCallInfo (REFLID lid);
// CODEWORK: nuke the following 5 functions & use a linked list
INTERNAL_(UINT) SetupModalLoop (PCALLINFO pCI);
INTERNAL_(void) ResetModalLoop (UINT id);
INTERNAL_(UINT) InsertCI (PCALLINFO pCI);
INTERNAL_(PCALLINFO) GetCIfromCallID (UINT callid)
{
// Get the callinfo associated with the call id
Win4Assert(callid < _cCallInfoMac
&& L"CCallMainControl::FreeCallID invalid call id.");
return _CallInfoTable[callid];
}
INTERNAL_(void) CCallMainControl::FreeCallID (UINT callid)
{
// frees the entrie in the table - only called by ResetModalLoop
Win4Assert(callid < _cCallInfoMac
&& L"CCallMainControl::FreeCallID invalid call id.");
_CallInfoTable[callid] = 0;
}
INTERNAL_(void) BeginCriticalSection()
{
if (_fMultiThreaded)
_mxs.Request();
}
INTERNAL_(void) EndCriticalSection()
{
if (_fMultiThreaded)
_mxs.Release();
}
CCallMainControl();
~CCallMainControl();
private:
INTERNAL_(void) HandleWakeForMsg(DWORD dwInput, PCALLINFO pCI);
long _cRef; // reference count
// Note: Main Call State
// here we remember the current state the app is
// if the call was at the 'root' level the call state is 0
CALLTYPE _CallType; // call state of server
CALLCATEGORY _CallCat; // call category of Incoming Call,
// NOT outgoing call.
// pointer to the Apps messagefilter
PMESSAGEFILTER32 _pMF; // the current installed message filter
BOOL _fInMessageFilter; // TRUE when calling the Apps MF
// call origin data
#define ODMAX 5
UINT _cODs;
PORIGINDATA _rgpOrigindata[ODMAX];
// BUBUG:
// Note: Table of CallInfos
// * the table holds pointers to the call info in use
// entry 1 is the first outgoing call (wait loop)
// entry 2 is the second one etc.
// * return value 0 means error for functions dealing with call infos
#define CALLINFOMAX 200
UINT _cCur; // this is the number of callinfos in the table
UINT _cCallInfoMac;
PCALLINFO _CallInfoTable[CALLINFOMAX];
PCALLINFO _pCICur; // current outgoing call
// thread safety
BOOL _fMultiThreaded;
CMutexSem _mxs;
};
CCallMainControl *GetCallMainControlForThread();
BOOL SetCallMainControlForThread(CCallMainControl *pcmc);
#endif // _CALLMAIN_HXX_