/// <summary> /// Will create a new <see cref="System.AppDomain"/> and execute the <paramref name="func"/> in it, returning the results. /// </summary> /// <remarks> /// This method will execute the <paramref name="func"/> in a new <see cref="System.AppDomain"/>; any required assemblies will be loaded into that <see cref="System.AppDomain"/>. /// When this method is finished, the <see cref="System.AppDomain"/> will be unloaded, removing all of the loaded assemblies. /// </remarks> /// <param name="privateBinPath">A list of directories that are probed for private assemblies. Each directory should be separated by semicolon [;].</param> /// <param name="state">An object passed into the <paramref name="func"/>.</param> /// <param name="func">The function to execute in the separate <see cref="System.AppDomain"/>.</param> /// <param name="exception">If any errors occur, a <b>null</b> will be returned and this exception will be populated.</param> /// <returns>The results of the <paramref name="func"/>.</returns> /// <example> /// The following code example will use the <see cref="dodSON.Core.AppDomain.AppDomainHelper.Execute(string, object, Func{object, object}, out Exception)"/> function to execute code in a separate application domain. /// The function, running in a separate application domain, will receive a <b>state</b>; this <b>state</b> will be modified to demonstrate passing and mutating <b>state</b>. /// In order for a <b>state</b> to be mutable, it must inherit from <see cref="MarshalByRefObject"/>; this will allow it to use proxies to communicate across the application domain boundaries. /// <para><br/>Create a console application and add the following code:</para> /// <code> /// [Serializable] /// public class StateClass /// : MarshalByRefObject /// { /// #region Ctor /// private StateClass() : base() { } /// /// public StateClass(string id, string name, double value1, long value2, bool value3) : this() /// { /// Id = id; /// Name = name; /// Value1 = value1; /// Value2 = value2; /// Value3 = value3; /// } /// #endregion /// #region Public Methods /// public string Id { get; set; } /// public string Name { get; set; } /// public double Value1 { get; set; } /// public long Value2 { get; set; } /// public bool Value3 { get; set; } /// #endregion /// } /// /// static void Main(string[] args) /// { /// // an object that can be passed into, and out of, the function /// var state = new StateClass("ID", "NAME", 3.141, 14, false); /// Console.WriteLine($"Application Domain #{AppDomain.CurrentDomain.Id}, State = [{state.Id}, {state.Name}, {state.Value1}, {state.Value2}, {state.Value3}]"); /// /// // the function to execute in the separate application domain /// var privateBinPath = ""; /// var func = new Func<object, object>( /// (state_) => /// { /// // display state /// var stateRef = (StateClass)state_; /// Console.WriteLine($"Application Domain #{AppDomain.CurrentDomain.Id}, State = [{stateRef.Id}, {stateRef.Name}, {stateRef.Value1}, {stateRef.Value2}, {stateRef.Value3}]"); /// // change state /// stateRef.Id = "SimpleId"; /// stateRef.Name = $"SimpleName"; /// stateRef.Value1 = Math.E; /// stateRef.Value2 = dodSON.Core.Common.ByteCountHelper.FromKilobytes(1); /// stateRef.Value3 = true; /// // display state /// Console.WriteLine($"Application Domain #{AppDomain.CurrentDomain.Id}, State = [{stateRef.Id}, {stateRef.Name}, {stateRef.Value1}, {stateRef.Value2}, {stateRef.Value3}]"); /// // return greetings /// return $"Hello from Application Domain #{System.AppDomain.CurrentDomain.Id}"; /// }); /// // execute function in separate application domain /// var funcResult = (string)dodSON.Core.Common.AppDomainHelper.Execute(privateBinPath, state, func, out Exception executeException); /// // display results /// Console.WriteLine($"Application Domain #{AppDomain.CurrentDomain.Id}, Function Results = {funcResult}"); /// Console.WriteLine($"Application Domain #{AppDomain.CurrentDomain.Id}, State = [{state.Id}, {state.Name}, {state.Value1}, {state.Value2}, {state.Value3}]"); /// // /// Console.WriteLine("press anykey>"); /// Console.ReadKey(true); /// } /// </code> /// <para><br/>The final screen shot.</para> /// <code> /// // Application Domain #1, State = [ID, NAME, 3.141, 14, False] /// // /// // Application Domain #2, State = [ID, NAME, 3.141, 14, False] /// // Application Domain #2, State = [SimpleId, SimpleName, 2.71828182845905, 1024, True] /// // /// // Application Domain #1, Function Results = Hello from Application Domain #2 /// // Application Domain #1, State = [SimpleId, SimpleName, 2.71828182845905, 1024, True] /// // press anykey> /// </code> /// </example> public static object Execute(string privateBinPath, object state, Func <object, object> func, out Exception exception) { // checks if (func == null) { throw new ArgumentNullException(nameof(func)); } // var proxySettings = new dodSON.Core.AppDomain.TypeProxySettings(typeof(DiscoveryAssistant).FullName, null, null, System.IO.Path.GetDirectoryName(typeof(DiscoveryAssistant).Assembly.Location), privateBinPath); var pluginFactory = new Addon.PluginFactory(proxySettings); // var plugin = pluginFactory.Load(); plugin.Start(); // var result = ((DiscoveryAssistant)plugin).Execute(state, func, out exception); // plugin.Stop(); pluginFactory.Unload(); // return(result); }
/// <summary> /// Will attempt to load the <paramref name="assemblyFilename"/> into a new <see cref="System.AppDomain"/>, search for the specified <paramref name="typeName"/> and execute the <paramref name="func"/> passing the discovered <paramref name="typeName"/>. /// </summary> /// <remarks> /// This method will load the <paramref name="typeName"/>'s <see cref="Type"/>, and any other required assemblies, into a new <see cref="System.AppDomain"/>. /// When this method is finished, the <see cref="System.AppDomain"/> will be unloaded, removing all of the loaded assemblies. /// </remarks> /// <param name="assemblyFilename">The assembly file to load and search.</param> /// <param name="typeName">The name of the type to search for.</param> /// <param name="privateBinPath">A list of directories that are probed for private assemblies. Each directory should be separated by semicolon [;].</param> /// <param name="state">An object passed into the <paramref name="func"/>.</param> /// <param name="func">The function to execute when the type is discovered.</param> /// <param name="exception">If any errors occur, a <b>null</b> will be returned and this exception will be populated.</param> /// <returns>If the type was found, the results of the <paramref name="func"/> are returned; otherwise <b>null</b> is returned along with a <b>null</b> exception.</returns> /// <example> /// The following code example will use the <see cref="dodSON.Core.AppDomain.AppDomainHelper.QueryType(string, string, string, object, Func{object, Type, object}, out Exception)"/> function to search for a type not found in it's assembly. /// The function, running in a separate application domain, will load several assemblies. These assemblies can be seen in the second list. /// The third list shows that those assemblies were not loaded into the main application domain. /// <para><br/>Place the different assemblies into different folders.</para> /// <para>Application Folder: Main_Executable, The InterfaceLibrary</para> /// <para>Code Folder: The TypeLibrary</para> /// <para>CustomCode Folder: The CustomCodeLibrary</para> /// <para><br/>Create a console application and add the following code:</para> /// <code> /// static void Main(string[] args) /// { /// // location of assembly to search /// var TypeLibraryAssemblyFilename = @"C:\(WORKING)\Dev\AppDomainHelper\Code\TypeLibrary.dll"; // ######## BE SURE TO CHANGE THIS ######## /// // /// DisplayLoadedAssemblies(); /// // location of assembly to search /// var assemblyFilename = TypeLibraryAssemblyFilename; /// // type to search for /// var typeName = "TypeLibrary.Alpha"; /// // any paths that contain required assemblies /// var privateBinPath = @"C:\(WORKING)\Dev\AppDomainHelper\CustomCode"; // ######## BE SURE TO CHANGE THIS ######## /// // an object that can be passed into the function /// var state = Tuple.Create("AlphaId", "AlphaName"); /// // the function to execute in the separate application domain /// var func = new Func<object, Type, object>( /// (state_, type_) => /// { /// Console.WriteLine($"Application Domain #{AppDomain.CurrentDomain.Id}, Discovered Type={type_.AssemblyQualifiedName}"); /// // create instance of discovered type /// var parameterTypes = new Type[] { typeof(string), typeof(string) }; /// var st = (Tuple<string, string>)state_; /// var parameters = new object[] { st.Item1, st.Item2 }; /// var typeInstance = (InterfaceLibrary.IAlpha)dodSON.Core.Common.InstantiationHelper.InvokeCtor(type_, parameterTypes, parameters); /// // execute instance method /// var returnMessage = typeInstance.Hello($"Message From Application Domain #{System.AppDomain.CurrentDomain.Id}"); /// // /// DisplayLoadedAssemblies(); /// return returnMessage; /// }); /// // execute the function in a separate application domain /// var result = dodSON.Core.Common.AppDomainHelper.QueryType(assemblyFilename, /// typeName, /// privateBinPath, /// state, /// func, /// out Exception queryTypeException); /// // report results /// Console.WriteLine($"Reporting results in Application Domain #{System.AppDomain.CurrentDomain.Id}"); /// if (queryTypeException != null) /// { /// Console.WriteLine($"Exception = {queryTypeException.InnerException.Message}"); /// } /// else /// { /// Console.WriteLine($"Results = \"{result}\""); /// } /// // /// DisplayLoadedAssemblies(); /// // /// Console.WriteLine("press anykey>"); /// Console.ReadKey(true); /// } /// /// private static void DisplayLoadedAssemblies() /// { /// Console.WriteLine(); /// Console.WriteLine("--------------------------------"); /// Console.WriteLine($"Application Domain #{AppDomain.CurrentDomain.Id}"); /// var count = 0; /// foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) /// { /// Console.WriteLine($"#{++count} = {assembly.FullName}, {assembly.Location}"); /// } /// Console.WriteLine("--------------------------------"); /// Console.WriteLine(); /// } /// </code> /// <para><br/>The final screen shot.</para> /// <code> /// // -------------------------------- /// // Application Domain #1 /// // #1 = mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, C:\Windows\Microsoft.NET\Framework\v4.0.30319\mscorlib.dll /// // #2 = ConsoleApp13, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null, c:\users\user\documents\visual studio 2017\Projects\ConsoleApp13\ConsoleApp13\bin\Debug\ConsoleApp13.exe /// // #3 = dodSON.Core, Version=1.1.0.0, Culture=neutral, PublicKeyToken=null, c:\users\user\documents\visual studio 2017\Projects\ConsoleApp13\ConsoleApp13\bin\Debug\dodSON.Core.dll /// // -------------------------------- /// // /// // Application Domain #2, Discovered Type=TypeLibrary.Alpha, TypeLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null /// // /// // -------------------------------- /// // Application Domain #2 /// // #1 = mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, C:\Windows\Microsoft.NET\Framework\v4.0.30319\mscorlib.dll /// // #2 = dodSON.Core, Version=1.1.0.0, Culture=neutral, PublicKeyToken=null, c:\users\user\documents\visual studio 2017\Projects\ConsoleApp13\ConsoleApp13\bin\Debug\dodSON.Core.dll /// // #3 = ConsoleApp13, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null, c:\users\user\documents\visual studio 2017\Projects\ConsoleApp13\ConsoleApp13\bin\Debug\ConsoleApp13.exe /// // #4 = TypeLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null, C:\(WORKING)\Dev\AppDomainHelper\Code\TypeLibrary.dll /// // #5 = InterfaceLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null, c:\users\user\documents\visual studio 2017\Projects\ConsoleApp13\ConsoleApp13\bin\Debug\InterfaceLibrary.dll /// // #6 = CustomCodeLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null, C:\(WORKING)\Dev\AppDomainHelper\CustomCode\CustomCodeLibrary.dll /// // -------------------------------- /// // /// // Reporting results in Application Domain #1 /// // Results = "Hello from inside AlphaName in Application Domain #2. (Id=AlphaId), Message="Message From Application Domain #2"" /// // /// // -------------------------------- /// // Application Domain #1 /// // #1 = mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, C:\Windows\Microsoft.NET\Framework\v4.0.30319\mscorlib.dll /// // #2 = ConsoleApp13, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null, c:\users\user\documents\visual studio 2017\Projects\ConsoleApp13\ConsoleApp13\bin\Debug\ConsoleApp13.exe /// // #3 = dodSON.Core, Version=1.1.0.0, Culture=neutral, PublicKeyToken=null, c:\users\user\documents\visual studio 2017\Projects\ConsoleApp13\ConsoleApp13\bin\Debug\dodSON.Core.dll /// // #4 = System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Core\v4.0_4.0.0.0__b77a5c561934e089\System.Core.dll /// // #5 = System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll /// // -------------------------------- /// // /// // press anykey> /// </code> /// <para><br/><b>The InterfaceLibrary</b></para> /// <para>Create a console application and add the following code:</para> /// <code> /// public interface IAlpha /// { /// string Id { get; } /// string Name { get; } /// string Hello(string message); /// } /// </code> /// <para><b>The TypeLibrary</b></para> /// <para>Create a console application and add the following code:</para> /// <code> /// public class Alpha /// : InterfaceLibrary.IAlpha /// { /// #region Ctor /// private Alpha() { } /// /// public Alpha(string id, string name) : this() /// { /// _CustomClass = new CustomCodeLibrary.CustomClass(id); /// Name = name; /// } /// #endregion /// #region Private Fields /// private CustomCodeLibrary.CustomClass _CustomClass = null; /// #endregion /// #region IAlpha Methods /// /// public string Id { get { return _CustomClass.Id; } } /// /// public string Name { get; } /// /// public string Hello(string message) /// { /// return $"Hello from inside {Name} in Application Domain #{System.AppDomain.CurrentDomain.Id}. (Id={Id}), Message=\"{message}\""; /// } /// #endregion /// } /// </code> /// <para><br/><b>The CustomCodeLibrary</b></para> /// <para>Create a console application and add the following code:</para> /// <code> /// public class CustomClass /// { /// #region Ctor /// private CustomClass() { } /// /// public CustomClass(string id) : this() /// { /// Id = id; /// } /// #endregion /// #region Public Methods /// public string Id { get; } /// #endregion /// } /// </code> /// </example> public static object QueryType(string assemblyFilename, string typeName, string privateBinPath, object state, Func <object, Type, object> func, out Exception exception) { // checks if (string.IsNullOrWhiteSpace(assemblyFilename)) { throw new ArgumentNullException(nameof(assemblyFilename)); } if (string.IsNullOrWhiteSpace(typeName)) { throw new ArgumentNullException(nameof(typeName)); } if (func == null) { throw new ArgumentNullException(nameof(func)); } // var proxySettings = new dodSON.Core.AppDomain.TypeProxySettings(typeof(DiscoveryAssistant).AssemblyQualifiedName, null, null, System.IO.Path.GetDirectoryName(typeof(DiscoveryAssistant).Assembly.Location), privateBinPath); var pluginFactory = new Addon.PluginFactory(proxySettings); // var plugin = pluginFactory.Load(); plugin.Start(); // var result = ((DiscoveryAssistant)plugin).FindType(assemblyFilename, typeName, state, func, out exception); // plugin.Stop(); pluginFactory.Unload(); // return(result); }