//+------------------------------------------------------------------------- // // 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 #include #include #include #include // 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_