diff --git a/ServerHost/AppDomainSetupUtils.cs b/ServerHost/AppDomainSetupUtils.cs new file mode 100644 index 0000000..b33b108 --- /dev/null +++ b/ServerHost/AppDomainSetupUtils.cs @@ -0,0 +1,95 @@ +using System; +using System.Globalization; +using System.IO; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.Remoting; + +namespace Server.Host +{ + public static class AppDomainSetupUtils + { + private static readonly object[] NoArgs = new object[0]; + + /// + /// Create a new instance of the trace listener in the specified app domain. + /// + /// The type of object to be created. + /// The AppDomain where the "far" tracer object should be created. + /// An instance of the remote trace instance. + [MethodImpl(MethodImplOptions.NoInlining)] + internal static T CreateObjectInstanceInAppDomain( + AppDomain appDomain, + object[] args = null) + where T : class + { + string appDomainName = appDomain.FriendlyName; + string className = typeof(T).GetTypeInfo().FullName; + string assemblyFilePath = typeof(T).GetTypeInfo().Assembly.Location; + + if (string.IsNullOrEmpty(className)) + { + throw new ArgumentNullException("Cannot determine class name for " + typeof(T)); + } + if (string.IsNullOrEmpty(assemblyFilePath)) + { + throw new ArgumentNullException("Cannot determine assembly location for " + typeof(T)); + } + + // Create instance of the class in the "far" app domain. + + ObjectHandle remoteObjRef = appDomain.CreateInstanceFrom( + assemblyFilePath, + className, + false, + BindingFlags.Default, + null, + args ?? NoArgs, + CultureInfo.CurrentCulture, + NoArgs); + + object remoteObj = remoteObjRef.Unwrap(); + + if (remoteObj == null) + { + throw new ArgumentNullException( + $"Could not create remote object instance {className}" + + $" from assembly {assemblyFilePath}" + + $" in AppDomain {appDomainName}"); + } + + T remoteTracer = remoteObj as T; + + if (remoteTracer == null) + { + Type type1 = remoteObj.GetType(); + Type type2 = typeof(T); + string codebase1 = type1.GetTypeInfo().Assembly.CodeBase; + string codebase2 = type2.GetTypeInfo().Assembly.CodeBase; + + throw new InvalidCastException( + $"Cannot cast server object {type1} from assembly {codebase1} to type {type2} from assembly {codebase2}"); + } + + return remoteTracer; + } + + /// + /// Construct AppDomain configuration metadata based on the current execution environment. + /// + /// AppDomainSetup info for creating an new child AppDomain. + public static AppDomainSetup GetAppDomainSetupInfo() + { + AppDomain currentAppDomain = AppDomain.CurrentDomain; + + return new AppDomainSetup + { + ApplicationBase = Directory.GetCurrentDirectory(), + ConfigurationFile = currentAppDomain.SetupInformation.ConfigurationFile, + ShadowCopyFiles = currentAppDomain.SetupInformation.ShadowCopyFiles, + ShadowCopyDirectories = currentAppDomain.SetupInformation.ShadowCopyDirectories, + CachePath = currentAppDomain.SetupInformation.CachePath + }; + } + } +} diff --git a/ServerHost/ServerHost.cs b/ServerHost/ServerHost.cs index 723a1d6..83c50d8 100644 --- a/ServerHost/ServerHost.cs +++ b/ServerHost/ServerHost.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; -using System.Globalization; using System.IO; using System.Linq; using System.Reflection; @@ -54,7 +53,7 @@ public static ServerHostHandle LoadServerInNewAppDomain( } } - AppDomainSetup setup = GetAppDomainSetupInfo(); + AppDomainSetup setup = AppDomainSetupUtils.GetAppDomainSetupInfo(); AppDomain appDomain = AppDomain.CreateDomain(serverName, null, setup); LoadedAppDomains[serverName] = appDomain; @@ -62,25 +61,9 @@ public static ServerHostHandle LoadServerInNewAppDomain( // The server class must have a public constructor which // accepts single parameter of server name. var args = new object[] { serverName }; - var noActivationAttributes = new object[0]; - object serverObj = appDomain.CreateInstanceFromAndUnwrap( - serverAssembly, serverTypeName, false, - BindingFlags.Default, null, args, CultureInfo.CurrentCulture, - noActivationAttributes); - - TServer server = serverObj as TServer; - - if (server == null) - { - Type type1 = serverObj.GetType(); - Type type2 = typeof(TServer); - string codebase1 = type1.GetTypeInfo().Assembly.CodeBase; - string codebase2 = serverType.GetTypeInfo().Assembly.CodeBase; - - throw new InvalidCastException( - $"Cannot cast server object {type1} from assembly {codebase1} to type {type2} from assembly {codebase2}"); - } + TServer server = AppDomainSetupUtils + .CreateObjectInstanceInAppDomain(appDomain, args); appDomain.UnhandledException += ReportUnobservedException; @@ -160,24 +143,6 @@ public static void UnloadAllServers() } } - /// - /// Construct AppDomain configuration metadata based on the current execution environment. - /// - /// AppDomainSetup info for creating an new child AppDomain. - public static AppDomainSetup GetAppDomainSetupInfo() - { - AppDomain currentAppDomain = AppDomain.CurrentDomain; - - return new AppDomainSetup - { - ApplicationBase = Directory.GetCurrentDirectory(), - ConfigurationFile = currentAppDomain.SetupInformation.ConfigurationFile, - ShadowCopyFiles = currentAppDomain.SetupInformation.ShadowCopyFiles, - ShadowCopyDirectories = currentAppDomain.SetupInformation.ShadowCopyDirectories, - CachePath = currentAppDomain.SetupInformation.CachePath - }; - } - /// /// Event handle method to report any unhandled exceptions in a newly created AppDomain. /// diff --git a/ServerHost/ServerHostFixture.cs b/ServerHost/ServerHostFixture.cs index 14d8453..93c36af 100644 --- a/ServerHost/ServerHostFixture.cs +++ b/ServerHost/ServerHostFixture.cs @@ -30,7 +30,7 @@ public ServerHostFixture() _log = LogManager.GetLogger(_className); - _log.InfoFormat("{0} - Initialize", _className); + _log.Info($"{_className} - Initialize"); } /// @@ -51,7 +51,7 @@ protected virtual void Dispose(bool disposing) ReleaseUnmanagedResources(); if (disposing) { - _log.InfoFormat("{0} - Dispose", _className); + _log.Info($"{_className} - Dispose"); // TODO release managed resources here } @@ -68,7 +68,7 @@ protected virtual void Dispose(bool disposing) [SuppressMessage("ReSharper", "MemberCanBeMadeStatic.Local")] private void ReleaseUnmanagedResources() { - _log.InfoFormat("{0} - ReleaseUnmanagedResources", _className); + _log.Info($"{_className} - ReleaseUnmanagedResources"); // TODO release unmanaged resources here } diff --git a/Tests/TestServer/App.config b/Tests/TestServer/App.config new file mode 100644 index 0000000..6238316 --- /dev/null +++ b/Tests/TestServer/App.config @@ -0,0 +1,23 @@ + + + +
+ + + + + + + + + + + + + + + + + + + diff --git a/Tests/TestServer/Program.cs b/Tests/TestServer/Program.cs index 21a1ff7..9e62a80 100644 --- a/Tests/TestServer/Program.cs +++ b/Tests/TestServer/Program.cs @@ -3,6 +3,7 @@ using System; using System.Reflection; +using JetBrains.Annotations; using log4net; using log4net.Config; @@ -11,6 +12,7 @@ namespace Server.Host.Tests.TestServer { + [PublicAPI] public static class Program { private static readonly ILog Log = LogManager.GetLogger("TestServer"); @@ -19,15 +21,17 @@ public static void Main(string[] args) { const string serverName = "MyTestServer"; + string progName = Assembly.GetEntryAssembly().GetName().Name; + Server server = new Server(serverName); - Log.InfoFormat("Initializing Server {0} with args = {1}", serverName, string.Join(", ", args)); + Log.Info($"Initializing Server {serverName} with args = " + string.Join(", ", args)); server.InitServer(); Log.Info("Running Server"); int rc = server.Run(); - Log.InfoFormat("{0}.exe finished with rc={1}", Assembly.GetEntryAssembly().GetName().Name, rc); + Log.Info($"{progName}.exe finished with rc={rc}"); Environment.Exit(rc); } } diff --git a/Tests/TestServer/Server.cs b/Tests/TestServer/Server.cs index f17ede2..18ae272 100644 --- a/Tests/TestServer/Server.cs +++ b/Tests/TestServer/Server.cs @@ -2,7 +2,9 @@ // Licensed with Apache 2.0 https://github.com/jthelin/ServerHost/blob/master/LICENSE using System; +using System.Diagnostics; using log4net; +using log4net.Config; namespace Server.Host.Tests.TestServer { @@ -12,12 +14,17 @@ public class Server : MarshalByRefObject public Server(string serverName) { - Log.InfoFormat("Server - {0}", serverName); + // Initialize log4net logging. + XmlConfigurator.Configure(); + + Log.Info($"Server - {serverName}"); } public void InitServer() { - Log.Info("InitServer!"); + Log.Info("InitServer - log4net"); + Debug.WriteLine("InitServer - Debug.WriteLine"); + Trace.TraceInformation("InitServer - Trace.TraceInformation"); } public int Run() diff --git a/Tests/TestServer/TestServer.csproj b/Tests/TestServer/TestServer.csproj index ccc8a5d..fed3c76 100644 --- a/Tests/TestServer/TestServer.csproj +++ b/Tests/TestServer/TestServer.csproj @@ -8,6 +8,9 @@ + + all + \ No newline at end of file diff --git a/Tests/Tests.Net46/App.config b/Tests/Tests.Net46/App.config new file mode 100644 index 0000000..6238316 --- /dev/null +++ b/Tests/Tests.Net46/App.config @@ -0,0 +1,23 @@ + + + +
+ + + + + + + + + + + + + + + + + + +