6992685b86
Former-commit-id: 0a113cb3a6feb7873f632839b1307cc6033cd595
374 lines
10 KiB
C#
374 lines
10 KiB
C#
//
|
|
// System.Runtime.Remoting.Channels.CrossAppDomainChannel.cs
|
|
//
|
|
// Author: Patrik Torstensson (totte_mono@yahoo.com)
|
|
// Lluis Sanchez Gual (lluis@ximian.com)
|
|
//
|
|
// 2003 (C) Copyright, Ximian, Inc.
|
|
//
|
|
|
|
//
|
|
// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining
|
|
// a copy of this software and associated documentation files (the
|
|
// "Software"), to deal in the Software without restriction, including
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
|
// permit persons to whom the Software is furnished to do so, subject to
|
|
// the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be
|
|
// included in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
//
|
|
|
|
using System.Collections;
|
|
using System.IO;
|
|
using System.Threading;
|
|
using System.Runtime.Remoting;
|
|
using System.Runtime.Remoting.Messaging;
|
|
using System.Runtime.Remoting.Channels;
|
|
using System.Runtime.Remoting.Contexts;
|
|
using System.Runtime.Serialization;
|
|
using System.Runtime.Serialization.Formatters.Binary;
|
|
using System.Reflection;
|
|
|
|
namespace System.Runtime.Remoting.Channels
|
|
{
|
|
|
|
// Holds the cross appdomain channel data (used to get/create the correct sink)
|
|
[Serializable]
|
|
internal class CrossAppDomainData
|
|
{
|
|
// TODO: Add context support
|
|
// Required for .NET compatibility
|
|
#pragma warning disable 0414
|
|
private object _ContextID;
|
|
#pragma warning restore
|
|
private int _DomainID;
|
|
private string _processGuid;
|
|
|
|
internal CrossAppDomainData(int domainId)
|
|
{
|
|
_ContextID = (int) 0;
|
|
_DomainID = domainId;
|
|
_processGuid = RemotingConfiguration.ProcessId;
|
|
}
|
|
|
|
internal int DomainID
|
|
{
|
|
get { return _DomainID; }
|
|
}
|
|
|
|
internal string ProcessID
|
|
{
|
|
get { return _processGuid; }
|
|
}
|
|
}
|
|
|
|
// Responsible for marshalling objects between appdomains
|
|
[Serializable]
|
|
internal class CrossAppDomainChannel : IChannel, IChannelSender, IChannelReceiver
|
|
{
|
|
private const String _strName = "MONOCAD";
|
|
|
|
private static Object s_lock = new Object();
|
|
|
|
internal static void RegisterCrossAppDomainChannel()
|
|
{
|
|
lock (s_lock)
|
|
{
|
|
// todo: make singleton
|
|
CrossAppDomainChannel monocad = new CrossAppDomainChannel();
|
|
ChannelServices.RegisterChannel ((IChannel) monocad);
|
|
}
|
|
}
|
|
|
|
// IChannel implementation
|
|
public virtual String ChannelName
|
|
{
|
|
get { return _strName; }
|
|
}
|
|
|
|
public virtual int ChannelPriority
|
|
{
|
|
get { return 100; }
|
|
}
|
|
|
|
public String Parse(String url, out String objectURI)
|
|
{
|
|
objectURI = url;
|
|
return null;
|
|
}
|
|
|
|
// IChannelReceiver
|
|
public virtual Object ChannelData
|
|
{
|
|
get { return new CrossAppDomainData(Thread.GetDomainID()); }
|
|
}
|
|
|
|
public virtual String[] GetUrlsForUri(String objectURI)
|
|
{
|
|
throw new NotSupportedException("CrossAppdomain channel dont support UrlsForUri");
|
|
}
|
|
|
|
// Dummies
|
|
public virtual void StartListening(Object data) {}
|
|
public virtual void StopListening(Object data) {}
|
|
|
|
// IChannelSender
|
|
public virtual IMessageSink CreateMessageSink(String url, Object data, out String uri)
|
|
{
|
|
uri = null;
|
|
|
|
if (data != null)
|
|
{
|
|
// Get the data and then get the sink
|
|
CrossAppDomainData cadData = data as CrossAppDomainData;
|
|
if (cadData != null && cadData.ProcessID == RemotingConfiguration.ProcessId)
|
|
// GetSink creates a new sink if we don't have any (use contexts here later)
|
|
return CrossAppDomainSink.GetSink(cadData.DomainID);
|
|
}
|
|
if (url != null && url.StartsWith(_strName))
|
|
throw new NotSupportedException("Can't create a named channel via crossappdomain");
|
|
|
|
return null;
|
|
}
|
|
}
|
|
|
|
[MonoTODO("Handle domain unloading?")]
|
|
internal class CrossAppDomainSink : IMessageSink
|
|
{
|
|
private static Hashtable s_sinks = new Hashtable();
|
|
|
|
private static MethodInfo processMessageMethod =
|
|
typeof (CrossAppDomainSink).GetMethod ("ProcessMessageInDomain", BindingFlags.NonPublic|BindingFlags.Static);
|
|
|
|
|
|
private int _domainID;
|
|
|
|
internal CrossAppDomainSink(int domainID)
|
|
{
|
|
_domainID = domainID;
|
|
}
|
|
|
|
internal static CrossAppDomainSink GetSink(int domainID)
|
|
{
|
|
// Check if we have a sink for the current domainID
|
|
// note, locking is not to bad here, very few class to GetSink
|
|
lock (s_sinks.SyncRoot)
|
|
{
|
|
if (s_sinks.ContainsKey(domainID))
|
|
return (CrossAppDomainSink) s_sinks[domainID];
|
|
else
|
|
{
|
|
CrossAppDomainSink sink = new CrossAppDomainSink(domainID);
|
|
s_sinks[domainID] = sink;
|
|
|
|
return sink;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal int TargetDomainId {
|
|
get { return _domainID; }
|
|
}
|
|
|
|
private struct ProcessMessageRes {
|
|
public byte[] arrResponse;
|
|
public CADMethodReturnMessage cadMrm;
|
|
}
|
|
|
|
#pragma warning disable 169
|
|
private static ProcessMessageRes ProcessMessageInDomain (
|
|
byte[] arrRequest,
|
|
CADMethodCallMessage cadMsg)
|
|
{
|
|
ProcessMessageRes res = new ProcessMessageRes ();
|
|
|
|
try
|
|
{
|
|
AppDomain.CurrentDomain.ProcessMessageInDomain (arrRequest, cadMsg, out res.arrResponse, out res.cadMrm);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
IMessage errorMsg = new MethodResponse (e, new ErrorMessage());
|
|
res.arrResponse = CADSerializer.SerializeMessage (errorMsg).GetBuffer();
|
|
}
|
|
return res;
|
|
}
|
|
#pragma warning restore 169
|
|
|
|
public virtual IMessage SyncProcessMessage(IMessage msgRequest)
|
|
{
|
|
IMessage retMessage = null;
|
|
|
|
try
|
|
{
|
|
// Time to transit into the "our" domain
|
|
byte [] arrResponse = null;
|
|
byte [] arrRequest = null;
|
|
|
|
CADMethodReturnMessage cadMrm = null;
|
|
CADMethodCallMessage cadMsg;
|
|
|
|
cadMsg = CADMethodCallMessage.Create (msgRequest);
|
|
if (null == cadMsg) {
|
|
// Serialize the request message
|
|
MemoryStream reqMsgStream = CADSerializer.SerializeMessage(msgRequest);
|
|
arrRequest = reqMsgStream.GetBuffer();
|
|
}
|
|
|
|
Context currentContext = Thread.CurrentContext;
|
|
|
|
try {
|
|
// InternalInvoke can't handle out arguments, this is why
|
|
// we return the results in a structure
|
|
ProcessMessageRes res = (ProcessMessageRes)AppDomain.InvokeInDomainByID (_domainID, processMessageMethod, null, new object [] { arrRequest, cadMsg });
|
|
arrResponse = res.arrResponse;
|
|
cadMrm = res.cadMrm;
|
|
} finally {
|
|
AppDomain.InternalSetContext (currentContext);
|
|
}
|
|
|
|
|
|
if (null != arrResponse) {
|
|
// Time to deserialize the message
|
|
MemoryStream respMsgStream = new MemoryStream(arrResponse);
|
|
|
|
// Deserialize the response message
|
|
retMessage = CADSerializer.DeserializeMessage(respMsgStream, msgRequest as IMethodCallMessage);
|
|
} else
|
|
retMessage = new MethodResponse (msgRequest as IMethodCallMessage, cadMrm);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
try
|
|
{
|
|
retMessage = new ReturnMessage (e, msgRequest as IMethodCallMessage);
|
|
}
|
|
catch (Exception)
|
|
{
|
|
// this is just to be sure
|
|
}
|
|
}
|
|
|
|
return retMessage;
|
|
}
|
|
|
|
public virtual IMessageCtrl AsyncProcessMessage (IMessage reqMsg, IMessageSink replySink)
|
|
{
|
|
AsyncRequest req = new AsyncRequest (reqMsg, replySink);
|
|
ThreadPool.QueueUserWorkItem (new WaitCallback ((data) => {
|
|
try {
|
|
SendAsyncMessage (data);
|
|
} catch {}
|
|
}
|
|
), req);
|
|
return null;
|
|
}
|
|
|
|
public void SendAsyncMessage (object data)
|
|
{
|
|
AsyncRequest req = (AsyncRequest)data;
|
|
IMessage response = SyncProcessMessage (req.MsgRequest);
|
|
req.ReplySink.SyncProcessMessage (response);
|
|
}
|
|
|
|
public IMessageSink NextSink { get { return null; } }
|
|
}
|
|
|
|
internal class CADSerializer
|
|
{
|
|
internal static IMessage DeserializeMessage(MemoryStream mem, IMethodCallMessage msg)
|
|
{
|
|
#if FEATURE_REMOTING
|
|
BinaryFormatter serializer = new BinaryFormatter();
|
|
|
|
serializer.SurrogateSelector = null;
|
|
mem.Position = 0;
|
|
|
|
if (msg == null)
|
|
return (IMessage) serializer.Deserialize(mem, null);
|
|
else
|
|
return (IMessage) serializer.DeserializeMethodResponse(mem, null, msg);
|
|
#else
|
|
throw new NotSupportedException ();
|
|
#endif
|
|
}
|
|
|
|
internal static MemoryStream SerializeMessage(IMessage msg)
|
|
{
|
|
MemoryStream mem = new MemoryStream ();
|
|
BinaryFormatter serializer = new BinaryFormatter ();
|
|
|
|
serializer.SurrogateSelector = new RemotingSurrogateSelector ();
|
|
serializer.Serialize (mem, msg);
|
|
|
|
mem.Position = 0;
|
|
|
|
return mem;
|
|
}
|
|
|
|
// This wrapper deserializes the objects on
|
|
// it's input byte array. It's safe for concurrent use
|
|
// while Deserialize will modify the cursor of the MemoryStream
|
|
//
|
|
// It is also the preferred way to deserialize CADMessage
|
|
// objects because their payload must be stored as byte arrays to avoid
|
|
// cross-domain references to MemoryStream objects
|
|
internal static object DeserializeObjectSafe(byte[] mem)
|
|
{
|
|
byte [] outstream = new byte [mem.Length];
|
|
Array.Copy (mem, outstream, mem.Length);
|
|
MemoryStream objStream = new MemoryStream (outstream);
|
|
var returnVal = DeserializeObject (objStream);
|
|
return returnVal;
|
|
}
|
|
|
|
internal static MemoryStream SerializeObject(object obj)
|
|
{
|
|
MemoryStream mem = new MemoryStream ();
|
|
BinaryFormatter serializer = new BinaryFormatter ();
|
|
|
|
serializer.SurrogateSelector = new RemotingSurrogateSelector ();
|
|
serializer.Serialize (mem, obj);
|
|
|
|
mem.Position = 0;
|
|
|
|
return mem;
|
|
}
|
|
|
|
internal static object DeserializeObject(MemoryStream mem)
|
|
{
|
|
BinaryFormatter serializer = new BinaryFormatter();
|
|
|
|
serializer.SurrogateSelector = null;
|
|
mem.Position = 0;
|
|
|
|
return serializer.Deserialize (mem);
|
|
}
|
|
}
|
|
|
|
internal class AsyncRequest
|
|
{
|
|
internal IMessageSink ReplySink;
|
|
internal IMessage MsgRequest;
|
|
|
|
public AsyncRequest (IMessage msgRequest, IMessageSink replySink)
|
|
{
|
|
ReplySink = replySink;
|
|
MsgRequest = msgRequest;
|
|
}
|
|
}
|
|
|
|
}
|