2 // System.Runtime.Remoting.RemotingServices.cs
5 // Dietmar Maurer (dietmar@ximian.com)
6 // Lluis Sanchez Gual (lluis@ideary.com)
9 // (C) 2001 Ximian, Inc. http://www.ximian.com
13 using System.Diagnostics;
15 using System.Reflection;
16 using System.Threading;
17 using System.Collections;
18 using System.Runtime.Remoting.Messaging;
19 using System.Runtime.Remoting.Proxies;
20 using System.Runtime.Remoting.Channels;
21 using System.Runtime.Remoting.Contexts;
22 using System.Runtime.Remoting.Activation;
23 using System.Runtime.Remoting.Lifetime;
24 using System.Runtime.CompilerServices;
25 using System.Runtime.Serialization;
28 namespace System.Runtime.Remoting
30 public sealed class RemotingServices
32 // Holds the identities of the objects, using uri as index
33 static Hashtable uri_hash = new Hashtable ();
35 internal static string app_id;
36 static int next_id = 1;
38 static RemotingServices ()
40 RegisterInternalChannels ();
41 app_id = Guid.NewGuid().ToString().Replace('-', '_') + "/";
42 CreateWellKnownServerIdentity (typeof(RemoteActivator), "RemoteActivationService.rem", WellKnownObjectMode.Singleton);
45 private RemotingServices () {}
47 [MethodImplAttribute(MethodImplOptions.InternalCall)]
48 internal extern static object InternalExecute (MonoMethod method, Object obj,
49 Object[] parameters, out object [] out_args);
51 [MethodImplAttribute(MethodImplOptions.InternalCall)]
52 public extern static bool IsTransparentProxy (object proxy);
54 internal static IMethodReturnMessage InternalExecuteMessage (
55 MarshalByRefObject target, IMethodCallMessage reqMsg)
59 MonoMethod method = (MonoMethod) target.GetType().GetMethod(reqMsg.MethodName, BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Instance, null, (Type[]) reqMsg.MethodSignature, null);
60 object oldContext = CallContext.SetCurrentCallContext (reqMsg.LogicalCallContext);
65 object rval = InternalExecute (method, target, reqMsg.Args, out out_args);
67 // Collect parameters with Out flag from the request message
68 // FIXME: This can be done in the unmanaged side and will be
71 ParameterInfo[] parameters = method.GetParameters();
72 object[] returnArgs = new object [parameters.Length];
76 foreach (ParameterInfo par in parameters)
78 if (par.IsOut && !par.ParameterType.IsByRef)
79 returnArgs [n++] = reqMsg.GetArg (par.Position);
80 else if (par.ParameterType.IsByRef)
81 returnArgs [n++] = out_args [noa++];
83 returnArgs [n++] = null;
86 result = new ReturnMessage (rval, returnArgs, n, CallContext.CreateLogicalCallContext(), reqMsg);
90 result = new ReturnMessage (e, reqMsg);
93 CallContext.RestoreCallContext (oldContext);
97 public static IMethodReturnMessage ExecuteMessage (
98 MarshalByRefObject target, IMethodCallMessage reqMsg)
100 if (IsTransparentProxy(target))
102 // Message must go through all chain of sinks
103 RealProxy rp = GetRealProxy (target);
104 return (IMethodReturnMessage) rp.Invoke (reqMsg);
107 return InternalExecuteMessage (target, reqMsg);
110 public static object Connect (Type classToProxy, string url)
112 ObjRef objRef = new ObjRef (classToProxy, url, null);
113 return GetRemoteObject (objRef, classToProxy);
116 public static object Connect (Type classToProxy, string url, object data)
118 ObjRef objRef = new ObjRef (classToProxy, url, data);
119 return GetRemoteObject (objRef, classToProxy);
122 public static bool Disconnect (MarshalByRefObject obj)
124 if (obj == null) throw new ArgumentNullException ("obj");
126 ServerIdentity identity;
128 if (IsTransparentProxy (obj))
130 // CBOs are always accessed through a proxy, even in the server, so
131 // for server CBOs it is ok to disconnect a proxy
133 RealProxy proxy = GetRealProxy(obj);
134 if (proxy.GetProxiedType().IsContextful && (proxy.ObjectIdentity is ServerIdentity))
135 identity = proxy.ObjectIdentity as ServerIdentity;
137 throw new ArgumentException ("The obj parameter is a proxy.");
140 identity = obj.ObjectIdentity;
142 if (identity == null || !identity.IsConnected)
146 LifetimeServices.StopTrackingLifetime (identity);
147 DisposeIdentity (identity);
152 public static Type GetServerTypeForUri (string uri)
154 ServerIdentity ident = GetIdentityForUri (uri) as ServerIdentity;
155 if (ident == null) return null;
156 return ident.ObjectType;
159 public static string GetObjectUri (MarshalByRefObject obj)
161 Identity ident = GetObjectIdentity(obj);
162 if (ident is ClientIdentity) return ((ClientIdentity)ident).TargetUri;
163 else if (ident != null) return ident.ObjectUri;
167 public static object Unmarshal (ObjRef objref)
169 return Unmarshal(objref, true);
172 public static object Unmarshal (ObjRef objref, bool fRefine)
174 Type classToProxy = fRefine ? objref.ServerType : typeof (MarshalByRefObject);
175 if (classToProxy == null) classToProxy = typeof (MarshalByRefObject);
177 if (objref.IsReferenceToWellKnow)
178 return GetRemoteObject(objref, classToProxy);
181 if (classToProxy.IsContextful)
183 // Look for a ProxyAttribute
184 ProxyAttribute att = (ProxyAttribute) Attribute.GetCustomAttribute (classToProxy, typeof(ProxyAttribute),true);
186 return att.CreateProxy (objref, classToProxy, null, null).GetTransparentProxy();
188 return GetProxyForRemoteObject (objref, classToProxy);
192 public static ObjRef Marshal (MarshalByRefObject obj)
194 return Marshal (obj, null, null);
197 public static ObjRef Marshal (MarshalByRefObject obj, string uri)
199 return Marshal (obj, uri, null);
202 public static ObjRef Marshal (MarshalByRefObject obj, string uri, Type requested_type)
204 if (IsTransparentProxy (obj))
206 RealProxy proxy = RemotingServices.GetRealProxy(obj);
207 Identity identity = proxy.ObjectIdentity;
209 if (identity != null)
211 if (proxy.GetProxiedType().IsContextful && !identity.IsConnected)
213 // Unregistered local contextbound object. Register now.
214 ClientActivatedIdentity cboundIdentity = (ClientActivatedIdentity)identity;
215 if (uri == null) uri = NewUri();
216 cboundIdentity.ObjectUri = uri;
217 RegisterServerIdentity (cboundIdentity);
218 cboundIdentity.StartTrackingLifetime ((ILease)obj.InitializeLifetimeService());
219 return cboundIdentity.CreateObjRef(requested_type);
221 else if (uri != null)
222 throw new RemotingException ("It is not possible marshal a proxy of a remote object.");
224 return proxy.ObjectIdentity.CreateObjRef(requested_type);
228 if (requested_type == null) requested_type = obj.GetType();
232 if (obj.ObjectIdentity == null)
235 CreateClientActivatedServerIdentity (obj, requested_type, uri);
240 ClientActivatedIdentity identity = GetIdentityForUri ("/" + uri) as ClientActivatedIdentity;
241 if (identity == null || obj != identity.GetServerObject())
242 CreateClientActivatedServerIdentity (obj, requested_type, uri);
245 if (IsTransparentProxy (obj))
246 return RemotingServices.GetRealProxy(obj).ObjectIdentity.CreateObjRef (requested_type);
248 return obj.CreateObjRef(requested_type);
251 static string NewUri ()
253 int n = Interlocked.Increment (ref next_id);
254 return app_id + Environment.TickCount + "_" + n + ".rem";
257 public static RealProxy GetRealProxy (object proxy)
259 if (!IsTransparentProxy(proxy)) throw new RemotingException("Cannot get the real proxy from an object that is not a transparent proxy.");
260 return (RealProxy)((TransparentProxy)proxy)._rp;
263 public static MethodBase GetMethodBaseFromMethodMessage(IMethodMessage msg)
265 Type type = Type.GetType (msg.TypeName);
267 throw new RemotingException ("Type '" + msg.TypeName + "' not found.");
269 BindingFlags bflags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
272 if (msg.MethodSignature == null)
273 method = type.GetMethod (msg.MethodName, bflags);
275 method = type.GetMethod (msg.MethodName, bflags, null, (Type[]) msg.MethodSignature, null);
280 if (msg.MethodSignature == null)
281 return type.GetConstructor (bflags, null, Type.EmptyTypes, null);
283 return type.GetConstructor (bflags, null, (Type[]) msg.MethodSignature, null);
286 public static void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
288 if (obj == null) throw new ArgumentNullException ("obj");
290 ObjRef oref = Marshal ((MarshalByRefObject)obj);
291 oref.GetObjectData (info, context);
294 public static ObjRef GetObjRefForProxy(MarshalByRefObject obj)
296 Identity ident = GetObjectIdentity(obj);
297 if (ident == null) return null;
298 else return ident.CreateObjRef(null);
301 public static object GetLifetimeService (MarshalByRefObject obj)
303 if (obj == null) return null;
304 return obj.GetLifetimeService ();
307 public static IMessageSink GetEnvoyChainForProxy (MarshalByRefObject obj)
309 if (IsTransparentProxy(obj))
310 return ((ClientIdentity)GetRealProxy (obj).ObjectIdentity).EnvoySink;
312 throw new ArgumentException ("obj must be a proxy.","obj");
316 [Conditional ("REMOTING_PERF")]
317 public static void LogRemotingStage (int stage)
319 throw new NotImplementedException ();
322 public static string GetSessionIdForMethodMessage(IMethodMessage msg)
324 // It seems that this it what MS returns.
328 public static bool IsMethodOverloaded(IMethodMessage msg)
330 // TODO: use internal call for better performance
331 Type type = msg.MethodBase.DeclaringType;
332 MemberInfo[] members = type.GetMember (msg.MethodName, MemberTypes.Method, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
333 return members.Length > 1;
336 public static bool IsObjectOutOfAppDomain(object tp)
338 // TODO: use internal call for better performance
339 Identity ident = GetObjectIdentity((MarshalByRefObject)tp);
340 if (ident != null) return !ident.IsFromThisAppDomain;
344 public static bool IsObjectOutOfContext(object tp)
346 // TODO: use internal call for better performance
347 ServerIdentity ident = GetObjectIdentity((MarshalByRefObject)tp) as ServerIdentity;
348 if (ident != null) return ident.Context != System.Threading.Thread.CurrentContext;
352 public static bool IsOneWay(MethodBase method)
354 // TODO: use internal call for better performance
355 object[] atts = method.GetCustomAttributes (typeof (OneWayAttribute), false);
356 return atts.Length > 0;
359 internal static bool IsAsyncMessage(IMessage msg)
361 if (! (msg is MonoMethodMessage)) return false;
362 else if (((MonoMethodMessage)msg).IsAsync) return true;
363 else if (IsOneWay (((MonoMethodMessage)msg).MethodBase)) return true;
367 public static void SetObjectUriForMarshal(MarshalByRefObject obj, string uri)
369 if (IsTransparentProxy (obj)) throw new RemotingException ("SetObjectUriForMarshal method should only be called for MarshalByRefObjects that exist in the current AppDomain.");
373 #region Internal Methods
375 internal static object CreateClientProxy (ActivatedClientTypeEntry entry, object[] activationAttributes)
377 if (entry.ContextAttributes != null || activationAttributes != null)
379 ArrayList props = new ArrayList ();
380 if (entry.ContextAttributes != null) props.AddRange (entry.ContextAttributes);
381 if (activationAttributes != null) props.AddRange (activationAttributes);
382 return CreateClientProxy (entry.ObjectType, entry.ApplicationUrl, props.ToArray ());
385 return CreateClientProxy (entry.ObjectType, entry.ApplicationUrl, null);
388 internal static object CreateClientProxy (Type objectType, string url, object[] activationAttributes)
390 string activationUrl = url + "/RemoteActivationService.rem";
393 IMessageSink sink = GetClientChannelSinkChain (activationUrl, null, out objectUri);
395 RemotingProxy proxy = new RemotingProxy (objectType, activationUrl, activationAttributes);
396 return proxy.GetTransparentProxy();
399 internal static object CreateClientProxy (WellKnownClientTypeEntry entry)
401 return Connect (entry.ObjectType, entry.ObjectUrl, null);
404 internal static object CreateClientProxyForContextBound (Type type, object[] activationAttributes)
406 if (type.IsContextful)
408 // Look for a ProxyAttribute
409 ProxyAttribute att = (ProxyAttribute) Attribute.GetCustomAttribute (type, typeof(ProxyAttribute), true);
411 return att.CreateInstance (type);
413 RemotingProxy proxy = new RemotingProxy (type, ChannelServices.CrossContextUrl, activationAttributes);
414 return proxy.GetTransparentProxy();
417 internal static Identity GetIdentityForUri (string uri)
421 return (Identity)uri_hash [GetNormalizedUri(uri)];
425 internal static Identity GetObjectIdentity (MarshalByRefObject obj)
427 if (IsTransparentProxy(obj))
428 return GetRealProxy (obj).ObjectIdentity;
430 return obj.ObjectIdentity;
433 internal static ClientIdentity GetOrCreateClientIdentity(ObjRef objRef, Type proxyType, out object clientProxy)
435 // This method looks for an identity for the given url.
436 // If an identity is not found, it creates the identity and
437 // assigns it a proxy to the remote object.
439 // Creates the client sink chain for the given url or channelData.
440 // It will also get the object uri from the url.
442 object channelData = objRef.ChannelInfo != null ? objRef.ChannelInfo.ChannelData : null;
443 string url = (channelData == null) ? objRef.URI : null;
446 IMessageSink sink = GetClientChannelSinkChain (url, channelData, out objectUri);
448 if (objectUri == null) objectUri = objRef.URI;
453 string uri = GetNormalizedUri (objRef.URI);
455 ClientIdentity identity = uri_hash [uri] as ClientIdentity;
456 if (identity != null)
458 // Object already registered
459 clientProxy = identity.ClientProxy;
460 if (clientProxy != null) return identity;
462 // The proxy has just been GCed, so its identity cannot
463 // be reused. Just dispose it.
464 DisposeIdentity (identity);
467 // Creates an identity and a proxy for the remote object
469 identity = new ClientIdentity (objectUri, objRef);
470 identity.ChannelSink = sink;
472 // Registers the identity
473 uri_hash [uri] = identity;
475 if (proxyType != null)
477 RemotingProxy proxy = new RemotingProxy (proxyType, identity);
478 clientProxy = proxy.GetTransparentProxy();
479 identity.ClientProxy = (MarshalByRefObject) clientProxy;
486 static IMessageSink GetClientChannelSinkChain(string url, object channelData, out string objectUri)
488 IMessageSink sink = ChannelServices.CreateClientChannelSinkChain (url, channelData, out objectUri);
493 string msg = String.Format ("Cannot create channel sink to connect to URL {0}. An appropriate channel has probably not been registered.", url);
494 throw new RemotingException (msg);
498 string msg = String.Format ("Cannot create channel sink to connect to the remote object. An appropriate channel has probably not been registered.", url);
499 throw new RemotingException (msg);
505 internal static ClientActivatedIdentity CreateContextBoundObjectIdentity(Type objectType)
507 ClientActivatedIdentity identity = new ClientActivatedIdentity (null, objectType);
508 identity.ChannelSink = ChannelServices.CrossContextChannel;
512 internal static ClientActivatedIdentity CreateClientActivatedServerIdentity(MarshalByRefObject realObject, Type objectType, string objectUri)
514 ClientActivatedIdentity identity = new ClientActivatedIdentity (objectUri, objectType);
515 identity.AttachServerObject (realObject, Context.DefaultContext);
516 RegisterServerIdentity (identity);
517 identity.StartTrackingLifetime ((ILease)realObject.InitializeLifetimeService ());
521 internal static ServerIdentity CreateWellKnownServerIdentity(Type objectType, string objectUri, WellKnownObjectMode mode)
523 ServerIdentity identity;
525 if (mode == WellKnownObjectMode.SingleCall)
526 identity = new SingleCallIdentity(objectUri, Context.DefaultContext, objectType);
528 identity = new SingletonIdentity(objectUri, Context.DefaultContext, objectType);
530 RegisterServerIdentity (identity);
534 private static void RegisterServerIdentity(ServerIdentity identity)
538 if (uri_hash.ContainsKey (identity.ObjectUri))
539 throw new RemotingException ("Uri already in use: " + identity.ObjectUri + ".");
541 uri_hash[identity.ObjectUri] = identity;
545 internal static object GetProxyForRemoteObject (ObjRef objref, Type classToProxy)
547 ClientActivatedIdentity identity = GetIdentityForUri (objref.URI) as ClientActivatedIdentity;
548 if (identity != null) return identity.GetServerObject ();
549 else return GetRemoteObject (objref, classToProxy);
552 internal static object GetRemoteObject(ObjRef objRef, Type proxyType)
555 GetOrCreateClientIdentity (objRef, proxyType, out proxy);
559 internal static object GetDomainProxy(AppDomain domain)
563 Context currentContext = Thread.CurrentContext;
567 data = (byte[])AppDomain.InvokeInDomain (domain, typeof (AppDomain).GetMethod ("GetMarshalledDomainObjRef", BindingFlags.Instance|BindingFlags.NonPublic), domain, null);
571 AppDomain.InternalSetContext (currentContext);
574 MemoryStream stream = new MemoryStream (data);
575 ObjRef appref = (ObjRef) CADSerializer.DeserializeObject (stream);
576 return (AppDomain) RemotingServices.Unmarshal(appref);
579 private static void RegisterInternalChannels()
581 CrossAppDomainChannel.RegisterCrossAppDomainChannel();
584 internal static void DisposeIdentity (Identity ident)
588 if (!ident.Disposed) {
589 ClientIdentity clientId = ident as ClientIdentity;
590 if (clientId != null)
591 uri_hash.Remove (GetNormalizedUri (clientId.TargetUri));
593 uri_hash.Remove (ident.ObjectUri);
595 ident.Disposed = true;
600 internal static Identity GetMessageTargetIdentity (IMessage msg)
602 // Returns the identity where the message is sent
604 if (msg is IInternalMessage)
605 return ((IInternalMessage)msg).TargetIdentity;
609 string uri = GetNormalizedUri (((IMethodMessage)msg).Uri);
610 return uri_hash [uri] as ServerIdentity;
614 internal static void SetMessageTargetIdentity (IMessage msg, Identity ident)
616 if (msg is IInternalMessage)
617 ((IInternalMessage)msg).TargetIdentity = ident;
620 internal static bool UpdateOutArgObject (ParameterInfo pi, object local, object remote)
622 if (local is StringBuilder)
624 StringBuilder sb = local as StringBuilder;
625 sb.Remove (0, sb.Length);
626 sb.Append (remote.ToString());
629 else if (pi.ParameterType.IsArray && ((Array)local).Rank == 1)
631 Array alocal = (Array) local;
632 if (alocal.Rank == 1)
634 Array.Copy ((Array) remote, alocal, alocal.Length);
645 static string GetNormalizedUri (string uri)
647 if (uri.StartsWith ("/")) return uri.Substring (1);