问题
I need to read standart output from application (Core.exe) that runs in the same process but in different appDomain. It's quite easy to redirect output when one deals with process but the appDomains concept is new to me.
So.. I start application in isolataed appDomain like that
new HostedApp("core", "Core.exe").Run();
class HostedApp
{
internal string DomainName;
internal string AssemblyName;
internal AppDomain Ad;
internal Thread AppThrd;
public HostedApp(string a_domain, string a_assemblyName)
{
DomainName = a_domain;
AssemblyName = a_assemblyName;
Ad = AppDomain.CreateDomain(a_domain);
}
public void Run()
{
AppThrd = new Thread(RunApp);
AppThrd.Start();
}
private void RunApp()
{
try
{
Ad.ExecuteAssembly(AssemblyName);
}
catch(Exception _ex)
{
MessageBox.Show("Unhandled exception\n" + _ex);
}
}
}
I've tride to redirect Console.Out of the current process assuming that if the apps share the same process there will be single standart output.
But it only shows the default appDomain standart output.
So, to sum it up I need to gain access to another appDomain application standart output. Or may be there is a way to call a method located in default appDomain from "core" appDomain?
回答1:
Maybe this can help you. I use these classes to be able to listen to remote AppDomains Trace.(Write/WriteLine) calls.
The first class is a class that allows me to redirect the TraceListen Write/WriteLine methods to a custom delegate.
public delegate void TraceWriterHandler(string message);
internal class SynchronizedTraceListener : TraceListener
{
private TraceWriterHandler messageHandler;
public SynchronizedTraceListener(TraceWriterHandler writeHandler)
{
messageHandler = writeHandler;
}
public override void Write(string message)
{
messageHandler(message);
}
public override void WriteLine(string message)
{
messageHandler(message + System.Environment.NewLine);
}
}
Then the core of my remote AppDomain trace listener class. This is the tricky part. I'll try not to mess the explanation. This is tricky for me to, but here it goes.
- The (local) CrossDomainTracer object creates a (far) CrossDomainTracer object on the far AppDomain.
- The (local) CrossDomainTracer object calls the (far) CrossDomainTracer object .StartListening and sends (local) it self as a reference.
- The (far)CrossDomainTracer object starts listening to any Trace.Write/WriteLine calls in (far) his domain.
- When a (far)Trace.Write/WriteLine call is made it calls the .RemoteWrite method from the (local) remote AppDomain.
- The (local) .RemoteWrite makes a call to it's own AppDomain scope Trace.Write so that the (local) listener can properly display the message.
Notes:
- The AssemblyResolve ensures errors while trying to reference the assembly that contains this code.
- This code must exist in both processes and share the same namespace. I use it in a library and add the assembly reference to both apps.
- Also note Serializable attribute and the MarshalByRefObject inheritance. This is required by the framework to proper Marshalling of the objects between AppDomains.
[Serializable]
public sealed class CrossDomainTracer : MarshalByRefObject
{
private CrossDomainTracer remoteTracer;
private SynchronizedTraceListener remoteListener;
public CrossDomainTracer()
{
}
public CrossDomainTracer(AppDomain farDomain)
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
this.remoteTracer = farDomain.CreateInstanceFrom(Assembly.GetExecutingAssembly().Location, typeof(CrossDomainTracer).FullName).Unwrap() as CrossDomainTracer;
AppDomain.CurrentDomain.AssemblyResolve -= new ResolveEventHandler(CurrentDomain_AssemblyResolve);
if (remoteTracer != null)
{
remoteTracer.StartListening(this);
}
}
public void StartListening(CrossDomainTracer farTracer)
{
this.remoteTracer = farTracer;
this.remoteListener = new SynchronizedTraceListener(new TraceWriterHandler(Write));
Trace.Listeners.Add(this.remoteListener);
}
public void Write(string message)
{
this.remoteTracer.RemoteWrite("AppDomain(" + AppDomain.CurrentDomain.Id.ToString() +") " + message);
}
public void RemoteWrite(string message)
{
Trace.Write(message);
}
Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
try
{
Assembly assembly = System.Reflection.Assembly.Load(args.Name);
if (assembly != null)
{
return assembly;
}
}
catch { }
// Try to load by assembly fullname (path to file)
string[] Parts = args.Name.Split(',');
string File = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\" + Parts[0].Trim() + ".dll";
return System.Reflection.Assembly.LoadFrom(File);
}
}
Finnaly you can neatly pack all this in a static class.
public static class CrossDomainTrace
{
public static void StartListening(AppDomain remoteDomain)
{
new CrossDomainTracer(remoteDomain);
}
}
By doing this in the app that will be registering the far Trace massages.
CrossDomainTrace.StartListening(theFarAppDomain);
The only thing left is to add a TraceListner to the Trace.Listeners collection on this side to do what ever you want with the messages.
Hope it helps.
来源:https://stackoverflow.com/questions/15548364/cross-appdomain-access-to-console-out