3 // Copyright (c) Microsoft Corporation. All rights reserved.
6 /*============================================================
8 ** File: RemotingProxy.cs
11 ** Purpose: Defines the general purpose remoting proxy
14 ===========================================================*/
15 namespace System.Runtime.Remoting.Proxies {
16 using System.Threading;
17 using System.Runtime.Remoting.Activation;
18 using System.Runtime.Remoting.Messaging;
19 using System.Runtime.Remoting.Contexts;
20 using System.Runtime.Remoting.Channels;
22 using MethodInfo = System.Reflection.MethodInfo;
23 using MethodBase = System.Reflection.MethodBase;
24 using System.Globalization;
26 [System.Security.SecurityCritical] // auto-generated
27 internal class RemotingProxy : RealProxy, IRemotingTypeInfo
30 private static MethodInfo _getTypeMethod = typeof(System.Object).GetMethod("GetType");
31 private static MethodInfo _getHashCodeMethod = typeof(System.Object).GetMethod("GetHashCode");
33 private static RuntimeType s_typeofObject = (RuntimeType)typeof(System.Object);
34 private static RuntimeType s_typeofMarshalByRefObject = (RuntimeType)typeof(System.MarshalByRefObject);
36 //*******************WARNING******************************************
37 // If you change the names of these fields then change the corresponding
38 // names in remoting.cpp
39 //********************************************************************
40 private ConstructorCallMessage _ccm;
41 private int _ctorThread;
45 public RemotingProxy(Type serverType)
50 private RemotingProxy()
52 // Prevent anyone from creating a blank instance of a proxy
53 // without the underlying server type
56 internal int CtorThread
64 //NOTE : the assert below is correct for activated objects.
65 //But for a connected object (where new XXX() does a Connect()
66 //the InternalActivate codepath may execute twice .. since
67 //we would be returning the same proxy for multiple calls to
68 //new XXX() & JIT would try to execute the default .ctor on
71 //BCLDebug.Assert(_ctorThread == 0, "ctorThread already set??");
76 // This is used when a TP is called with SyncProcessMessage
77 internal static IMessage CallProcessMessage(IMessageSink ms,
79 ArrayWithSize proxySinks,
81 Context currentContext,
82 bool bSkippingContextChain)
84 // Notify Dynamic Sinks: CALL starting
85 if (proxySinks != null)
87 DynamicPropertyHolder.NotifyDynamicSinks(
95 bool bHasDynamicSinks = false;
96 if (bSkippingContextChain)
98 // this would have been done in the client context terminator sink
100 currentContext.NotifyDynamicSinks(reqMsg,
104 true); // bNotifyGlobals
106 ChannelServices.NotifyProfiler(reqMsg, RemotingProfilerEvent.ClientSend);
111 throw new RemotingException(
112 Environment.GetResourceString(
113 "Remoting_Proxy_NoChannelSink"));
116 IMessage retMsg = ms.SyncProcessMessage(reqMsg);
118 if (bSkippingContextChain)
120 // this would have been done in the client context terminator sink
121 ChannelServices.NotifyProfiler(retMsg, RemotingProfilerEvent.ClientReceive);
123 if (bHasDynamicSinks)
125 currentContext.NotifyDynamicSinks(
130 true); // bNotifyGlobals
134 IMethodReturnMessage mrm = retMsg as IMethodReturnMessage;
135 if (retMsg == null || mrm == null)
137 throw new RemotingException(
138 Environment.GetResourceString("Remoting_Message_BadType"));
141 // notify DynamicSinks: CALL returned
142 if (proxySinks != null)
144 DynamicPropertyHolder.NotifyDynamicSinks(
157 [System.Security.SecurityCritical]
158 public override IMessage Invoke(IMessage reqMsg)
160 // Dispatch based on whether its a constructor call
163 IConstructionCallMessage ccm = reqMsg as IConstructionCallMessage;
168 return InternalActivate(ccm);
172 // Handle regular method calls
174 // Check that the initialization has completed
177 // This covers the case where an object may call out
178 // on another object passing its "this" pointer during its
180 // The other object attempting to call on the this pointer
181 // (in x-context case) would be calling on a proxy not
182 // marked fully initialized.
187 // Let the original constructor thread go through but
188 // throw for other threads.
189 if (CtorThread == Thread.CurrentThread.GetHashCode())
191 ServerIdentity srvId = IdentityObject as ServerIdentity;
195 ((ServerIdentity)IdentityObject).ServerContext != null,
196 "Wrap may associate with wrong context!");
198 // If we are here, the server object passed itself
199 // out to another x-context object during the .ctor
200 // That guy is calling us back. Let us call Wrap()
201 // earlier than usual so that envoy & channel sinks
207 RemotingServices.Wrap(
208 (ContextBoundObject) this.UnwrappedServerObject);
213 // Throw an exception to indicate that we are
214 // calling on a proxy while the constructor call
216 throw new RemotingException(Environment.GetResourceString("Remoting_Proxy_InvalidCall"));
222 int callType = Message.Sync;
223 Message msg = reqMsg as Message;
226 callType = msg.GetCallType();
229 return InternalInvoke((IMethodCallMessage)reqMsg, false, callType);
235 // This is called for all remoted calls on a TP except Ctors
236 // The method called may be [....], Async or OneWay(special case of Async)
237 // In the Async case we come here for both BeginInvoke & EndInvoke
238 internal virtual IMessage InternalInvoke(
239 IMethodCallMessage reqMcmMsg, bool useDispatchMessage, int callType)
241 Message reqMsg = reqMcmMsg as Message;
242 if ((reqMsg == null) && (callType != Message.Sync))
244 // Only the synchronous call type is supported for messages that
245 // aren't of type Message.
246 throw new RemotingException(
247 Environment.GetResourceString("Remoting_Proxy_InvalidCallType"));
250 IMessage retMsg = null;
251 Thread currentThread = Thread.CurrentThread;
253 // pick up call context from the thread
254 LogicalCallContext cctx = currentThread.GetMutableExecutionContext().LogicalCallContext;
256 Identity idObj = IdentityObject;
257 ServerIdentity serverID = idObj as ServerIdentity;
258 if ((null != serverID) && idObj.IsFullyDisconnected())
260 throw new ArgumentException(
261 Environment.GetResourceString("Remoting_ServerObjectNotFound", reqMcmMsg.Uri));
264 // Short-circuit calls to Object::GetType and Object::GetHashCode
265 MethodBase mb = reqMcmMsg.MethodBase;
266 if(_getTypeMethod == mb)
268 // Time to load the true type of the remote object....
269 Type t = GetProxiedType();
270 return new ReturnMessage(t, null, 0, cctx, reqMcmMsg);
273 if (_getHashCodeMethod == mb)
275 int hashCode = idObj.GetHashCode();
276 return new ReturnMessage(hashCode, null, 0, cctx, reqMcmMsg);
279 // check for channel sink
280 if (idObj.ChannelSink == null)
282 IMessageSink chnlSink = null;
283 IMessageSink envoySink = null;
284 // If channelSink is null try to Create them again
285 // the objref should be correctly fixed up at this point
286 if(!idObj.ObjectRef.IsObjRefLite())
288 RemotingServices.CreateEnvoyAndChannelSinks(null, idObj.ObjectRef, out chnlSink, out envoySink);
292 RemotingServices.CreateEnvoyAndChannelSinks(idObj.ObjURI, null, out chnlSink, out envoySink);
294 // Set the envoy and channel sinks in a thread safe manner
295 RemotingServices.SetEnvoyAndChannelSinks(idObj, chnlSink, envoySink);
297 // If the channel sink is still null then throw
298 if(idObj.ChannelSink == null)
300 throw new RemotingException(
301 Environment.GetResourceString("Remoting_Proxy_NoChannelSink"));
305 // Set the identity in the message object
306 IInternalMessage iim = (IInternalMessage)reqMcmMsg;
307 iim.IdentityObject = idObj;
309 if (null != serverID)
311 Message.DebugOut("Setting serveridentity on message \n");
312 iim.ServerIdentityObject = serverID;
317 // We need to set the URI only for identities that
318 // are not the server identities. The uri is used to
319 // dispatch methods for objects outside the appdomain.
320 // Inside the appdomain (xcontext case) we dispatch
321 // by getting the server object from the server identity.
322 iim.SetURI(idObj.URI);
325 Message.DebugOut("InternalInvoke. Dispatching based on class type\n");
326 AsyncResult ar = null;
330 Message.DebugOut("RemotingProxy.Invoke Call: SyncProcessMsg\n");
331 BCLDebug.Assert(!useDispatchMessage,"!useDispatchMessage");
332 bool bSkipContextChain = false;
333 Context currentContext = currentThread.GetCurrentContextInternal();
334 IMessageSink nextSink = idObj.EnvoyChain;
336 // if we are in the default context, there can be no
337 // client context chain, so we can skip the intermediate
338 // calls if there are no envoy sinks
340 if (currentContext.IsDefaultContext)
342 if (nextSink is EnvoyTerminatorSink)
344 bSkipContextChain = true;
346 // jump directly to the channel sink
347 nextSink = idObj.ChannelSink;
351 retMsg = CallProcessMessage(nextSink,
353 idObj.ProxySideDynamicSinks,
360 case Message.BeginAsync:
361 case Message.BeginAsync | Message.OneWay:
362 // For async calls we clone the call context from the thread
363 // This is a limited clone (we dont deep copy the user data)
364 cctx = (LogicalCallContext) cctx.Clone();
365 iim.SetCallContext(cctx);
367 ar = new AsyncResult(reqMsg);
369 InternalInvokeAsync(ar, reqMsg, useDispatchMessage, callType);
371 Message.DebugOut("Propagate out params for BeginAsync\n");
372 retMsg = new ReturnMessage(ar, null, 0, null/*cctx*/, reqMsg);
376 // For async calls we clone the call context from the thread
377 // This is a limited clone (we dont deep copy the user data)
378 cctx = (LogicalCallContext) cctx.Clone();
379 iim.SetCallContext(cctx);
380 InternalInvokeAsync(null, reqMsg, useDispatchMessage, callType);
381 retMsg = new ReturnMessage(null, null, 0, null/*cctx*/, reqMcmMsg);
384 case (Message.EndAsync | Message.OneWay):
385 retMsg = new ReturnMessage(null, null, 0, null/*cctx*/, reqMcmMsg);
388 case Message.EndAsync:
389 // For endAsync, we merge back the returned callContext
390 // into the thread's callContext
391 retMsg = RealProxy.EndInvokeHelper(reqMsg, true);
399 // This is called from InternalInvoke above when someone makes an
400 // Async (or a one way) call on a TP
401 internal void InternalInvokeAsync(IMessageSink ar, Message reqMsg,
402 bool useDispatchMessage, int callType)
404 IMessageCtrl cc = null;
405 Identity idObj = IdentityObject;
406 ServerIdentity serverID = idObj as ServerIdentity;
407 MethodCall cpyMsg= new MethodCall(reqMsg);
408 IInternalMessage iim = ((IInternalMessage)cpyMsg);
410 // Set the identity in the message object
411 iim.IdentityObject = idObj;
412 if (null != serverID)
414 Message.DebugOut("Setting SrvID on deser msg\n");
415 iim.ServerIdentityObject = serverID;
418 if (useDispatchMessage)
421 "RemotingProxy.Invoke: Calling AsyncDispatchMessage\n");
423 BCLDebug.Assert(ar != null,"ar != null");
424 BCLDebug.Assert( (callType & Message.BeginAsync) != 0,
425 "BeginAsync flag not set!");
427 Message.DebugOut("Calling AsynDispatchMessage \n");
428 cc = ChannelServices.AsyncDispatchMessage(
430 ((callType & Message.OneWay) != 0)
433 else if (null != idObj.EnvoyChain)
435 Message.DebugOut("RemotingProxy.Invoke: Calling AsyncProcessMsg on the envoy chain\n");
437 cc = idObj.EnvoyChain.AsyncProcessMessage(
439 ((callType & Message.OneWay) != 0)
444 // Channel sink cannot be null since it is the last sink in
445 // the client context
446 // Assert if Invoke is called without a channel sink
447 BCLDebug.Assert(false, "How did we get here?");
449 throw new InvalidOperationException(
450 Environment.GetResourceString("Remoting_Proxy_InvalidState"));
453 if ((callType & Message.BeginAsync) != 0)
456 if ((callType & Message.OneWay) != 0)
458 ar.SyncProcessMessage(null);
463 // New method for activators.
465 // This gets called during remoting intercepted activation when
466 // JIT tries to run a constructor on a TP (which remoting gave it
467 // in place of an actual uninitialized instance of the expected type)
468 private IConstructionReturnMessage InternalActivate(IConstructionCallMessage ctorMsg)
470 // Remember the hashcode of the constructor thread.
471 CtorThread = Thread.CurrentThread.GetHashCode();
473 IConstructionReturnMessage ctorRetMsg = ActivationServices.Activate(this, ctorMsg);
475 // Set the flag to indicate that the object is initialized
476 // Note: this assert is not valid for WKOs
477 //BCLDebug.Assert(!Initialized, "Proxy marked as initialized before activation call completed");
483 // Invoke for case where call is in the same context as the server object
484 // (This special static method is used for AsyncDelegate-s ... this is called
485 // directly from the EE)
486 private static void Invoke(Object NotUsed, ref MessageData msgData)
488 Message m = new Message();
489 m.InitFields(msgData);
491 Object thisPtr = m.GetThisPtr();
493 if ((d = thisPtr as Delegate) != null)
497 RemotingProxy rp = (RemotingProxy)
498 RemotingServices.GetRealProxy(d.Target);
502 rp.InternalInvoke(m, true, m.GetCallType());
506 int callType = m.GetCallType();
510 case Message.BeginAsync:
511 case Message.BeginAsync | Message.OneWay:
512 // pick up call context from the thread
513 m.Properties[Message.CallContextKey] =
514 Thread.CurrentThread.GetMutableExecutionContext().LogicalCallContext.Clone();
515 ar = new AsyncResult(m);
516 AgileAsyncWorkerItem workItem =
517 new AgileAsyncWorkerItem(
519 ((callType & Message.OneWay) != 0) ?
520 null : ar, d.Target);
522 ThreadPool.QueueUserWorkItem(
524 AgileAsyncWorkerItem.ThreadPoolCallBack),
527 if ((callType & Message.OneWay) != 0)
529 ar.SyncProcessMessage(null);
531 m.PropagateOutParameters(null, ar);
533 case (Message.EndAsync | Message.OneWay):
536 case Message.EndAsync:
537 // This will also merge back the call context
538 // onto the thread that called EndAsync
539 RealProxy.EndInvokeHelper(m, false);
544 "Should never be here. Sync delegate code for agile object ended up in remoting");
551 // Static invoke called with incorrect this pointer ...
552 throw new RemotingException(
553 Environment.GetResourceString(
554 "Remoting_Default"));
558 internal ConstructorCallMessage ConstructorMessage
572 // IRemotingTypeInfo interface
575 // Obtain the fully qualified name of the type that the proxy represents
576 public String TypeName
578 [System.Security.SecurityCritical]
581 return GetProxiedType().FullName;
584 [System.Security.SecurityCritical]
587 throw new NotSupportedException();
592 [System.Security.SecurityCritical]
593 public override IntPtr GetCOMIUnknown(bool fIsBeingMarshalled)
595 IntPtr pUnk = IntPtr.Zero;
596 Object otp = GetTransparentProxy();
597 bool fIsXProcess = RemotingServices.IsObjectOutOfProcess(otp);
600 // we are in a different process
601 if (fIsBeingMarshalled)
603 // we need to go to the server to get the real IUnknown
604 pUnk = MarshalByRefObject.GetComIUnknown((MarshalByRefObject)otp);
608 // create an IUnknown here
609 pUnk = MarshalByRefObject.GetComIUnknown((MarshalByRefObject)otp);
614 bool fIsXAppDomain = RemotingServices.IsObjectOutOfAppDomain(otp);
615 // we are in the same proces, ask the object for its IUnknown
618 // do an appdomain switch
619 pUnk = ((MarshalByRefObject)otp).GetComIUnknown(fIsBeingMarshalled);
623 // otherwise go ahead and create a CCW here
624 pUnk = MarshalByRefObject.GetComIUnknown((MarshalByRefObject)otp);
631 [System.Security.SecurityCritical]
632 public override void SetCOMIUnknown(IntPtr i)
634 // for now ignore this
637 // Check whether we can cast the transparent proxy to the given type
638 [System.Security.SecurityCritical]
639 public bool CanCastTo(Type castType, Object o)
641 if (castType == null)
642 throw new ArgumentNullException("castType");
644 RuntimeType rtType = castType as RuntimeType;
646 throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeType"));
648 bool fCastOK = false;
650 // The identity should be non-null
651 BCLDebug.Assert(null != IdentityObject,"null != IdentityObject");
653 Message.DebugOut("CheckCast for identity " + IdentityObject.GetType());
655 if ((rtType == s_typeofObject) ||
656 (rtType == s_typeofMarshalByRefObject))
661 // Get the objref of the proxy
662 ObjRef oRef = IdentityObject.ObjectRef;
664 // If the object ref is non-null then check against the type info
668 Object oTP = GetTransparentProxy();
670 // Check that there is a matching type in the server object
671 // hierarchy represented in the objref
672 Message.DebugOut("Calling CanCastTo for type " + rtType);
673 IRemotingTypeInfo typeInfo = oRef.TypeInfo;
676 fCastOK = typeInfo.CanCastTo(rtType, oTP);
677 if (!fCastOK && typeInfo.GetType()==typeof(TypeInfo) && oRef.IsWellKnown() )
679 fCastOK = CanCastToWK(rtType);
684 if (oRef.IsObjRefLite())
686 // we should do a dynamic cast across the network
687 fCastOK = MarshalByRefObject.CanCastToXmlTypeHelper(rtType, (MarshalByRefObject)o);
691 // This is a well known object which does not have a backing ObjRef
694 fCastOK = CanCastToWK(rtType);
699 // WellKnown proxies we always allow casts to interfaces, and allow
700 // casting down a single branch in the type hierarchy (both are on good
701 // faith. The calls are failed on server side if a bogus cast is done)
702 bool CanCastToWK(Type castType)
704 Message.DebugOut( "CheckCast for well known objects and type " + castType);
705 bool fCastOK = false;
706 // Check whether the type to which we want to cast is
707 // compatible with the current type
710 fCastOK = GetProxiedType().IsAssignableFrom(castType);
714 // NOTE: we are coming here also for x-context proxies
715 // when unmanaged code cannot determine if the cast is not
717 if (!(IdentityObject is ServerIdentity))
720 IdentityObject.URI != null,
722 // Always allow interface casts to succeed. If the
723 // interface is not supported by the well known object
724 // then we will throw an exception when the interface
735 internal class AgileAsyncWorkerItem
737 private IMethodCallMessage _message;
738 private AsyncResult _ar;
739 private Object _target;
741 [System.Security.SecurityCritical] // auto-generated
742 public AgileAsyncWorkerItem(IMethodCallMessage message, AsyncResult ar, Object target)
744 _message = new MethodCall(message);
749 [System.Security.SecurityCritical] // auto-generated
750 public static void ThreadPoolCallBack(Object o)
752 ((AgileAsyncWorkerItem) o).DoAsyncCall();
756 [System.Security.SecurityCritical] // auto-generated
757 public void DoAsyncCall()
759 (new StackBuilderSink(_target)).AsyncProcessMessage(_message, _ar);