diff --git a/multi-instances/MultipleInstancesDemo/App.xaml.cs b/multi-instances/MultipleInstancesDemo/App.xaml.cs index 2894892..a485a34 100644 --- a/multi-instances/MultipleInstancesDemo/App.xaml.cs +++ b/multi-instances/MultipleInstancesDemo/App.xaml.cs @@ -6,6 +6,7 @@ using System.Windows; using DOT.AGM.GwTransport; using DOT.Logging; +using log4net; using log4net.Config; using Tick42.StartingContext; @@ -19,25 +20,25 @@ public partial class App : Application private const string MutexName = "MultipleInstancesDemo_SingleInstanceMutex"; private const string PipeName = "MultipleInstancesDemo_Pipe"; + private int instance_; + private volatile int alive_; + private Mutex mutex_; private Thread pipeServerThread_; private GwProtocolSerializer serializer_; - protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); - AppDomain.CurrentDomain.UnhandledException += (sender, args) => - { - }; + AppDomain.CurrentDomain.UnhandledException += (sender, args) => { }; Debugger.Launch(); XmlConfigurator.Configure(); DotLoggingFacade.UseLibrary(LogLibrary.DynamicLog4Net); serializer_ = new GwProtocolSerializer(ValueGwConverter.Settings.None); // or choose another ShutdownMode if needed - ShutdownMode = ShutdownMode.OnLastWindowClose; + ShutdownMode = ShutdownMode.OnExplicitShutdown; mutex_ = new Mutex(true, MutexName, out bool isOwned); @@ -131,14 +132,120 @@ private void SendArgumentsToFirstInstance(string[] args, RemoteConfigurationOpti private void ProcessArguments(string[] args, RemoteConfigurationOptions rco) { - // Handle the arguments as needed in your application - Dispatcher.Invoke(() => + // isolate the window in a new AppDomain, so it has its own log file + + var setup = new AppDomainSetup { - // Handle the arguments in the UI thread - // For example, pass the arguments to a new instance of the MainWindow + ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase, + PrivateBinPath = "bin" // Ensure the private bin path is set if necessary + }; + + var newDomain = AppDomain.CreateDomain(rco?.InstanceId ?? "Main", null, setup); + newDomain.Load(typeof(LogManager).Assembly.FullName); + + var runnerType = typeof(Isolator); + var runner = (Isolator)newDomain.CreateInstanceAndUnwrap(runnerType.Assembly.FullName, runnerType.FullName); + + // Run the window in the new AppDomain + Dispatcher.BeginInvoke((Action)(() => + { + Interlocked.Increment(ref alive_); + runner.RunIsolated(args, new SerializableRCO(rco), ++instance_); + // Optionally unload the new AppDomain when done + AppDomain.Unload(newDomain); + if (Interlocked.Decrement(ref alive_) == 0) + { + Shutdown(); + } + })); + + + // or handle the arguments as needed in your application + // and create the window in the same AppDomain - this will share the log file with the main instance + // Dispatcher.Invoke(() => + // { + // // Handle the arguments in the UI thread + // // For example, pass the arguments to a new instance of the MainWindow + // + // var window = new MainWindow(); + // + // window.ProcessArguments(args, rco); + // }); + } + + [Serializable] + public class SerializableRCO + { + public SerializableRCO(RemoteConfigurationOptions rco) + { + if (rco == null) + { + return; + } + + AppName = rco.AppName; + InstanceId = rco.InstanceId; + Environment = rco.Environment; + Region = rco.Region; + GwUrl = rco.GwUrl; + GwToken = rco.GwToken; + Username = rco.Username; + } + + public string Environment { get; set; } + public string Region { get; set; } + public string GwUrl { get; set; } + public string GwToken { get; set; } + public string AppName { get; set; } + public string InstanceId { get; set; } + public string Username { get; set; } + + public RemoteConfigurationOptions ToRCO() + { + return new RemoteConfigurationOptions + { + AppName = AppName, + InstanceId = InstanceId, + Environment = Environment, + Region = Region, + GwUrl = GwUrl, + GwToken = GwToken, + Username = Username + }; + } + } + + public class Isolator : MarshalByRefObject + { + public void RunIsolated(string[] args, SerializableRCO rco, int instance) + { + ConfigureLog4Net(rco?.InstanceId ?? "main"); + DotLoggingFacade.UseLibrary(LogLibrary.DynamicLog4Net); + var app = new Application(); var window = new MainWindow(); - window.ProcessArguments(args, rco); - }); + window.ProcessArguments(args, rco?.ToRCO(), instance); + app.Run(window); + } + + private void ConfigureLog4Net(string instance) + { + // Load log4net configuration from a file or create programmatically + Environment.SetEnvironmentVariable("instance_name", instance); + //var logRepository = LogManager.GetRepository(Assembly.GetEntryAssembly()); + string logConfigFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "log4net.config"); + XmlConfigurator.Configure(new FileInfo(logConfigFilePath)); + + // Set a different log file for each AppDomain + // foreach (var appender in log4net.LogManager.GetRepository().GetAppenders()) + // { + // if (appender.Name == "RollingFileAppender" && appender is RollingFileAppender fileAppender) + // { + // string logFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"{instance}.log"); + // fileAppender.File = logFile; + // fileAppender.ActivateOptions(); // Refresh the appender configuration + // } + // } + } } } } \ No newline at end of file diff --git a/multi-instances/MultipleInstancesDemo/MainWindow.xaml.cs b/multi-instances/MultipleInstancesDemo/MainWindow.xaml.cs index 0a02a4c..13e0d41 100644 --- a/multi-instances/MultipleInstancesDemo/MainWindow.xaml.cs +++ b/multi-instances/MultipleInstancesDemo/MainWindow.xaml.cs @@ -65,7 +65,7 @@ protected override void OnClosed(EventArgs e) } } - public async void ProcessArguments(string[] args, RemoteConfigurationOptions rco) + public async void ProcessArguments(string[] args, RemoteConfigurationOptions rco, int instance) { var initializeOptions = new InitializeOptions { @@ -89,10 +89,20 @@ public async void ProcessArguments(string[] args, RemoteConfigurationOptions rco var initializing = Glue42.InitializeGlue(initializeOptions); glue_ = await initializing; - wnd_ = await glue_.GlueWindows.RegisterStartupWindow(this, initializeOptions.ApplicationName, - w => w.WithChannelSupport(true)); - glue_.Interop.RegisterService(this, - modifyServiceConfig: c => c.Dispatcher = new WrappedDispatcher(Dispatcher)); + bool somethingService = instance % 2 != 0; + string title = initializeOptions.ApplicationName + " " + instance + (somethingService + ? " SOMETHING" + : " NO SERVICE"); + + wnd_ = await glue_.GlueWindows.RegisterStartupWindow(this, title, + w => w.WithChannelSupport(true).WithTitle(title)); + + if (somethingService) + { + glue_.Interop.RegisterService(this, + modifyServiceConfig: c => c.Dispatcher = new WrappedDispatcher(Dispatcher)); + } + caller_ = glue_.Interop.CreateServiceProxy(); T GetRestoreState(T _) => glue_.GetRestoreState(); @@ -115,6 +125,11 @@ public async void ProcessArguments(string[] args, RemoteConfigurationOptions rco private void DoSomething_Click(object sender, RoutedEventArgs e) { + // to invoke it from JS you can just go: + // glue.interop.invoke('io.multiple.GetSomethingElse', {something: {thing: 'frog', price: 3.14, happenedOn: new Date()}}, "all"); + + // this will invoke the service method on the first instance + // and will add the result to the list caller_.GetSomethingElse(new Something { Thing = "Something", diff --git a/multi-instances/MultipleInstancesDemo/MultipleInstancesDemo.csproj b/multi-instances/MultipleInstancesDemo/MultipleInstancesDemo.csproj index b71a30a..ec8d085 100644 --- a/multi-instances/MultipleInstancesDemo/MultipleInstancesDemo.csproj +++ b/multi-instances/MultipleInstancesDemo/MultipleInstancesDemo.csproj @@ -34,8 +34,8 @@ 4 - - packages\io.Connect.NET.1.10.0.0\lib\net45\ioConnectNET.dll + + packages\io.Connect.NET.1.12.0.0\lib\net45\ioConnectNET.dll packages\log4net.2.0.17\lib\net45\log4net.dll @@ -94,6 +94,9 @@ ResXFileCodeGenerator Resources.Designer.cs + + Always + SettingsSingleFileGenerator diff --git a/multi-instances/MultipleInstancesDemo/log4net.config b/multi-instances/MultipleInstancesDemo/log4net.config new file mode 100644 index 0000000..16f73de --- /dev/null +++ b/multi-instances/MultipleInstancesDemo/log4net.config @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/multi-instances/MultipleInstancesDemo/packages.config b/multi-instances/MultipleInstancesDemo/packages.config index 8d82339..4fe590b 100644 --- a/multi-instances/MultipleInstancesDemo/packages.config +++ b/multi-instances/MultipleInstancesDemo/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file