1840 lines
50 KiB
C++
1840 lines
50 KiB
C++
//+-------------------------------------------------------------------
|
|
//
|
|
// File: service.cxx
|
|
//
|
|
// Contents: Rpc service object implementation
|
|
//
|
|
// Classes: CRpcService - Rpc service object
|
|
// CSrvList - list of Rpc service objects
|
|
//
|
|
// Functions:
|
|
//
|
|
// History: 23-Nov-92 Rickhi
|
|
// 31-Dec-93 ErikGav Chicago port
|
|
// 28-Jun-94 BruceMa Memory sift fixes
|
|
// 19 Jul 94 CraigWi Added support for ASYNC calls
|
|
// 03-Mar-95 JohannP Delaying rpc initialize
|
|
//
|
|
// CODEWORK: should a call to the remote object fail on the given binding,
|
|
// we could try to call on another binding. Ideally, the
|
|
// CEndPoint code would bind to all strings, then ask the
|
|
// Rpc runtime to select one of the protocols for us, which
|
|
// is really where the endpoint selection belongs.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
#include <ole2int.h>
|
|
|
|
#include <objsrv.h>
|
|
|
|
#include <service.hxx> // class definition
|
|
#include <ichnl.h> // IChannelService_ServerIfHandle
|
|
#include <channelb.hxx>
|
|
#include <sichnl.hxx>
|
|
#include <getif.h>
|
|
|
|
#ifdef _CHICAGO_
|
|
#include <callmain.hxx>
|
|
|
|
BOOL NotifyToInitializeRpc(HWND hwnd);
|
|
|
|
#else
|
|
|
|
// global variables
|
|
CSrvList sg_SrvList; // list of svc objects in process
|
|
// static class members
|
|
CRpcService *CRpcService::sg_pLocalSrv = NULL; // local service object
|
|
BOOL CRpcService::_fListening = FALSE;
|
|
#endif
|
|
|
|
BOOL sg_fServerRegisterIf = FALSE;
|
|
|
|
COleStaticMutexSem sg_SrvListLock;
|
|
|
|
|
|
// Rpc does not return SCODES with the status bit set.
|
|
#define RPC_ERROR_MASK 0x000fffff
|
|
#define MAX_RETRIES 2 // max Rpc attempts
|
|
|
|
#ifdef _CHICAGO_
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Method: StartLocalService
|
|
//
|
|
// Synopsis: Starts up the rpc for the current thread
|
|
//
|
|
// Arguments: (none)
|
|
//
|
|
// Returns:
|
|
//
|
|
// History: 3-23-95 JohannP (Johann Posch) Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
BOOL StartLocalService()
|
|
{
|
|
CairoleDebugOut((DEB_ENDPNT, "StartLocalService()\n"));
|
|
LocalService()->Listen(TRUE);
|
|
CairoleDebugOut((DEB_ENDPNT, "StartLocalService() done\n"));
|
|
return TRUE;
|
|
}
|
|
#endif // _CHICAGO_
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CRpcService::CRpcService, public
|
|
//
|
|
// Synopsis: constructor for an Rpc service object
|
|
//
|
|
// History: 23-Nov-93 Rickhi Created
|
|
//
|
|
//--------------------------------------------------------------------
|
|
CRpcService::CRpcService(SEndPoint *pSEp, HRESULT &hr) :
|
|
_CEp(pSEp, hr, TRUE), // TRUE means copy the SEp
|
|
_pContext(NULL),
|
|
_eState(client_ss),
|
|
_ulRefCnt(1)
|
|
{
|
|
CairoleDebugOut((DEB_ENDPNT, "CRpcService::CRpcService %x pSEp %x\n", this, pSEp));
|
|
// AssertValid in callers because sg_pLocalSrv not set yet
|
|
|
|
// If the endpoint strings being passed in are NULL then this is
|
|
// definitely in this process. Otherwise, check our lists.
|
|
_fThisProcess = (pSEp != NULL) ? IsInLocalProcess(&_CEp) : TRUE;
|
|
|
|
#ifdef _CHICAGO_
|
|
_fListening = FALSE;
|
|
_fListenNow = FALSE;
|
|
|
|
// Add to global list of endpoints local to this process for
|
|
// Chicago. In daytona, we only have one end point.
|
|
if (_fThisProcess)
|
|
{
|
|
lslLocalServices.Add(this);
|
|
}
|
|
#endif // _CHICAGO_
|
|
CairoleDebugOut((DEB_ENDPNT, "CRpcService::CRpcService %x pSEp %x\n done", this, pSEp));
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CRpcService::~CRpcService, public
|
|
//
|
|
// Synopsis: destructor for an Rpc service object
|
|
//
|
|
// History: 23-Nov-93 Rickhi Created
|
|
//
|
|
//--------------------------------------------------------------------
|
|
CRpcService::~CRpcService(void)
|
|
{
|
|
// If the service object is not already disconnected, rundown all the
|
|
// channels using it.
|
|
// If the service object was a client, the dtor for the CRpcBindingHandle
|
|
// will unbind. We can't check _hRpc or _pContext more than the
|
|
// AssertValid function.
|
|
|
|
// If the service object was a server, the rundown routine would have been
|
|
// called before this point
|
|
|
|
if (_pContext != NULL)
|
|
{
|
|
RpcSmDestroyClientContext( &_pContext );
|
|
_pContext = NULL;
|
|
}
|
|
|
|
// remove from the service list. This must be done while holding
|
|
// the lock for the list, hence we ask the list object to do it.
|
|
// the object may or may not still be on the list.
|
|
|
|
#ifdef _CHICAGO_
|
|
if (_fListening)
|
|
{
|
|
_CEp.RemoveDefaultProtseq();
|
|
}
|
|
|
|
// Remove the endpoint from list of local endpoints since it
|
|
// is going away.
|
|
lslLocalServices.Remove(this);
|
|
#else
|
|
// On Chicago AssertValid references TLS. Since this destructor may be
|
|
// called at process detach, don't AssertValid on Chicago.
|
|
AssertValid();
|
|
#endif
|
|
_eState = deleted_ss;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CRpcService::AddRef, public
|
|
//
|
|
// Synopsis: increment reference count
|
|
//
|
|
// History: 1 Dec 93 AlexMit Created
|
|
//
|
|
//--------------------------------------------------------------------
|
|
ULONG CRpcService::AddRef(void)
|
|
{
|
|
Win4Assert( _ulRefCnt >= 1 );
|
|
ULONG ulRefCnt = InterlockedIncrement( (long *) &_ulRefCnt );
|
|
return ulRefCnt;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CRpcService::Release, public
|
|
//
|
|
// Synopsis: decrement reference count
|
|
//
|
|
// History: 1 Dec 93 AlexMit Created
|
|
//
|
|
//--------------------------------------------------------------------
|
|
ULONG CRpcService::Release(void)
|
|
{
|
|
// Prevent this object from being found after it is deleted.
|
|
sg_SrvListLock.Request();
|
|
|
|
ULONG ulRefCnt = _ulRefCnt - 1;
|
|
|
|
if (InterlockedDecrement( (long*) &_ulRefCnt ) == 0)
|
|
{
|
|
#ifdef _CHICAGO_
|
|
CSrvList *pSrvList = (CSrvList *) TLSGetServiceList();
|
|
Win4Assert( pSrvList != NULL );
|
|
pSrvList->RemoveFromList(this);
|
|
#else
|
|
sg_SrvList.RemoveFromList(this);
|
|
#endif
|
|
sg_SrvListLock.Release();
|
|
delete this;
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
sg_SrvListLock.Release();
|
|
return ulRefCnt;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CRpcService::Bind, public
|
|
//
|
|
// Synopsis: Selects the most appropriate endpoint from the array
|
|
// of endpoints and binds to it, getting an Rpc handle.
|
|
// This is called only once per RpcService object, the
|
|
// handle is subsequently cached.
|
|
//
|
|
// History: 23-Nov-93 Rickhi Created
|
|
//
|
|
// Note: this is only called if this is a remote service object.
|
|
//
|
|
// Exceptions: None
|
|
//
|
|
// Notes: This code is thread safe, so only one bind occurs.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
SCODE CRpcService::Bind(void)
|
|
{
|
|
TRACECALL(TRACE_RPC, "CRpcService::Bind");
|
|
CairoleDebugOut((DEB_ENDPNT, "CRpcService::Bind %p\n", this));
|
|
AssertValid();
|
|
|
|
// single thread access to the binding code
|
|
CLock sms(_mxs);
|
|
|
|
#ifndef _CHICAGO_
|
|
Win4Assert(this != sg_pLocalSrv);
|
|
#endif
|
|
Win4Assert(_CEp.GetSEp() != NULL);
|
|
|
|
if (_hRpc.Handle() != NULL)
|
|
{
|
|
// we took the semaphore & when we returned, the bind had
|
|
// already happened, so there is nothing more for us to do.
|
|
return S_OK;
|
|
}
|
|
|
|
if (_eState == disconnected_ss)
|
|
return RPC_E_SERVER_DIED_DNE;
|
|
|
|
#ifdef _CHICAGO_
|
|
// Initialize rpc on server and local thread
|
|
NotifyServerOfSEp();
|
|
LocalService()->Listen(TRUE);
|
|
#endif
|
|
|
|
// try binding to the preferred strings first.
|
|
SCODE sc = _hRpc.BindByString(_CEp.GetStringBinding());
|
|
if (FAILED(sc))
|
|
{
|
|
_eState = disconnected_ss;
|
|
CairoleDebugOut((DEB_ERROR, "CRpcService Failed to Bind %x\n", sc));
|
|
}
|
|
#ifdef _CHICAGO_
|
|
else
|
|
{
|
|
CairoleDebugOut((DEB_CHANNEL | DEB_ENDPNT, "CRpcService - Created binding handle %ws\n",
|
|
_CEp.GetStringBinding() ));
|
|
CairoleDebugOut((DEB_CHANNEL | DEB_ENDPNT, " handle 0x%x thread 0x%x\n",
|
|
_hRpc.Handle(), GetCurrentThreadId() ));
|
|
sc = I_RpcBindingSetAsync( _hRpc.Handle(), OleModalLoopBlockFn );
|
|
}
|
|
#endif
|
|
|
|
CairoleDebugOut((DEB_ENDPNT, "CRpcService::Bind %p done \n", this));
|
|
return sc;
|
|
}
|
|
|
|
//
|
|
// The following manifest maps the specific error RPC_S_TYPE_ALREADY_REGISTERED to sc
|
|
//
|
|
#define MAP_REGISTERIF_ERROR(sc) ((sc == RPC_S_TYPE_ALREADY_REGISTERED)? sc = RPC_S_OK: sc)
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CRpcService::Listen, public
|
|
//
|
|
// Synopsis: this starts the Rpc service listening. this is required
|
|
// in order to marshal interfaces. it is executed lazily,
|
|
// that is, we dont start listening until someone tries to
|
|
// marshal a local object interface. this is done so we dont
|
|
// spawn a thread unnecessarily.
|
|
//
|
|
// History: 23-Nov-93 Rickhi Created
|
|
//
|
|
// Note: this is only called if this is the local service object.
|
|
// This code is thread safe so only one listen occurs.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
SCODE CRpcService::Listen(BOOL fListenNow)
|
|
{
|
|
TRACECALL(TRACE_RPC, "CRpcService::Listen");
|
|
CairoleDebugOut((DEB_CHANNEL, "CRpcService::Listen %p\n", this));
|
|
CairoleDebugOut((DEB_ENDPNT, "CRpcService::Listen (fListenNow:%d) on %p\n", fListenNow, this));
|
|
AssertValid();
|
|
Win4Assert (this == LocalService());
|
|
|
|
|
|
RPC_STATUS sc = RPC_S_OK;
|
|
|
|
// single thread access to this code.
|
|
CLock sms(_mxs);
|
|
#ifdef _CHICAGO_
|
|
if (fListenNow)
|
|
{
|
|
_fListenNow = fListenNow;
|
|
}
|
|
|
|
if(_fListenNow == FALSE)
|
|
{
|
|
sc = _CEp.CreateFakeSEp();
|
|
}
|
|
else
|
|
#endif // _CHICAGO_
|
|
if (!IsServiceListen())
|
|
{
|
|
//Win4Assert (FALSE && "CRpcService::StartListe calling");
|
|
|
|
#ifdef _CHICAGO_
|
|
CChannelControl *pChannelControl;
|
|
|
|
// make sure the channelcontroller is initialized
|
|
GetLocalChannelControl( pChannelControl );
|
|
if (!pChannelControl)
|
|
{
|
|
HRESULT hres;
|
|
hres = InitChannelControl();
|
|
if (hres != S_OK)
|
|
{
|
|
CairoleDebugOut((DEB_ERROR, "CRpcServer - InitChannelControl failed:%x\n", hres));
|
|
}
|
|
}
|
|
#endif _CHICAGO_
|
|
|
|
// register protocol sequences with Rpc
|
|
sc = _CEp.RegisterDefaultProtseq();
|
|
|
|
if (sc == S_OK)
|
|
{
|
|
// register the IChannelService interface and the main
|
|
// incoming call interface with Rpc
|
|
|
|
#ifdef _CHICAGO_
|
|
if (sg_fServerRegisterIf != FALSE)
|
|
{
|
|
CairoleDebugOut((DEB_ENDPNT,"CRpcListen: sg_fServerRegisterIf != FALSE\n"));
|
|
goto exitNow;
|
|
}
|
|
#endif
|
|
sc = RpcServerRegisterIf(IChannelService_ServerIfHandle, NULL, NULL);
|
|
|
|
if(MAP_REGISTERIF_ERROR(sc) != RPC_S_OK)
|
|
{
|
|
CairoleDebugOut((DEB_ERROR,
|
|
"RpcListen IChannelService_ServerIfHandle %x\n",
|
|
sc));
|
|
goto exitNow;
|
|
}
|
|
sc = RpcServerRegisterIf(&the_server_if, NULL, NULL);
|
|
if(MAP_REGISTERIF_ERROR(sc) != RPC_S_OK)
|
|
{
|
|
CairoleDebugOut((DEB_ERROR,"RpcListen the_server_if %x\n",sc));
|
|
goto exitNow;
|
|
}
|
|
|
|
sc = RpcServerRegisterIf(IObjServer_ServerIfHandle, 0, 0);
|
|
if(MAP_REGISTERIF_ERROR(sc) != RPC_S_OK)
|
|
{
|
|
CairoleDebugOut((DEB_ERROR,"IObjServer_ServerIfHandle %x\n",sc));
|
|
goto exitNow;
|
|
}
|
|
|
|
|
|
sc = RpcServerRegisterIf(IInterfaceFromWindowProp_ServerIfHandle,0,0);
|
|
|
|
if(MAP_REGISTERIF_ERROR(sc) != RPC_S_OK)
|
|
{
|
|
CairoleDebugOut((DEB_ERROR,
|
|
"IInterfaceFromWindowProp_ServerIfHandle %x\n",
|
|
sc));
|
|
goto exitNow;
|
|
}
|
|
|
|
if ( sc == RPC_S_OK)
|
|
{
|
|
sg_fServerRegisterIf = TRUE;
|
|
// turn off context handle serialization, as this causes
|
|
// deadlocks in recursive Rpc calls.
|
|
I_RpcSsDontSerializeContext();
|
|
|
|
// start listening for Rpc calls.
|
|
sc = RpcServerListen(1, // min call threads
|
|
0xffff, // max calls
|
|
1); // dont wait
|
|
|
|
#if defined (_CAIRO_) || defined(_CHICAGO_)
|
|
// Under Cairo, some services are both an ordinary RPC server
|
|
// and an OLE server, and may issue their own RpcServerListen
|
|
// before OLE. Therefore, treat "already listening" as a
|
|
// successful operation
|
|
if ( sc == RPC_S_ALREADY_LISTENING )
|
|
sc = RPC_S_OK;
|
|
#endif
|
|
|
|
if (sc == RPC_S_OK)
|
|
{
|
|
// wait until we are really listening before telling the
|
|
// world that we are ready to accept calls.
|
|
|
|
while ((sc = RpcMgmtIsServerListening(NULL)) == RPC_S_NOT_LISTENING)
|
|
{
|
|
CairoleDebugOut((DEB_MARSHAL,"RpcMgmtIsServerListening=%x\n",sc));
|
|
Sleep(1); // yield to let Rpc go.
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CairoleDebugOut((DEB_ERROR, "RpcServerListen failed:%x\n", sc));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CairoleDebugOut((DEB_ERROR, "RpcServerRegisterIf failed:%x\n", sc));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CairoleDebugOut((DEB_ERROR, "CRpcService::Listen RegisterDefaultProtseq failed:%x\n", sc));
|
|
}
|
|
|
|
exitNow:
|
|
|
|
if (sc == RPC_S_OK)
|
|
{
|
|
SetThreadListening(TRUE);
|
|
}
|
|
else
|
|
{
|
|
sc = HRESULT_FROM_WIN32(sc);
|
|
}
|
|
}
|
|
|
|
CairoleDebugOut((DEB_CHANNEL | DEB_ENDPNT, "CRpcService::Listen done\n"));
|
|
return sc;
|
|
}
|
|
|
|
#ifdef _CHICAGO_
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Method: CRpcService::NotifyServerOfSEp
|
|
//
|
|
// Synopsis: notifies the server of the endpoit to start listen
|
|
//
|
|
// Arguments: [void] --
|
|
//
|
|
// Returns:
|
|
//
|
|
// History: 3-24-95 JohannP (Johann Posch) Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
BOOL CRpcService::NotifyServerOfSEp(void)
|
|
{
|
|
CairoleDebugOut((DEB_ENDPNT, "CRpcService::NotifyServerOfSEp %p\n", this));
|
|
BOOL fRet = TRUE;
|
|
|
|
SEndPoint *pSE = _CEp.GetSEp();
|
|
if (pSE)
|
|
{
|
|
NotifyToInitializeRpc((HWND)pSE->ulhwnd);
|
|
}
|
|
|
|
CairoleDebugOut((DEB_ENDPNT, "CRpcService::NotifyServerOfSEp %p done fRet:%d\n", this,fRet));
|
|
return fRet;
|
|
}
|
|
#endif // _CHICAGO_
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Function: StopListen
|
|
//
|
|
// Synopsis: this stops the Rpc service listening. In multithreaded
|
|
// mode it can safely wait for all calls to complete without
|
|
// deadlocking. In the apartment mode the channel controller
|
|
// will already have completed all calls and blocked new calls.
|
|
//
|
|
// History: 23-Nov-93 Rickhi Created
|
|
//
|
|
//--------------------------------------------------------------------
|
|
SCODE StopListen(void)
|
|
{
|
|
CairoleDebugOut((DEB_ITRACE, "StopListen\n"));
|
|
SCODE sc = S_OK;
|
|
RPC_STATUS status;
|
|
|
|
#ifdef _CHICAGO_
|
|
if (sg_fServerRegisterIf == FALSE)
|
|
{
|
|
return sc;
|
|
}
|
|
#endif
|
|
|
|
// unregister IChannelService with RPC - wait for calls to complete
|
|
RpcServerUnregisterIf(IChannelService_ServerIfHandle, 0, 1);
|
|
|
|
// unregister the main incoming call interface with RPC - wait for calls
|
|
RpcServerUnregisterIf(&the_server_if, 0, 1);
|
|
|
|
sg_fServerRegisterIf = FALSE;
|
|
|
|
if ( LocalService()
|
|
&& IsThreadListening())
|
|
{
|
|
// Stop the Rpc server listening. This will tell Rpc to complete
|
|
// the remaining calls, at which point, the RpcServerListen thread
|
|
// will return from the bowels of the Rpc runtime and will exit.
|
|
|
|
status = RpcMgmtStopServerListening(NULL);
|
|
|
|
if (status != RPC_S_OK)
|
|
{
|
|
CairoleDebugOut((DEB_ERROR, "RpcMgmtStopServerListening failed sc=%x\n", status));
|
|
sc = MAKE_SCODE( SEVERITY_ERROR, FACILITY_WIN32, status );
|
|
}
|
|
else
|
|
{
|
|
// Wait for all calls to complete.
|
|
status = RpcMgmtWaitServerListen();
|
|
#if DBG == 1
|
|
if (status != RPC_S_OK)
|
|
{
|
|
CairoleDebugOut((DEB_ERROR, "RpcMgmtWaitServerListening failed sc=%x\n", status));
|
|
}
|
|
#endif
|
|
}
|
|
SetThreadListening(FALSE);
|
|
}
|
|
|
|
return sc;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CRpcService::GetDestCtx
|
|
//
|
|
// Synopsis: returns the destination context for purposes of
|
|
// marshalling an interface. the destination context
|
|
// for now is just the protseq we need to talk on.
|
|
//
|
|
// History: 23-Nov-93 Rickhi Created
|
|
//
|
|
//--------------------------------------------------------------------
|
|
void CRpcService::GetDestCtx(DWORD *pdwDestCtx, void **ppvDestCtx)
|
|
{
|
|
AssertValid();
|
|
CLock lck(_mxs);
|
|
|
|
if (_CEp.DiffMachine())
|
|
{
|
|
// different network address, so we are marshalling for a
|
|
// different machine.
|
|
|
|
*pdwDestCtx = MSHCTX_DIFFERENTMACHINE;
|
|
if (_CEp.GetActiveProtseq((WCHAR **)ppvDestCtx))
|
|
{
|
|
// if the protocol specified by GetActiveProtseq has not
|
|
// been registered, specify context, otherwise set it to
|
|
// NULL so that MarshalInterface in the channel avoids
|
|
// another check. The win here will become obvious when
|
|
// pvDestCtx becomes an interface derived from IUnknown
|
|
// and the marshalling code has to do lots of work to
|
|
// figure things out.
|
|
|
|
if (ppvDestCtx)
|
|
{
|
|
*ppvDestCtx = NULL;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// same machine, specify local context
|
|
|
|
// Check if this is an inprocess marshal that is not in WOW.
|
|
if ((_eState == client_ss)
|
|
&& _fThisProcess
|
|
&& !IsWOWThread())
|
|
{
|
|
*pdwDestCtx = MSHCTX_INPROC;
|
|
}
|
|
else
|
|
{
|
|
// Interprocess case
|
|
*pdwDestCtx = MSHCTX_LOCAL;
|
|
}
|
|
|
|
if (ppvDestCtx)
|
|
{
|
|
*ppvDestCtx = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CRpcService::RegisterProtseq, public
|
|
//
|
|
// Synopsis: registers a protseq with Rpc
|
|
//
|
|
// History: 23-Nov-93 Rickhi Created
|
|
//
|
|
//--------------------------------------------------------------------
|
|
SCODE CRpcService::RegisterProtseq(WCHAR *pwszProtseq)
|
|
{
|
|
// local server, register the protseq here
|
|
AssertValid();
|
|
CLock lck(_mxs);
|
|
return _CEp.RegisterProtseq(pwszProtseq);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CRpcService::RemoteRegisterProtseq, public
|
|
//
|
|
// Synopsis: registers a protseq with Rpc in the process this SO represents
|
|
//
|
|
// History: 23-Nov-93 Rickhi Created
|
|
// 24-Nov-94 AlexMit Hacked from RegisterProtseq
|
|
//
|
|
// Note: Since this doesn't call user code on the server side,
|
|
// it doesn't have to get off the client thread.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
SCODE CRpcService::RemoteRegisterProtseq(WCHAR *pwszProtseq)
|
|
{
|
|
HRESULT sc = S_OK;
|
|
|
|
AssertValid();
|
|
|
|
// remote server, tell the other side to register the protseq
|
|
// then update our endpoint structure.
|
|
|
|
if (_CEp.DiffMachine())
|
|
{
|
|
if (_eState == disconnected_ss)
|
|
return RPC_E_SERVER_DIED_DNE;
|
|
|
|
SEndPoint *pSEp = NULL;
|
|
ULONG cRetry = 0;
|
|
|
|
// make sure we have a handle to call on
|
|
handle_t RpcHdl = GetRpcHdl();
|
|
if (!RpcHdl)
|
|
return E_HANDLE;
|
|
|
|
do
|
|
{
|
|
// Rpc call to remote RpcServiceObject
|
|
error_status_t errstat = 0;
|
|
sc = _ICS_RegisterProtseq(RpcHdl, pwszProtseq, &pSEp, &errstat);
|
|
|
|
if (errstat != 0)
|
|
{
|
|
// convert RPC return code to HRESULT incase we
|
|
// exit the loop
|
|
sc = HRESULT_FROM_WIN32(errstat);
|
|
|
|
if (errstat == RPC_S_SERVER_TOO_BUSY ||
|
|
errstat == RPC_S_NOT_LISTENING ||
|
|
errstat == RPC_S_SERVER_UNAVAILABLE)
|
|
{
|
|
// normal (?) errors. sleep and retry
|
|
Sleep(SERVER_BUSY_SLEEP_MS);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
CairoleDebugOut((DEB_MARSHAL, "ICS_RegisterProtseq %ws = %x\n",
|
|
pwszProtseq, sc));
|
|
break;
|
|
|
|
} while (++cRetry < MAX_RETRIES);
|
|
|
|
if (SUCCEEDED(sc) && pSEp)
|
|
{
|
|
CLock lck(_mxs);
|
|
_CEp.Replace(pSEp);
|
|
MIDL_user_free(pSEp);
|
|
}
|
|
}
|
|
|
|
return sc;
|
|
}
|
|
|
|
|
|
|
|
/*-------------------------------------------------------------------
|
|
|
|
Member: ActuallyCallGetContextHdl
|
|
|
|
Synopsis: calls the remote service object to generate a context
|
|
handle.
|
|
|
|
To avoid being recalled when the call controller wants to
|
|
retransmit, this function never returns RPC_E_SERVERCALL_RETRYLATER or
|
|
RPC_E_SERVERCALL_REJECTED which are the only errors the call controller
|
|
retries.
|
|
|
|
History: 2 Aug 94 AlexMit Hacked
|
|
|
|
--------------------------------------------------------------------*/
|
|
|
|
HRESULT ActuallyCallGetContextHdl( STHREADCALLINFO *call )
|
|
{
|
|
SCODE sc;
|
|
POBJCTX pContext;
|
|
ULONG cRetry = 0;
|
|
SGetContextHdl *params = (SGetContextHdl *) call;
|
|
error_status_t errstat = 0;
|
|
|
|
// make sure we have a handle to call on
|
|
handle_t RpcHdl = params->service->GetRpcHdl();
|
|
|
|
if (!RpcHdl)
|
|
sc = E_FAIL;
|
|
|
|
else
|
|
{
|
|
do
|
|
{
|
|
// Rpc call to remote RpcServiceObject
|
|
|
|
sc = _ICS_GetContextHdl(RpcHdl,
|
|
params->psepClient,
|
|
&pContext,
|
|
&errstat);
|
|
|
|
if (errstat != 0)
|
|
{
|
|
// Convert RPC return code to HRESULT incase we
|
|
// exit the loop!
|
|
sc = MAKE_SCODE( SEVERITY_ERROR, FACILITY_WIN32, (errstat) );
|
|
// Retry on RPC_S_CALL_FAILED only because that is the return
|
|
// code RPC gives if we are trying string bindings for a
|
|
// process that are a duplicate of string bindings that we
|
|
// still hold for another process (that has gone away).
|
|
|
|
if (errstat == RPC_S_SERVER_TOO_BUSY ||
|
|
errstat == RPC_S_NOT_LISTENING ||
|
|
errstat == RPC_S_SERVER_UNAVAILABLE ||
|
|
errstat == RPC_S_CALL_FAILED)
|
|
{
|
|
// normal (?) errors. sleep and retry
|
|
Sleep(SERVER_BUSY_SLEEP_MS);
|
|
continue;
|
|
}
|
|
|
|
#if DBG == 1
|
|
// Make it easier to print out an error message
|
|
#endif // DBG
|
|
|
|
}
|
|
|
|
CairoleDebugOut((DEB_MARSHAL, "ICS_GetContextHdl pHdl=%x\n",
|
|
pContext));
|
|
|
|
#if DBG == 1
|
|
if (pContext == NULL)
|
|
{
|
|
CairoleDebugOut((DEB_ERROR, "ICS_GetContextHdl failed: %x\n", sc));
|
|
}
|
|
else
|
|
{
|
|
DWORD *tmp = (DWORD *) pContext;
|
|
CairoleDebugOut((DEB_MARSHAL, "ICS_GetContextHdl Hdl=%x%x%x%x%x\n",
|
|
tmp[0], tmp[1], tmp[2], tmp[3], tmp[4]));
|
|
}
|
|
#endif // DBG
|
|
|
|
// An error occurred
|
|
break;
|
|
|
|
} while (++cRetry < MAX_RETRIES);
|
|
|
|
// If ICS_GetContextHdl succeeded, save the result.
|
|
if (sc == S_OK)
|
|
{
|
|
sc = params->service->SetContextHdl( pContext );
|
|
|
|
// If a context handle was already saved, free this one.
|
|
if (sc != S_OK)
|
|
{
|
|
ICS_ReleaseContextHdl( &pContext, &errstat );
|
|
Win4Assert( pContext == NULL || errstat != RPC_S_OK );
|
|
if (errstat != RPC_S_OK)
|
|
RpcSmDestroyClientContext( &pContext );
|
|
sc = S_OK;
|
|
}
|
|
}
|
|
}
|
|
|
|
return sc;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: ~SGetContextHdl
|
|
//
|
|
// Synopsis: Called when ActuallyGetContextHdl is canceled. Releases
|
|
// service.
|
|
//
|
|
// Exceptions: none
|
|
//
|
|
// History: 2 Aug 94 AlexMit Created
|
|
//
|
|
// Notes: Thread safe; client/server safe (only used on client side now)
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
SGetContextHdl::~SGetContextHdl( )
|
|
{
|
|
service->Release();
|
|
PrivMemFree( psepClient );
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CRpcService::CheckContextHdl
|
|
//
|
|
// Synopsis: calls the remote service object to generate a context
|
|
// handle.
|
|
//
|
|
// Exceptions: none
|
|
//
|
|
// History: 23-Nov-93 Rickhi Created
|
|
//
|
|
// Notes: Thread safe
|
|
//
|
|
//--------------------------------------------------------------------
|
|
SCODE CRpcService::CheckContextHdl( HAPT server )
|
|
{
|
|
CairoleDebugOut((DEB_ENDPNT, "CRpcService::CheckContextHdl %p\n", this));
|
|
TRACECALL(TRACE_RPC, "CRpcService::CheckContextHdl");
|
|
AssertValid();
|
|
|
|
SCODE sc;
|
|
SGetContextHdl *params;
|
|
|
|
// single thread access
|
|
_mxs.Request();
|
|
|
|
|
|
if (this == LocalService())
|
|
{
|
|
_mxs.Release();
|
|
return S_OK;
|
|
}
|
|
|
|
if (_eState == disconnected_ss)
|
|
{
|
|
_mxs.Release();
|
|
return RPC_E_SERVER_DIED_DNE;
|
|
}
|
|
|
|
// Don't do anything if the context handle is already set.
|
|
if (_pContext != NULL)
|
|
{
|
|
_mxs.Release();
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
#ifdef _CHICAGO_
|
|
// initialize rpc for this thread
|
|
LocalService()->Listen(TRUE);
|
|
Win4Assert (IsThreadListening() == TRUE && "RpcService of this thread still not listen\n");
|
|
#else
|
|
// make sure the remote protocols are registered locally if needed
|
|
if (DiffMachine())
|
|
{
|
|
WCHAR *pwszProtseq;
|
|
GetActiveProtseq(&pwszProtseq);
|
|
LocalService()->RegisterProtseq(pwszProtseq);
|
|
}
|
|
|
|
#endif // _CHICAGO_
|
|
|
|
_mxs.Release();
|
|
|
|
// Get a parameter block.
|
|
params = new SGetContextHdl(ActuallyCallGetContextHdl,
|
|
CALLCAT_INTERNALINPUTSYNC, server);
|
|
if (params == NULL)
|
|
return E_OUTOFMEMORY;
|
|
|
|
// Fill in the parameters.
|
|
params->psepClient = LocalService()->CopySEp();
|
|
params->service = this;
|
|
AddRef();
|
|
|
|
// Switch threads.
|
|
if (params->psepClient != NULL)
|
|
sc = CChannelControl::GetOffCOMThread( (STHREADCALLINFO **) ¶ms );
|
|
else
|
|
sc = E_OUTOFMEMORY;
|
|
|
|
// Free the parameter block.
|
|
if (sc != RPC_E_CALL_CANCELED)
|
|
{
|
|
delete params;
|
|
}
|
|
CairoleDebugOut((DEB_ENDPNT, "CRpcService::CheckContextHdl %p done\n", this));
|
|
return sc;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: ActuallyCallGetChannelId
|
|
//
|
|
// Synopsis: calls the remote service object to generate a channel
|
|
// and return its id.
|
|
//
|
|
// This function is a channel control dispatch function. To avoid being
|
|
// recalled when the call controller wants to retransmit, this function
|
|
// never returns
|
|
// RPC_E_SERVERCALL_RETRYLATER or RPC_E_SERVERCALL_REJECTED which are the
|
|
// only errors the call controller retries.
|
|
//
|
|
// History: 27 Nov 93 AlexMit Hacked
|
|
//
|
|
//--------------------------------------------------------------------
|
|
HRESULT ActuallyCallGetChannelId( STHREADCALLINFO *call )
|
|
{
|
|
SGetChannelId *params = (SGetChannelId *) call;
|
|
ULONG cRetry = 0;
|
|
HRESULT result = S_OK;
|
|
|
|
// Make sure we have a handle to call on.
|
|
POBJCTX context = params->service->GetContextHdl();
|
|
params->channel_id = BAD_CHANNEL_ID;
|
|
|
|
if (!context)
|
|
{
|
|
result = E_HANDLE;
|
|
}
|
|
else if (params->psepClient == NULL)
|
|
{
|
|
result = E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
do
|
|
{
|
|
#if DBG==1
|
|
DWORD *tmp = (DWORD *) context;
|
|
CairoleDebugOut((DEB_MARSHAL,
|
|
"Calling ICS_GetChannelID Context=%x%x%x%x%x\n",
|
|
tmp[0], tmp[1], tmp[2], tmp[3], tmp[4]));
|
|
#endif
|
|
|
|
// Rpc call to remote RpcServiceObject
|
|
error_status_t errstat;
|
|
|
|
// Rpc call to remote RpcServiceObject
|
|
result = _ICS_GetChannelId(&context,
|
|
params->psepClient,
|
|
params->object_id,
|
|
params->flags,
|
|
params->server,
|
|
call->lid(),
|
|
params->dwClientTID,
|
|
¶ms->channel_id,
|
|
&errstat);
|
|
|
|
if (errstat != 0)
|
|
{
|
|
// Convert RPC return code to HRESULT incase we
|
|
// exit the loop!
|
|
result = HRESULT_FROM_WIN32(errstat);
|
|
|
|
// Should we retry the error?
|
|
if ((errstat == RPC_S_SERVER_TOO_BUSY))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// We only do this once unless there is an RPC error that
|
|
// encourages us to retry
|
|
break;
|
|
|
|
} while (++cRetry < MAX_RETRIES);
|
|
|
|
Win4Assert( (result == S_OK && params->channel_id != BAD_CHANNEL_ID) ||
|
|
(result != S_OK && params->channel_id == BAD_CHANNEL_ID) );
|
|
return result;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: SGetChannelId dtor
|
|
//
|
|
// Synopsis: Called when ActuallyGetChannelId is canceled. Releases
|
|
// service.
|
|
//
|
|
// Exceptions: none
|
|
//
|
|
// History: 26 June 94 AlexMit Created
|
|
// 7 July 94 CraigWi Changed to destructor
|
|
//
|
|
// Notes: Thread safe; client/server safe (only used on client side now)
|
|
//
|
|
//--------------------------------------------------------------------
|
|
SGetChannelId::~SGetChannelId( )
|
|
{
|
|
service->Release();
|
|
PrivMemFree(psepClient);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CRpcService::GetChannelId
|
|
//
|
|
// Synopsis: calls the remote service object to generate a channel
|
|
// and return its id.
|
|
//
|
|
// Exceptions: none
|
|
//
|
|
// History: 27-Nov-93 AlexMit Created
|
|
//
|
|
// Notes: Thread safe
|
|
//
|
|
//--------------------------------------------------------------------
|
|
SCODE CRpcService::GetChannelId( OID ObjectId, DWORD dwFlags, HAPT hapt,
|
|
DWORD *pChannelId )
|
|
{
|
|
TRACECALL(TRACE_RPC, "CRpcService::GetChannelId");
|
|
AssertValid();
|
|
|
|
SGetChannelId *params;
|
|
HRESULT result;
|
|
|
|
CairoleDebugOut((DEB_MARSHAL, "ICS_GetChannelId\n"));
|
|
|
|
// Get a parameter block.
|
|
*pChannelId = BAD_CHANNEL_ID;
|
|
params = new SGetChannelId(ActuallyCallGetChannelId,
|
|
CALLCAT_INTERNALINPUTSYNC, hapt);
|
|
|
|
if (params == NULL)
|
|
return E_OUTOFMEMORY;
|
|
|
|
// Fill in the parameters.
|
|
params->object_id = ObjectId;
|
|
params->flags = dwFlags;
|
|
params->dwClientTID = GetCurrentThreadId();
|
|
params->channel_id = BAD_CHANNEL_ID;
|
|
params->psepClient = LocalService()->CopySEp();
|
|
params->service = this;
|
|
AddRef(); // released in SGetChannelId dtor
|
|
|
|
// If local, just switch threads.
|
|
if (this == LocalService())
|
|
{
|
|
params->ResetDispatchFn(ThreadGetChannelId);
|
|
result = CChannelControl::SwitchCOMThread( hapt,
|
|
(STHREADCALLINFO **) ¶ms );
|
|
}
|
|
|
|
// If remote, dispatch on some RPC thread.
|
|
else
|
|
{
|
|
params->server = hapt;
|
|
result = CChannelControl::GetOffCOMThread( (STHREADCALLINFO **) ¶ms );
|
|
}
|
|
|
|
// return results and free packet
|
|
if (result != RPC_E_CALL_CANCELED)
|
|
{
|
|
*pChannelId = params->channel_id;
|
|
delete params;
|
|
}
|
|
Win4Assert( (result == S_OK && *pChannelId != BAD_CHANNEL_ID) ||
|
|
(result != S_OK && *pChannelId == BAD_CHANNEL_ID) );
|
|
return result;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: ActuallyCallReleaseChannel
|
|
//
|
|
// Synopsis: calls the remote channel service to release a proxy
|
|
// for the object.
|
|
//
|
|
// History: 9 Nov 93 AlexMit Hacked
|
|
// 3-31-95 JohannP Made call really sync or async
|
|
//
|
|
//
|
|
// Note: For protocol wmsg the call is made sync or async by
|
|
// calling the correct raw rpc call.
|
|
// On other protocolls this is done on the server side
|
|
// by choosing the correct mechanism to switch to the
|
|
// appropriate thread form the rpc thread.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
HRESULT ActuallyCallReleaseChannel( STHREADCALLINFO *call )
|
|
{
|
|
|
|
CairoleDebugOut((DEB_CHANNEL | DEB_ENDPNT , "ActuallyCallReleaseChannel()\n"));
|
|
|
|
SReleaseChannel *params = (SReleaseChannel *) call;
|
|
ULONG cRetry = 0;
|
|
HRESULT result;
|
|
|
|
// make sure we have a handle to call on
|
|
handle_t RpcHdl = params->service->GetRpcHdl();
|
|
|
|
if (!RpcHdl)
|
|
{
|
|
return E_HANDLE;
|
|
}
|
|
|
|
do
|
|
{
|
|
// Rpc call to remote RpcServiceObject
|
|
error_status_t errstat;
|
|
|
|
#ifdef _CHICAGO_
|
|
if (params->async)
|
|
{
|
|
|
|
result = RPC_S_OK;
|
|
errstat = 0;
|
|
_ICS_AsyncReleaseChannel(RpcHdl, params->channel_id,
|
|
params->count, params->async,
|
|
call->lid());
|
|
}
|
|
else
|
|
#endif // _CHICAGO_
|
|
{
|
|
CairoleDebugOut((DEB_CHANNEL | DEB_ENDPNT , "Calling _ICS_ReleaseChannel\n"));
|
|
result = _ICS_ReleaseChannel(RpcHdl, params->channel_id,
|
|
params->count, params->async,
|
|
call->lid(), &errstat);
|
|
CairoleDebugOut((DEB_CHANNEL | DEB_ENDPNT , "Calling _ICS_ReleaseChannel done errstat: %x\n",errstat));
|
|
|
|
if (errstat != 0)
|
|
{
|
|
// Convert RPC return code to HRESULT incase we
|
|
// exit the loop
|
|
result = HRESULT_FROM_WIN32(errstat);
|
|
|
|
// Should we retry the error?
|
|
if ((errstat == RPC_S_SERVER_TOO_BUSY))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
// We only do this once unless there is an RPC error that
|
|
// encourages us to retry
|
|
break;
|
|
|
|
} while (++cRetry < MAX_RETRIES);
|
|
CairoleDebugOut((DEB_CHANNEL | DEB_ENDPNT , "ActuallyCallReleaseChannel() done \n"));
|
|
return result;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: SReleaseChannel dtor
|
|
//
|
|
// Synopsis: Called when ActuallyReleaseChannel is canceled. Releases
|
|
// service.
|
|
//
|
|
// Exceptions: none
|
|
//
|
|
// History: 26 June 94 AlexMit Created
|
|
// 7 July 94 CraigWi Changed to destructor
|
|
//
|
|
// Notes: Thread safe; client/server safe and called on both sides
|
|
//
|
|
//--------------------------------------------------------------------
|
|
SReleaseChannel::~SReleaseChannel( )
|
|
{
|
|
// NULL in the async case (server side)
|
|
if (service != NULL)
|
|
service->Release();
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CRpcService::ReleaseChannel
|
|
//
|
|
// Synopsis: calls the remote channel service to release a proxy
|
|
// for the object.
|
|
//
|
|
// History: 23-Nov-93 Rickhi Created
|
|
//
|
|
//--------------------------------------------------------------------
|
|
SCODE CRpcService::ReleaseChannel(CRpcChannelBuffer *pChannel,
|
|
BOOL fAsyncRelease)
|
|
{
|
|
TRACECALL(TRACE_RPC, "CRpcService::ReleaseChannel");
|
|
SReleaseChannel *params;
|
|
HRESULT hr;
|
|
|
|
// Check the state of this service object.
|
|
AssertValid();
|
|
if (_eState == disconnected_ss)
|
|
return RPC_E_SERVER_DIED_DNE;
|
|
|
|
// Get a parameter block.
|
|
params = new SReleaseChannel(
|
|
ActuallyCallReleaseChannel,
|
|
fAsyncRelease ? CALLCAT_ASYNC : CALLCAT_INTERNALSYNC,
|
|
pChannel->GetServerApt());
|
|
|
|
if (params == NULL)
|
|
return E_OUTOFMEMORY;
|
|
|
|
CairoleDebugOut((DEB_MARSHAL, "ICS_ReleaseChannel channel id=%x\n",
|
|
pChannel->GetID()));
|
|
|
|
// Fill in the parameters.
|
|
params->count = pChannel->GetMarshalCnt();
|
|
params->service = this;
|
|
AddRef(); // released in SReleaseChannel dtor
|
|
params->async = fAsyncRelease;
|
|
params->channel_id = pChannel->GetID();
|
|
|
|
// If local, just switch threads.
|
|
if (this == LocalService())
|
|
{
|
|
params->ResetDispatchFn(ThreadReleaseChannel);
|
|
|
|
// lookup and AddRef the channel controller for this channel id.
|
|
CChannelControl *pChanCtrl = ChannelList.LookupControl(params->channel_id);
|
|
|
|
if (pChanCtrl)
|
|
{
|
|
hr = pChanCtrl->SwitchCOMThread( (STHREADCALLINFO **) ¶ms );
|
|
pChanCtrl->Release();
|
|
}
|
|
else
|
|
{
|
|
CairoleDebugOut((DEB_WARN, "ReleaseChannel: cant find pChanCtrl\n"));
|
|
hr = E_HANDLE;
|
|
}
|
|
}
|
|
|
|
// If remote, dispatch on some RPC thread.
|
|
else
|
|
{
|
|
hr = CChannelControl::GetOffCOMThread( (STHREADCALLINFO **) ¶ms );
|
|
}
|
|
|
|
if (hr != RPC_E_CALL_CANCELED)
|
|
{
|
|
delete params;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: ActuallyCallDoChannelOperation
|
|
//
|
|
// Synopsis: calls the remote channel service to do a channel operation
|
|
//
|
|
// History: 12 May 94 CraigWi Created
|
|
//
|
|
//--------------------------------------------------------------------
|
|
HRESULT ActuallyCallDoChannelOperation( STHREADCALLINFO *call )
|
|
{
|
|
SDoChannelOperation *params = (SDoChannelOperation *) call;
|
|
ULONG cRetry = 0;
|
|
HRESULT result;
|
|
|
|
// make sure we have a handle to call on
|
|
handle_t RpcHdl = params->service->GetRpcHdl();
|
|
|
|
if (!RpcHdl)
|
|
{
|
|
return E_HANDLE;
|
|
}
|
|
|
|
do
|
|
{
|
|
// Rpc call to remote RpcServiceObject
|
|
error_status_t errstat;
|
|
|
|
// Rpc call to remote RpcServiceObject
|
|
if (call->GetCallCat() == CALLCAT_SYNCHRONOUS)
|
|
result = _ICS_SyncChannelOp(RpcHdl,
|
|
params->channel_id,
|
|
call->lid(),
|
|
params->chop,
|
|
params->hapt,
|
|
¶ms->guid,
|
|
&errstat);
|
|
else
|
|
result = _ICS_InputSyncChannelOp(RpcHdl,
|
|
params->channel_id,
|
|
call->lid(),
|
|
params->chop,
|
|
params->hapt,
|
|
¶ms->guid,
|
|
&errstat);
|
|
|
|
if (errstat != 0)
|
|
{
|
|
// Convert RPC return code to HRESULT incase we exit
|
|
// the loop
|
|
result = HRESULT_FROM_WIN32(errstat);
|
|
|
|
// Should we retry the error?
|
|
if ((errstat == RPC_S_SERVER_TOO_BUSY))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// We only do this once unless there is an RPC error that
|
|
// encourages us to retry
|
|
break;
|
|
|
|
} while (++cRetry < MAX_RETRIES);
|
|
return result;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: SDoChannelOperation dtor
|
|
//
|
|
// Synopsis: Called when ActuallyDoChannelOperation is canceled. Releases
|
|
// service.
|
|
//
|
|
// Exceptions: none
|
|
//
|
|
// History: 26 June 94 AlexMit Created
|
|
// 7 July 94 CraigWi Changed to destructor
|
|
//
|
|
// Notes: Thread safe; client/server side safe
|
|
//
|
|
//--------------------------------------------------------------------
|
|
SDoChannelOperation::~SDoChannelOperation()
|
|
{
|
|
if (service)
|
|
service->Release();
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CRpcService::DoChannelOperation
|
|
//
|
|
// Synopsis: calls the remote channel service to do a channel operation
|
|
//
|
|
// Parameters: [dwChannelId] -
|
|
// [chop] - channel operation
|
|
// [pEndPoint] - our endpoint
|
|
// [hapt] - server apartment id
|
|
// [pguid] - IID for DoesSupportIID
|
|
//
|
|
// History: 12 May 94 CraigWi Created
|
|
//
|
|
//--------------------------------------------------------------------
|
|
SCODE CRpcService::DoChannelOperation(DWORD dwChannelID, DWORD chop,
|
|
HAPT hapt, const GUID *pguid)
|
|
{
|
|
TRACECALL(TRACE_RPC, "CRpcService::DoChannelOperation");
|
|
HRESULT result;
|
|
AssertValid();
|
|
|
|
// Fail if this service object has been disconnected.
|
|
if (_eState == disconnected_ss)
|
|
return RPC_E_SERVER_DIED_DNE;
|
|
|
|
// Allocate memory to pass the parameters.
|
|
SDoChannelOperation *params;
|
|
|
|
// match the decision in CRpcService::DoChannelOperation
|
|
CALLCATEGORY callcat;
|
|
if ((chop & CHOP_OPERATION) <= CHOP_TRANSFER_MARSHALCONNECTION)
|
|
callcat = CALLCAT_INTERNALINPUTSYNC;
|
|
else
|
|
callcat = CALLCAT_SYNCHRONOUS;
|
|
|
|
params = new SDoChannelOperation(
|
|
ActuallyCallDoChannelOperation,
|
|
callcat,
|
|
hapt);
|
|
|
|
if (params == NULL)
|
|
return E_OUTOFMEMORY;
|
|
|
|
CairoleDebugOut((DEB_MARSHAL, "ICS_DoChannelOperation channel id=%x\n",
|
|
dwChannelID));
|
|
|
|
if (pguid != NULL)
|
|
params->guid = *pguid;
|
|
params->chop = chop;
|
|
params->hapt = hapt;
|
|
params->service = this;
|
|
params->channel_id = dwChannelID;
|
|
|
|
AddRef(); // released in SDoChannelOperation dtor
|
|
|
|
// If local, just switch threads.
|
|
if (this == LocalService())
|
|
{
|
|
params->ResetDispatchFn(ThreadDoChannelOperation);
|
|
CChannelControl *pChanCtrl = ChannelList.LookupControl(dwChannelID);
|
|
|
|
if (pChanCtrl)
|
|
{
|
|
result = pChanCtrl->SwitchCOMThread( (STHREADCALLINFO **) ¶ms );
|
|
pChanCtrl->Release();
|
|
}
|
|
else
|
|
{
|
|
CairoleDebugOut((DEB_WARN, "DoChannelOp Cant find pChanCtrl\n"));
|
|
result = E_HANDLE;
|
|
}
|
|
}
|
|
|
|
// If remote, dispatch on some RPC thread.
|
|
else
|
|
{
|
|
result = CChannelControl::GetOffCOMThread( (STHREADCALLINFO **) ¶ms );
|
|
}
|
|
|
|
if (result != RPC_E_CALL_CANCELED)
|
|
{
|
|
delete params;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CRpcService::TransferMarshalConnection
|
|
//
|
|
// Synopsis: calls the remote channel service increment marshal count.
|
|
//
|
|
// History: 27-Nov-93 AlexMit Created
|
|
// 12-May-94 CraigWi Funneled through DoChannelOperation
|
|
//
|
|
//--------------------------------------------------------------------
|
|
SCODE CRpcService::TransferMarshalConnection( DWORD dwChannelID )
|
|
{
|
|
TRACECALL(TRACE_RPC, "CRpcService::TransferMarshalConnection");
|
|
AssertValid();
|
|
|
|
return DoChannelOperation(dwChannelID, CHOP_TRANSFER_MARSHALCONNECTION,
|
|
haptNULL, NULL);
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------------
|
|
//
|
|
// Member: CRpcService::AddMarshalConnection, public
|
|
//
|
|
// Synopsis: does the part of normal marshaling that we can't do on the client
|
|
//
|
|
// History: 14-May-94 CraigWi Created
|
|
//
|
|
//-------------------------------------------------------------------------
|
|
SCODE CRpcService::AddMarshalConnection(DWORD dwChannelID, HAPT hapt, REFOID roid)
|
|
{
|
|
TRACECALL(TRACE_RPC, "CRpcService::AddMarshalConnection");
|
|
AssertValid();
|
|
|
|
return DoChannelOperation(dwChannelID,
|
|
CHOP_ADD_MARSHALCONNECTION | CHOPFLAG_CHECK_OID_ENDPOINT_APT,
|
|
hapt, &roid);
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------------
|
|
//
|
|
// Member: CRpcService::RemoveMarshalConnection, public
|
|
//
|
|
// Synopsis:
|
|
//
|
|
// History: 14-May-94 CraigWi Created
|
|
//
|
|
//-------------------------------------------------------------------------
|
|
SCODE CRpcService::RemoveMarshalConnection(DWORD dwChannelID)
|
|
{
|
|
TRACECALL(TRACE_RPC, "CRpcService::RemoveMarshalConnection");
|
|
AssertValid();
|
|
|
|
return DoChannelOperation(dwChannelID, CHOP_REMOVE_MARSHALCONNECTION,
|
|
haptNULL, NULL);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CRpcService::QueryObjectInterface
|
|
//
|
|
// Synopsis: calls the remote object to instantiate a new interface
|
|
// on the object.
|
|
//
|
|
// History: 23-Nov-93 Rickhi Created
|
|
// 12-May-94 CraigWi Funneled through DoChannelOperation
|
|
//
|
|
//--------------------------------------------------------------------
|
|
SCODE CRpcService::QueryObjectInterface(DWORD channel_id, REFIID riid)
|
|
{
|
|
TRACECALL(TRACE_RPC, "CRpcService::QueryObjectInterface");
|
|
AssertValid();
|
|
|
|
return DoChannelOperation(channel_id, CHOP_DOESSUPPORTIID,
|
|
haptNULL, &riid);
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------------
|
|
//
|
|
// Member: CRpcService::LockObjectConnection, public
|
|
//
|
|
// Synopsis: locks/unlocks the connection for the container
|
|
//
|
|
// History: 14-May-94 CraigWi Created
|
|
//
|
|
//-------------------------------------------------------------------------
|
|
SCODE CRpcService::LockObjectConnection(DWORD dwChannelID, BOOL fLock, BOOL fLastUnlockCloses)
|
|
{
|
|
TRACECALL(TRACE_RPC, "CRpcService::LockObjectConnection");
|
|
AssertValid();
|
|
|
|
DWORD chop = (fLock ? CHOP_LOCK_CONNECTION : CHOP_UNLOCK_CONNECTION);
|
|
if (fLastUnlockCloses)
|
|
chop |= CHOPFLAG_LASTUNLOCKCLOSES;
|
|
|
|
return DoChannelOperation(dwChannelID, chop, haptNULL, NULL);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CRpcService::Disconnect, public
|
|
//
|
|
// Synopsis: Called by rundown and at CoUninitialize; simulates
|
|
// a simulatneous disconnect by all clients for which
|
|
// this service object represents.
|
|
//
|
|
// History: 27-Nov-93 AlexMit Created (was in Rundown)
|
|
// 22-Feb-94 CraigWi Separated out Disconnect
|
|
//
|
|
//--------------------------------------------------------------------
|
|
void CRpcService::Disconnect()
|
|
{
|
|
AssertValid();
|
|
|
|
{
|
|
// single thread access
|
|
CLock lck(_mxs);
|
|
|
|
_eState = disconnected_ss;
|
|
|
|
// At this point the lock goes out of scope. We must not hold it
|
|
// across DisconnectService since that may switch threads.
|
|
}
|
|
|
|
// this works even during shutdown since before the channel list is cleaned
|
|
// up, the semaphores and the channel control state (which prevents thread
|
|
// switches after shutdown is called) keep things working correctly.
|
|
// After the channel list is cleared, the disconnect won't find a match.
|
|
|
|
ChannelList.DisconnectService(this);
|
|
}
|
|
|
|
|
|
#if DBG == 1
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CRpcService::AssertValid
|
|
//
|
|
// Synopsis: Validates that the state of the object is consistent.
|
|
//
|
|
// History: 21-Jan-94 CraigWi Created.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
void CRpcService::AssertValid()
|
|
{
|
|
Win4Assert(_ulRefCnt < 0x7fff && "Service ref count unreasonably high");
|
|
|
|
if (this == LocalService())
|
|
{
|
|
if (IsServiceListen())
|
|
{
|
|
if (_CEp.GetSEp() != NULL)
|
|
{
|
|
Win4Assert(GoodSEp(_CEp.GetSEp()));
|
|
}
|
|
}
|
|
|
|
#ifndef _CHICAGO_
|
|
Win4Assert(_hRpc.Handle() == NULL);
|
|
Win4Assert(_pContext == NULL);
|
|
Win4Assert(_eState == client_ss);
|
|
#endif
|
|
}
|
|
else if (_eState == client_ss)
|
|
{
|
|
#ifndef _CHICAGO_
|
|
Win4Assert(_CEp.GetSEp() != NULL);
|
|
#endif
|
|
if (_CEp.GetSEp() != NULL)
|
|
{
|
|
Win4Assert(GoodSEp(_CEp.GetSEp()));
|
|
}
|
|
|
|
// if _pContext is set, must have binding handle
|
|
if (_pContext != NULL)
|
|
Win4Assert(_hRpc.Handle() != NULL);
|
|
|
|
if (_hRpc.Handle() != NULL)
|
|
{
|
|
unsigned int timeout;
|
|
Win4Assert(RpcMgmtInqComTimeout(_hRpc.Handle(), &timeout) == RPC_S_OK);
|
|
}
|
|
}
|
|
else if (_eState == disconnected_ss)
|
|
{
|
|
}
|
|
else
|
|
{
|
|
Win4Assert(!"Service Object with invalid state");
|
|
}
|
|
}
|
|
#endif // DBG == 1
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CSrvList::FindSRVFromEP
|
|
//
|
|
// Synopsis: finds an Rpc service object using the specified Endpoint
|
|
//
|
|
// Arguments: [pSEP] - endpoint structure for the service object
|
|
// [fCreate] - TRUE means create if not found
|
|
//
|
|
// History: 23-Nov-93 Rickhi Created
|
|
//
|
|
// Notes: Thread Safe
|
|
//
|
|
//--------------------------------------------------------------------
|
|
CRpcService * CSrvList::FindSRVFromEP(SEndPoint *pSEp, BOOL fCreate)
|
|
{
|
|
CairoleDebugOut((DEB_ENDPNT, "CSrvList::FindSRVFromEP this:%p; pSEp:%p\n", this, pSEp));
|
|
// single thread access to this code
|
|
COleStaticLock lck(sg_SrvListLock);
|
|
|
|
// starting from the list head
|
|
CRpcService *pSrv = _List.first();
|
|
|
|
while (pSrv)
|
|
{
|
|
// look for a match in the endpoints; do not find disconnected
|
|
// service objects; this may occur during shutdown.
|
|
if (pSrv->_eState != disconnected_ss && pSrv->IsEqualEp(pSEp))
|
|
{
|
|
Win4Assert( pSrv->_eState != disconnected_ss );
|
|
pSrv->AddRef();
|
|
pSrv->AssertValid();
|
|
return pSrv;
|
|
}
|
|
pSrv = _List.next(pSrv);
|
|
}
|
|
|
|
// no match found, make a new one
|
|
if (fCreate)
|
|
{
|
|
HRESULT hr = E_OUTOFMEMORY;
|
|
pSrv = new CRpcService(pSEp, hr);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
#if DBG==1
|
|
if (LocalService() != NULL)
|
|
pSrv->AssertValid();
|
|
#endif
|
|
_List.insert_at_end(pSrv);
|
|
}
|
|
else if (pSrv)
|
|
{
|
|
// allocation succeeded, but there was some other
|
|
// constructor error
|
|
delete pSrv;
|
|
pSrv = NULL;
|
|
}
|
|
}
|
|
|
|
CairoleDebugOut((DEB_ENDPNT, "CSrvList::FindSRVFromEP this:%p; pSerive:%p done\n", this, pSrv));
|
|
return pSrv;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CSrvList::FindSRVFromContext
|
|
//
|
|
// Synopsis: finds an Rpc service object using the specifed
|
|
// context handle.
|
|
//
|
|
// Arguments: [hObjCtx] - Context handle provided by RPC
|
|
// [fRemove] - TRUE means remove the object from the list
|
|
//
|
|
// Returns: NULL - handle could not be validated by us.
|
|
// ~NULL - we found the object.
|
|
//
|
|
// History: 23-Nov-93 Rickhi Created
|
|
//
|
|
// Notes: Thread Safe
|
|
//
|
|
//--------------------------------------------------------------------
|
|
CRpcService *CSrvList::FindSRVFromContext(POBJCTX phObjCtx, BOOL fRemove)
|
|
{
|
|
// the context handle should be a pointer to a service object.
|
|
CRpcService *pSrv = (CRpcService *)phObjCtx;
|
|
|
|
// single thread access to this code
|
|
COleStaticLock lck(sg_SrvListLock);
|
|
|
|
CRpcService *linkp = _List.first();
|
|
|
|
while (linkp != NULL)
|
|
{
|
|
if (linkp == pSrv)
|
|
{
|
|
linkp->AddRef();
|
|
|
|
if (fRemove)
|
|
{
|
|
// remove this service object from the list
|
|
linkp->delete_self();
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
linkp = _List.next(linkp);
|
|
}
|
|
|
|
return linkp;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CSrvList::Cleanup
|
|
//
|
|
// Synopsis: Releases the service objects in the list.
|
|
//
|
|
// History: 23-Nov-93 Rickhi Created
|
|
//
|
|
// Notes: C++ rules dictate that the class destructor gets called
|
|
// before member destructors, which get called before base
|
|
// class destructors.
|
|
//
|
|
// Notes: Thread Safe
|
|
//
|
|
//--------------------------------------------------------------------
|
|
void CSrvList::Cleanup(void)
|
|
{
|
|
COleStaticLock lck(sg_SrvListLock);
|
|
|
|
CRpcService *linkp;
|
|
|
|
// The rundown routine will validate its pointer from the RPC runtime
|
|
// and takes a mutex which will exclude this object from being Released
|
|
// until the call completes.
|
|
|
|
while ((linkp = _List.first()) != NULL)
|
|
{
|
|
linkp->Release();
|
|
}
|
|
}
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CSrvList::~CSrvList
|
|
//
|
|
// Synopsis: Leaks the entire list without freeing memory or calling
|
|
// destructors. Since the service list is static, it
|
|
// gets destroyed after all DLLs are uninitialized. This
|
|
// causes some DLLs to crash.
|
|
//
|
|
// The destructor is only called at process detach. The
|
|
// list gets cleaned up by CoUninitialize.
|
|
//
|
|
// History: 7 Nov 94 AlexMit Created
|
|
//
|
|
//--------------------------------------------------------------------
|
|
CSrvList::~CSrvList()
|
|
{
|
|
// Yes, this is really what I want to do. Read the header.
|
|
_List.no_cleanup();
|
|
}
|