|
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
/*============================================================
**
** File: RemotingProxy.cs
**
**
** Purpose: Defines the general purpose remoting proxy
**
**
===========================================================*/
namespace System.Runtime.Remoting.Proxies {
using System.Threading;
using System.Runtime.Remoting.Activation;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Contexts;
using System.Runtime.Remoting.Channels;
using System;
using MethodInfo = System.Reflection.MethodInfo;
using MethodBase = System.Reflection.MethodBase;
using System.Globalization;
// Remoting proxy
[System.Security.SecurityCritical] // auto-generated
internal class RemotingProxy : RealProxy, IRemotingTypeInfo
{
// Static Fields
private static MethodInfo _getTypeMethod = typeof(System.Object).GetMethod("GetType");
private static MethodInfo _getHashCodeMethod = typeof(System.Object).GetMethod("GetHashCode");
private static RuntimeType s_typeofObject = (RuntimeType)typeof(System.Object);
private static RuntimeType s_typeofMarshalByRefObject = (RuntimeType)typeof(System.MarshalByRefObject);
//*******************WARNING******************************************
// If you change the names of these fields then change the corresponding
// names in remoting.cpp
//********************************************************************
private ConstructorCallMessage _ccm;
private int _ctorThread;
// Constructor
public RemotingProxy(Type serverType)
: base(serverType)
{
}
private RemotingProxy()
{
// Prevent anyone from creating a blank instance of a proxy
// without the underlying server type
}
internal int CtorThread
{
get
{
return _ctorThread;
}
set
{
//NOTE : the assert below is correct for activated objects.
//But for a connected object (where new XXX() does a Connect()
//the InternalActivate codepath may execute twice .. since
//we would be returning the same proxy for multiple calls to
//new XXX() & JIT would try to execute the default .ctor on
//the returned proxy.
//BCLDebug.Assert(_ctorThread == 0, "ctorThread already set??");
_ctorThread = value;
}
}
// This is used when a TP is called with SyncProcessMessage
internal static IMessage CallProcessMessage(IMessageSink ms,
IMessage reqMsg,
ArrayWithSize proxySinks,
Thread currentThread,
Context currentContext,
bool bSkippingContextChain)
{
// Notify Dynamic Sinks: CALL starting
if (proxySinks != null)
{
DynamicPropertyHolder.NotifyDynamicSinks(
reqMsg,
proxySinks,
true, // bCliSide
true, // bStart
false); // bAsync
}
bool bHasDynamicSinks = false;
if (bSkippingContextChain)
{
// this would have been done in the client context terminator sink
bHasDynamicSinks =
currentContext.NotifyDynamicSinks(reqMsg,
true, // bCliSide
true, // bStart
false, // bAsync
true); // bNotifyGlobals
ChannelServices.NotifyProfiler(reqMsg, RemotingProfilerEvent.ClientSend);
}
if (ms == null)
{
throw new RemotingException(
Environment.GetResourceString(
"Remoting_Proxy_NoChannelSink"));
}
IMessage retMsg = ms.SyncProcessMessage(reqMsg);
if (bSkippingContextChain)
{
// this would have been done in the client context terminator sink
ChannelServices.NotifyProfiler(retMsg, RemotingProfilerEvent.ClientReceive);
if (bHasDynamicSinks)
{
currentContext.NotifyDynamicSinks(
retMsg,
true, // bCliSide
false, // bStart
false, // bAsync
true); // bNotifyGlobals
}
}
IMethodReturnMessage mrm = retMsg as IMethodReturnMessage;
if (retMsg == null || mrm == null)
{
throw new RemotingException(
Environment.GetResourceString("Remoting_Message_BadType"));
}
// notify DynamicSinks: CALL returned
if (proxySinks != null)
{
DynamicPropertyHolder.NotifyDynamicSinks(
retMsg,
proxySinks,
true, // bCliSide
false, // bStart
false); // bAsync
}
return retMsg;
}
// Implement Invoke
[System.Security.SecurityCritical]
public override IMessage Invoke(IMessage reqMsg)
{
// Dispatch based on whether its a constructor call
// or a method call
IConstructionCallMessage ccm = reqMsg as IConstructionCallMessage;
if(ccm != null)
{
// Activate
return InternalActivate(ccm);
}
else
{
// Handle regular method calls
// Check that the initialization has completed
if(!Initialized)
{
// This covers the case where an object may call out
// on another object passing its "this" pointer during its
// .ctor.
// The other object attempting to call on the this pointer
// (in x-context case) would be calling on a proxy not
// marked fully initialized.
// <
// Let the original constructor thread go through but
// throw for other threads.
if (CtorThread == Thread.CurrentThread.GetHashCode())
{
ServerIdentity srvId = IdentityObject as ServerIdentity;
BCLDebug.Assert(
srvId != null
&&
((ServerIdentity)IdentityObject).ServerContext != null,
"Wrap may associate with wrong context!");
// If we are here, the server object passed itself
// out to another x-context object during the .ctor
// That guy is calling us back. Let us call Wrap()
// earlier than usual so that envoy & channel sinks
// get set up!
// <
RemotingServices.Wrap(
(ContextBoundObject) this.UnwrappedServerObject);
}
else
{
// Throw an exception to indicate that we are
// calling on a proxy while the constructor call
// is still running.
throw new RemotingException(Environment.GetResourceString("Remoting_Proxy_InvalidCall"));
}
}
// Dispatch
int callType = Message.Sync;
Message msg = reqMsg as Message;
if (msg != null)
{
callType = msg.GetCallType();
}
return InternalInvoke((IMethodCallMessage)reqMsg, false, callType);
}
} // Invoke
// This is called for all remoted calls on a TP except Ctors
// The method called may be Sync, Async or OneWay(special case of Async)
// In the Async case we come here for both BeginInvoke & EndInvoke
internal virtual IMessage InternalInvoke(
IMethodCallMessage reqMcmMsg, bool useDispatchMessage, int callType)
{
Message reqMsg = reqMcmMsg as Message;
if ((reqMsg == null) && (callType != Message.Sync))
{
// Only the synchronous call type is supported for messages that
// aren't of type Message.
throw new RemotingException(
Environment.GetResourceString("Remoting_Proxy_InvalidCallType"));
}
IMessage retMsg = null;
Thread currentThread = Thread.CurrentThread;
// pick up call context from the thread
LogicalCallContext cctx = currentThread.GetMutableExecutionContext().LogicalCallContext;
Identity idObj = IdentityObject;
ServerIdentity serverID = idObj as ServerIdentity;
if ((null != serverID) && idObj.IsFullyDisconnected())
{
throw new ArgumentException(
Environment.GetResourceString("Remoting_ServerObjectNotFound", reqMcmMsg.Uri));
}
// Short-circuit calls to Object::GetType and Object::GetHashCode
MethodBase mb = reqMcmMsg.MethodBase;
if(_getTypeMethod == mb)
{
// Time to load the true type of the remote object....
Type t = GetProxiedType();
return new ReturnMessage(t, null, 0, cctx, reqMcmMsg);
}
if (_getHashCodeMethod == mb)
{
int hashCode = idObj.GetHashCode();
return new ReturnMessage(hashCode, null, 0, cctx, reqMcmMsg);
}
// check for channel sink
if (idObj.ChannelSink == null)
{
IMessageSink chnlSink = null;
IMessageSink envoySink = null;
// If channelSink is null try to Create them again
// the objref should be correctly fixed up at this point
if(!idObj.ObjectRef.IsObjRefLite())
{
RemotingServices.CreateEnvoyAndChannelSinks(null, idObj.ObjectRef, out chnlSink, out envoySink);
}
else
{
RemotingServices.CreateEnvoyAndChannelSinks(idObj.ObjURI, null, out chnlSink, out envoySink);
}
// Set the envoy and channel sinks in a thread safe manner
RemotingServices.SetEnvoyAndChannelSinks(idObj, chnlSink, envoySink);
// If the channel sink is still null then throw
if(idObj.ChannelSink == null)
{
throw new RemotingException(
Environment.GetResourceString("Remoting_Proxy_NoChannelSink"));
}
}
// Set the identity in the message object
IInternalMessage iim = (IInternalMessage)reqMcmMsg;
iim.IdentityObject = idObj;
if (null != serverID)
{
Message.DebugOut("Setting serveridentity on message \n");
iim.ServerIdentityObject = serverID;
}
else
{
// We need to set the URI only for identities that
// are not the server identities. The uri is used to
// dispatch methods for objects outside the appdomain.
// Inside the appdomain (xcontext case) we dispatch
// by getting the server object from the server identity.
iim.SetURI(idObj.URI);
}
Message.DebugOut("InternalInvoke. Dispatching based on class type\n");
AsyncResult ar = null;
switch (callType)
{
case Message.Sync:
Message.DebugOut("RemotingProxy.Invoke Call: SyncProcessMsg\n");
BCLDebug.Assert(!useDispatchMessage,"!useDispatchMessage");
bool bSkipContextChain = false;
Context currentContext = currentThread.GetCurrentContextInternal();
IMessageSink nextSink = idObj.EnvoyChain;
// if we are in the default context, there can be no
// client context chain, so we can skip the intermediate
// calls if there are no envoy sinks
if (currentContext.IsDefaultContext)
{
if (nextSink is EnvoyTerminatorSink)
{
bSkipContextChain = true;
// jump directly to the channel sink
nextSink = idObj.ChannelSink;
}
}
retMsg = CallProcessMessage(nextSink,
reqMcmMsg,
idObj.ProxySideDynamicSinks,
currentThread,
currentContext,
bSkipContextChain);
break;
case Message.BeginAsync:
case Message.BeginAsync | Message.OneWay:
// For async calls we clone the call context from the thread
// This is a limited clone (we dont deep copy the user data)
cctx = (LogicalCallContext) cctx.Clone();
iim.SetCallContext(cctx);
ar = new AsyncResult(reqMsg);
InternalInvokeAsync(ar, reqMsg, useDispatchMessage, callType);
Message.DebugOut("Propagate out params for BeginAsync\n");
retMsg = new ReturnMessage(ar, null, 0, null/*cctx*/, reqMsg);
break;
case Message.OneWay:
// For async calls we clone the call context from the thread
// This is a limited clone (we dont deep copy the user data)
cctx = (LogicalCallContext) cctx.Clone();
iim.SetCallContext(cctx);
InternalInvokeAsync(null, reqMsg, useDispatchMessage, callType);
retMsg = new ReturnMessage(null, null, 0, null/*cctx*/, reqMcmMsg);
break;
case (Message.EndAsync | Message.OneWay):
retMsg = new ReturnMessage(null, null, 0, null/*cctx*/, reqMcmMsg);
break;
case Message.EndAsync:
// For endAsync, we merge back the returned callContext
// into the thread's callContext
retMsg = RealProxy.EndInvokeHelper(reqMsg, true);
break;
}
return retMsg;
}
// This is called from InternalInvoke above when someone makes an
// Async (or a one way) call on a TP
internal void InternalInvokeAsync(IMessageSink ar, Message reqMsg,
bool useDispatchMessage, int callType)
{
IMessageCtrl cc = null;
Identity idObj = IdentityObject;
ServerIdentity serverID = idObj as ServerIdentity;
MethodCall cpyMsg= new MethodCall(reqMsg);
IInternalMessage iim = ((IInternalMessage)cpyMsg);
// Set the identity in the message object
iim.IdentityObject = idObj;
if (null != serverID)
{
Message.DebugOut("Setting SrvID on deser msg\n");
iim.ServerIdentityObject = serverID;
}
if (useDispatchMessage)
{
Message.DebugOut(
"RemotingProxy.Invoke: Calling AsyncDispatchMessage\n");
BCLDebug.Assert(ar != null,"ar != null");
BCLDebug.Assert( (callType & Message.BeginAsync) != 0,
"BeginAsync flag not set!");
Message.DebugOut("Calling AsynDispatchMessage \n");
cc = ChannelServices.AsyncDispatchMessage(
cpyMsg,
((callType & Message.OneWay) != 0)
? null : ar);
}
else if (null != idObj.EnvoyChain)
{
Message.DebugOut("RemotingProxy.Invoke: Calling AsyncProcessMsg on the envoy chain\n");
cc = idObj.EnvoyChain.AsyncProcessMessage(
cpyMsg,
((callType & Message.OneWay) != 0)
? null : ar);
}
else
{
// Channel sink cannot be null since it is the last sink in
// the client context
// Assert if Invoke is called without a channel sink
BCLDebug.Assert(false, "How did we get here?");
throw new InvalidOperationException(
Environment.GetResourceString("Remoting_Proxy_InvalidState"));
}
if ((callType & Message.BeginAsync) != 0)
{
if ((callType & Message.OneWay) != 0)
{
ar.SyncProcessMessage(null);
}
}
}
// New method for activators.
// This gets called during remoting intercepted activation when
// JIT tries to run a constructor on a TP (which remoting gave it
// in place of an actual uninitialized instance of the expected type)
private IConstructionReturnMessage InternalActivate(IConstructionCallMessage ctorMsg)
{
// Remember the hashcode of the constructor thread.
CtorThread = Thread.CurrentThread.GetHashCode();
IConstructionReturnMessage ctorRetMsg = ActivationServices.Activate(this, ctorMsg);
// Set the flag to indicate that the object is initialized
// Note: this assert is not valid for WKOs
//BCLDebug.Assert(!Initialized, "Proxy marked as initialized before activation call completed");
Initialized = true;
return ctorRetMsg;
}
// Invoke for case where call is in the same context as the server object
// (This special static method is used for AsyncDelegate-s ... this is called
// directly from the EE)
private static void Invoke(Object NotUsed, ref MessageData msgData)
{
Message m = new Message();
m.InitFields(msgData);
Object thisPtr = m.GetThisPtr();
Delegate d;
if ((d = thisPtr as Delegate) != null)
{
// <
RemotingProxy rp = (RemotingProxy)
RemotingServices.GetRealProxy(d.Target);
if (rp != null)
{
rp.InternalInvoke(m, true, m.GetCallType());
}
else
{
int callType = m.GetCallType();
AsyncResult ar;
switch (callType)
{
case Message.BeginAsync:
case Message.BeginAsync | Message.OneWay:
// pick up call context from the thread
m.Properties[Message.CallContextKey] =
Thread.CurrentThread.GetMutableExecutionContext().LogicalCallContext.Clone();
ar = new AsyncResult(m);
AgileAsyncWorkerItem workItem =
new AgileAsyncWorkerItem(
m,
((callType & Message.OneWay) != 0) ?
null : ar, d.Target);
ThreadPool.QueueUserWorkItem(
new WaitCallback(
AgileAsyncWorkerItem.ThreadPoolCallBack),
workItem);
if ((callType & Message.OneWay) != 0)
{
ar.SyncProcessMessage(null);
}
m.PropagateOutParameters(null, ar);
break;
case (Message.EndAsync | Message.OneWay):
return;
case Message.EndAsync:
// This will also merge back the call context
// onto the thread that called EndAsync
RealProxy.EndInvokeHelper(m, false);
break;
default:
BCLDebug.Assert(
false,
"Should never be here. Sync delegate code for agile object ended up in remoting");
break;
}
}
}
else
{
// Static invoke called with incorrect this pointer ...
throw new RemotingException(
Environment.GetResourceString(
"Remoting_Default"));
}
}
internal ConstructorCallMessage ConstructorMessage
{
get
{
return _ccm;
}
set
{
_ccm = value;
}
}
//
// IRemotingTypeInfo interface
//
// Obtain the fully qualified name of the type that the proxy represents
public String TypeName
{
[System.Security.SecurityCritical]
get
{
return GetProxiedType().FullName;
}
[System.Security.SecurityCritical]
set
{
throw new NotSupportedException();
}
}
// interop methods
[System.Security.SecurityCritical]
public override IntPtr GetCOMIUnknown(bool fIsBeingMarshalled)
{
IntPtr pUnk = IntPtr.Zero;
Object otp = GetTransparentProxy();
bool fIsXProcess = RemotingServices.IsObjectOutOfProcess(otp);
if (fIsXProcess)
{
// we are in a different process
if (fIsBeingMarshalled)
{
// we need to go to the server to get the real IUnknown
pUnk = MarshalByRefObject.GetComIUnknown((MarshalByRefObject)otp);
}
else
{
// create an IUnknown here
pUnk = MarshalByRefObject.GetComIUnknown((MarshalByRefObject)otp);
}
}
else
{
bool fIsXAppDomain = RemotingServices.IsObjectOutOfAppDomain(otp);
// we are in the same proces, ask the object for its IUnknown
if (fIsXAppDomain)
{
// do an appdomain switch
pUnk = ((MarshalByRefObject)otp).GetComIUnknown(fIsBeingMarshalled);
}
else
{
// otherwise go ahead and create a CCW here
pUnk = MarshalByRefObject.GetComIUnknown((MarshalByRefObject)otp);
}
}
return pUnk;
}
[System.Security.SecurityCritical]
public override void SetCOMIUnknown(IntPtr i)
{
// for now ignore this
}
// Check whether we can cast the transparent proxy to the given type
[System.Security.SecurityCritical]
public bool CanCastTo(Type castType, Object o)
{
if (castType == null)
throw new ArgumentNullException("castType");
RuntimeType rtType = castType as RuntimeType;
if (rtType == null)
throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeType"));
bool fCastOK = false;
// The identity should be non-null
BCLDebug.Assert(null != IdentityObject,"null != IdentityObject");
Message.DebugOut("CheckCast for identity " + IdentityObject.GetType());
if ((rtType == s_typeofObject) ||
(rtType == s_typeofMarshalByRefObject))
{
return true;
}
// Get the objref of the proxy
ObjRef oRef = IdentityObject.ObjectRef;
// If the object ref is non-null then check against the type info
// stored in the it
if (null != oRef)
{
Object oTP = GetTransparentProxy();
// Check that there is a matching type in the server object
// hierarchy represented in the objref
Message.DebugOut("Calling CanCastTo for type " + rtType);
IRemotingTypeInfo typeInfo = oRef.TypeInfo;
if(null != typeInfo)
{
fCastOK = typeInfo.CanCastTo(rtType, oTP);
if (!fCastOK && typeInfo.GetType()==typeof(TypeInfo) && oRef.IsWellKnown() )
{
fCastOK = CanCastToWK(rtType);
}
}
else
{
if (oRef.IsObjRefLite())
{
// we should do a dynamic cast across the network
fCastOK = MarshalByRefObject.CanCastToXmlTypeHelper(rtType, (MarshalByRefObject)o);
}
}
}
// This is a well known object which does not have a backing ObjRef
else
{
fCastOK = CanCastToWK(rtType);
}
return fCastOK;
}
// WellKnown proxies we always allow casts to interfaces, and allow
// casting down a single branch in the type hierarchy (both are on good
// faith. The calls are failed on server side if a bogus cast is done)
bool CanCastToWK(Type castType)
{
Message.DebugOut( "CheckCast for well known objects and type " + castType);
bool fCastOK = false;
// Check whether the type to which we want to cast is
// compatible with the current type
if(castType.IsClass)
{
fCastOK = GetProxiedType().IsAssignableFrom(castType);
}
else
{
// NOTE: we are coming here also for x-context proxies
// when unmanaged code cannot determine if the cast is not
// okay <
if (!(IdentityObject is ServerIdentity))
{
BCLDebug.Assert(
IdentityObject.URI != null,
"Bad WellKnown ID");
// Always allow interface casts to succeed. If the
// interface is not supported by the well known object
// then we will throw an exception when the interface
// is invoked.
fCastOK = true;
}
}
return fCastOK;
}
}
internal class AgileAsyncWorkerItem
{
private IMethodCallMessage _message;
private AsyncResult _ar;
private Object _target;
[System.Security.SecurityCritical] // auto-generated
public AgileAsyncWorkerItem(IMethodCallMessage message, AsyncResult ar, Object target)
{
_message = new MethodCall(message);
_ar = ar;
_target = target;
}
[System.Security.SecurityCritical] // auto-generated
public static void ThreadPoolCallBack(Object o)
{
((AgileAsyncWorkerItem) o).DoAsyncCall();
}
[System.Security.SecurityCritical] // auto-generated
public void DoAsyncCall()
{
(new StackBuilderSink(_target)).AsyncProcessMessage(_message, _ar);
}
}
}
|