// ==++== // // 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); } } }