Support for AppDomains is changed in .NET Core. In .NET Core, there is exactly one AppDomain. Creation
of new AppDomains is no longer supported because they require runtime support and are generally expensive to
create.
Recently I wanted dynamically to load two different versions (18 and 30) of a component TX Text Control .NET for Windows Forms. First problem was that I was unable to load dynamically version 30 because of license problem. So, first I have loaded TX Text Control .NET for Windows Forms 30. Then I have dynamically loaded assemblies needed for TX Text Control .NET for Windows Forms 18.
First I have created a list of DLL's which I am gonna need:
If I want to have only one DLL from one version than I can siply use:
Assembly.LoadFile(assemblyPath);
In order to have both versions of assemblies loaded In order to be able to unload assembly I needed to create additional AppDomain:
AppDomainSetup domainInfo = new AppDomainSetup
{
ApplicationBase = Environment.CurrentDirectory
};
Evidence evidence = AppDomain.CurrentDomain.Evidence;
AppDomain domain = AppDomain.CreateDomain("TXTextControl18Domain", evidence, domainInfo);
Type type = typeof(Proxy);
var value = (Proxy)domain.CreateInstanceAndUnwrap(
type.Assembly.FullName,
type.FullName);
Where Proxy is:
public class Proxy : MarshalByRefObject
{
public Assembly GetAssembly(string assemblyPath)
{
try
{
return Assembly.LoadFile(assemblyPath);
}
catch (Exception)
{
return null;
// throw new InvalidOperationException(ex);
}
}
}
Now, for example, I want to get list of types inside every assembly listed:
Assembly asm;
string path = @"C:\Program Files\Text Control GmbH\TX Text Control 18.0.NET for Windows Forms\Assembly";
foreach (string assembly in listOfAssemblies)
{
asm = value.GetAssembly(Path.Combine(path, assembly));
Type[] types = asm.GetTypes();
foreach (Type t in types)
{
Console.WriteLine("Type: {0}", t);
}
}
In order to do it, I need "TXTextControl.TextControl" and "TXTextControl.StreamType" like:
Assembly asm;
string path = @"C:\Program Files\Text Control GmbH\TX Text Control 18.0.NET for Windows Forms\Assembly";
Type textControlType = null;
Type streamType = null;
foreach (string assembly in listOfAssemblies)
{
asm = value.GetAssembly(Path.Combine(path, assembly));
if (textControlType is null)
{
textControlType = asm.GetType("TXTextControl.TextControl");
}
if (streamType is null)
{
streamType = asm.GetType("TXTextControl.StreamType");
}
}
To get "StreamType.InternalFormat" I will do something like:
object objTextControl = Activator.CreateInstance(textControlType);
var textControl = (Control)objTextControl;
textControl.Dock = DockStyle.Fill;
Controls.Add(textControl);
object internalFormat = Enum.Parse(streamType, "InternalFormat");
MethodInfo miLoadWithFileName = textControlType.GetMethod("Load", new [] {typeof(string), streamType});
if (!(miLoadWithFileName is null))
{
miLoadWithFileName.Invoke(objTextControl, new[] { @"test.tx", internalFormat });
}
Same method with no paramaters:
MethodInfo miLoadWithoutParameters = textControlType.GetMethod("Load", Type.EmptyTypes);
if (!(miLoadWithoutParameters is null))
{
miLoadWithoutParameters.Invoke(objTextControl, null);
}
Third example, I want ApplicationFields collection, and iterate through it. Normally I would use something like:
foreach (ApplicationField applicationField in textControl.ApplicationFields)
{
Debug.WriteLine(applicationField.Text);
}
IEnumerable ApplicationFields I am gonna get like:
PropertyInfo piApplicationFields = textControlType.GetProperty("ApplicationFields");
if (!(piApplicationFields is null))
{
applicationFieldsObject = piApplicationFields.GetValue(objTextControl, null);
}
IEnumerable applicationFields = applicationFieldsObject as IEnumerable;
Property Text from ApplicationField:
PropertyInfo piParameters = applicationFieldType.GetProperty("Text");
if (!(piParameters is null))
{
object applicationFieldText = piParameters.GetValue(applicationField, null);
Console.WriteLine(applicationFieldText);
}
Whole method:
object applicationFieldsObject = null;
Assembly asm;
Proxy value = DomainInstance();
string path = @"C:\Program Files\Text Control GmbH\TX Text Control 18.0.NET for Windows Forms\Assembly";
Type textControlType = null;
Type applicationFieldType = null;
foreach (string assembly in listOfAssemblies)
{
asm = value.GetAssembly(Path.Combine(path, assembly));
if (textControlType is null)
{
textControlType = asm.GetType("TXTextControl.TextControl");
}
if (applicationFieldType is null)
{
applicationFieldType = asm.GetType("TXTextControl.ApplicationField");
}
}
object objTextControl = Activator.CreateInstance(textControlType);
Control textControl = (Control)objTextControl;
textControl.Dock = DockStyle.Fill;
Controls.Add(textControl);
MethodInfo miLoadWithoutParameters = textControlType.GetMethod("Load", Type.EmptyTypes);
if (!(miLoadWithoutParameters is null))
{
miLoadWithoutParameters.Invoke(objTextControl, null);
}
PropertyInfo piApplicationFields = textControlType.GetProperty("ApplicationFields");
if (!(piApplicationFields is null))
{
applicationFieldsObject = piApplicationFields.GetValue(objTextControl, null);
}
IEnumerable applicationFields = applicationFieldsObject as IEnumerable;
foreach (object applicationField in applicationFields)
{
PropertyInfo piParameters = applicationFieldType.GetProperty("Text");
if (!(piParameters is null))
{
object applicationFieldText = piParameters.GetValue(applicationField, null);
Console.WriteLine(applicationFieldText);
}
}
If I want to take string array of paramaters from application field for example:
PropertyInfo piParametersAppFld = applicationFieldType.GetProperty("Parameters");
if (!(piParametersAppFld is null))
{
object parameters = piParametersAppFld.GetValue(applicationField, null);
string[] paramatersAry = parameters as string[];
foreach (string parameter in paramatersAry)
{
Console.WriteLine(parameter);
}
}
To get Count property of ApplicationFields:
applicationFieldCollectionType = asm.GetType("TXTextControl.ApplicationFieldCollection");
PropertyInfo piCountAppFld = applicationFieldCollectionType.GetProperty("Count");
if (!(piCountAppFld is null))
{
object appFldsCount = piCountAppFld.GetValue(applicationFieldsObject, null);
Console.WriteLine((int)appFldsCount);
}
Example dowload from here.
EDIT: In order to force my app to work on a machine where I don't have TX Text Control installed, I had to change the file DynamicallyLoadingAssemblies.exe.config like:
Here I wrote the example of displaying KML files on google maps, and here is one my example of creating KML. I was using sample from here.
The model:
using System.Xml.Serialization;
namespace CreateKmlFromFiles
{
public class KmlModel
{
[XmlRoot("kml", Namespace = "http://www.opengis.net/kml/2.2")]
public class Kml
{
public Document Document { get; set; }
}
public class Document
{
public string name { get; set; }
public string description { get; set; }
public Style Style { get; set; }
public Placemark Placemark { get; set; }
}
public class Style
{
[XmlAttribute("id")]
public string id { get; set; }
public LineStyle LineStyle { get; set; }
public PolyStyle PolyStyle { get; set; }
}
public class LineStyle
{
public string color { get; set; }
public string width { get; set; }
}
public class PolyStyle
{
public string color { get; set; }
}
public class Placemark
{
public string name { get; set; }
public string description { get; set; }
public string styleUrl { get; set; }
public LineString LineString { get; set; }
}
public class LineString
{
public string extrude { get; set; }
public string tessellate { get; set; }
public string altitudeMode { get; set; }
public string coordinates { get; set; }
}
}
}
The code:
using System;
using System.IO;
using System.Text;
using System.Xml.Serialization;
namespace CreateKmlFromFiles
{
class Program
{
static void Main(string[] args)
{
StringBuilder sb = new StringBuilder();
sb.Append("7.0971881, 50.7359953, 2357");
KmlModel.Kml kmlModel = new KmlModel.Kml
{
Document = new KmlModel.Document
{
name = "Paths"
, description = "test"
, Style = new KmlModel.Style
{
id = "red"
, LineStyle = new KmlModel.LineStyle
{
color = "7f0000ff"
, width = "4"
}
, PolyStyle = new KmlModel.PolyStyle
{
color = "7f0000ff"
}
}
, Placemark = new KmlModel.Placemark
{
name = "Absolute Extruded"
, description = "Transparent green wall with yellow outlines"
, styleUrl = "#yellowLineGreenPoly"
, LineString = new KmlModel.LineString
{
extrude = "1"
, tessellate = "1"
, altitudeMode = "absolute"
, coordinates = sb.ToString()
}
}
}
};
TextWriter txtWriter = new StreamWriter
(
Path.ChangeExtension
(
System.Reflection.Assembly.GetEntryAssembly().Location
, ".kml"
));
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "http://www.opengis.net/kml/2.2");
XmlSerializer xmlSerializer = new XmlSerializer(typeof(KmlModel.Kml));
xmlSerializer.Serialize(txtWriter, kmlModel, ns);
txtWriter.Close();
}
}
}
Here and here I gave examples on how to get the exe path, but sometimes that is not enough, sometimes I need to find path of our class library, for example, that is why sometimes I need Directory.GetCurrentDirectory()