Generating DLL assembly dynamically at run time
using System.CodeDom.Compiler;
using System.Diagnostics;
using Microsoft.CSharp;
CSharpCodeProvider codeProvider = new CSharpCodeProvider();
ICodeCompiler icc = codeProvider.CreateCompiler();
System.CodeDom.Compiler.CompilerParameters parameters = new CompilerParameters();
parameters.GenerateExecutable = false;
parameters.OutputAssembly = "AutoGen.dll";
CompilerResults results = icc.CompileAssemblyFromSource(parameters, yourCodeAsString);
Adapted from http://support.microsoft.com/kb/304655
How to create dll that contain class in runTime ?
Yes, use CSharpCodeProvider.
You can read the sample code for "Snippy" that I used for C# in Depth - it does exactly this sort of thing.
You can ask CSharpCodeProvider
to write to a file or build the assembly in memmory.
Sample code:
using System;
using System.CodeDom.Compiler;
using Microsoft.CSharp;
class Test
{
static void Main()
{
var provider = new CSharpCodeProvider();
var options = new CompilerParameters {
OutputAssembly = "Foo.dll"
};
string source = "public class Foo {}";
provider.CompileAssemblyFromSource(options, new[] { source });
}
}
How to dynamically load and unload (reload) a .dll assembly
You cannot unload a single assembly, but you can unload an Appdomain. This means you need to create an app domain and load the assembly in the App domain.
Exmaple:
var appDomain = AppDomain.CreateDomain("MyAppDomain", null, new AppDomainSetup
{
ApplicationName = "MyAppDomain",
ShadowCopyFiles = "true",
PrivateBinPath = "MyAppDomainBin",
});
ShadowCopyFiles property will cause the .NET runtime to copy dlls in "MyAppDomainBin" folder to a cache location so as not to lock the files in that path. Instead the cached files are locked. For more information refer to article about Shadow Copying Assemblies
Now let's say you have an class you want to use in the assembly you want to unload. In your main app domain you call CreateInstanceAndUnwrap
to get an instance of the object
_appDomain.CreateInstanceAndUnwrap("MyAssemblyName", "MyNameSpace.MyClass");
However, and this is very important, "Unwrap" part of CreateInstanceAndUnwrap
will cause the assembly to be loaded in your main app domain if your class does not inherit from MarshalByRefObject
. So basically you achieved nothing by creating an app domain.
To solve this problem, create a 3rd Assembly containing an Interface that is implemented by your class.
For example:
public interface IMyInterface
{
void DoSomething();
}
Then add reference to the assembly containing the interface in both your main application and your dynamically loaded assembly project. And have your class implement the interface, and inherit from MarshalByRefObject
. Example:
public class MyClass : MarshalByRefObject, IMyInterface
{
public void DoSomething()
{
Console.WriteLine("Doing something.");
}
}
And to get a reference to your object:
var myObj = (IMyInterface)_appDomain.CreateInstanceAndUnwrap("MyAssemblyName", "MyNameSpace.MyClass");
Now you can call methods on your object, and .NET Runtime will use Remoting to forward the call to the other domain. It will use Serialization to serialize the parameters and return values to and from both domains. So make sure your classes used in parameters and return values are marked with [Serializable]
Attribute. Or they can inherit from MarshalByRefObject
in which case the you are passing a reference cross domains.
To have your application monitor changes to the folder, you can setup a FileSystemWatcher
to monitor changes to the folder "MyAppDomainBin"
var watcher = new FileSystemWatcher(Path.GetFullPath(Path.Combine(".", "MyAppDomainBin")))
{
NotifyFilter = NotifyFilters.LastWrite,
};
watcher.EnableRaisingEvents = true;
watcher.Changed += Folder_Changed;
And in the Folder_Changed
handler unload the appdomain and reload it again
private static async void Watcher_Changed(object sender, FileSystemEventArgs e)
{
Console.WriteLine("Folder changed");
AppDomain.Unload(_appDomain);
_appDomain = AppDomain.CreateDomain("MyAppDomain", null, new AppDomainSetup
{
ApplicationName = "MyAppDomain",
ShadowCopyFiles = "true",
PrivateBinPath = "MyAppDomainBin",
});
}
Then when you replace your DLL, in "MyAppDomainBin" folder, your application domain will be unloaded, and a new one will be created. Your old object references will be invalid (since they reference objects in an unloaded app domain), and you will need to create new ones.
A final note: AppDomains and .NET Remoting are not supported in .NET Core or future versions of .NET (.NET 5+). In those version, separation is achieved by creating separate processes instead of app domains. And using some sort of messaging library to communicate between processes.
Loading DLLs at runtime in C#
Members must be resolvable at compile time to be called directly from C#. Otherwise you must use reflection or dynamic objects.
Reflection
namespace ConsoleApplication1
{
using System;
using System.Reflection;
class Program
{
static void Main(string[] args)
{
var DLL = Assembly.LoadFile(@"C:\visual studio 2012\Projects\ConsoleApplication1\ConsoleApplication1\DLL.dll");
foreach(Type type in DLL.GetExportedTypes())
{
var c = Activator.CreateInstance(type);
type.InvokeMember("Output", BindingFlags.InvokeMethod, null, c, new object[] {@"Hello"});
}
Console.ReadLine();
}
}
}
Dynamic (.NET 4.0)
namespace ConsoleApplication1
{
using System;
using System.Reflection;
class Program
{
static void Main(string[] args)
{
var DLL = Assembly.LoadFile(@"C:\visual studio 2012\Projects\ConsoleApplication1\ConsoleApplication1\DLL.dll");
foreach(Type type in DLL.GetExportedTypes())
{
dynamic c = Activator.CreateInstance(type);
c.Output(@"Hello");
}
Console.ReadLine();
}
}
}
How to write to an at runtime generated .dll file in C#?
As I said in the comments, I do this often with a twist.
I have a calculus engine which compiles formulas. Every time a formula changes, it's re compiled.
Every time i need to run a formula, i instantiate its class.
But....
Every time I recompile, I create a NEW dll with a DIFFERENT name.
So I use a timestamp for the names and a timestamp for the class names.
Everytime I instantiate, I look for the latest dll
So my class inside the dll looks like:
public class MyGeneratedClass_20191024103000 {
// do stuff
}
assembly creation (pseudocode):
aseemblyManager.CreateLibrary(OUTPUT_DLLS_PATH + "\\Calculus_" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".dll", refs, sourcecode) ... etc
aseembly load:
string pathNewest = ListFolderSortByDate(); //you should also get the timestamp
assembly = Assembly.LoadFrom(pathNewest ); //register dll
mytype = assembly.GetType("mycalculus");
finally, instantiation:
myobject= Activator.CreateInstance(mytype , new object[] { some parameters });
mytype .GetMethod("Calculate" + timestamp).Invoke(myobject, arrParam);
Load DLL and create instances of interface implementations dynamically at runtime
You have to use reflection (of course).
Take a look at the Assembly
class.
You can e.g. use Assembly.LoadFile
or Assembly.LoadFrom
to load the assembly.
To activate a type contained in an assmebly in order to invoke its member, you can use Assembly.CreateInstance
:
Assembly assembly = Assembly.LoadFile("C:\bin\runtime.dll");
TypeInsideAssembly instanceOfTypeInsideAssembly = (TypeInsideAssembly) assembly.CreateInstance(typeOf(TypeInsideAssembly));
instanceOfTypeInsideAssembly.InvokeMethod(params);
If the constructor requires arguments you can use the appropriate overload CreateInstance(String, Boolean, BindingFlags, Binder, Object[], CultureInfo, Object[])
.
Example
The following generic example uses reflection to create instances of TBase
located in a specified assembly, expecting a parameterless constructor. TBase
can be a class (base class) or interface.
Types that are not public
or didn't define a parameterless constructor are ignored.
private IEnumerable<TBase> GetInstancesOf<TBase>(string assemblyFilePath)
{
var assembly = Assembly.LoadFile(assemblyFilePath);
// Get all public types that are defined in this assembly
Type[] publicTypes = assembly.GetExportedTypes();
// Filter all public types in assembly and return only types...
IEnumerable<TBase> documentProviders = publicTypes
// ...that are not an interface (as we can't create instances of interfaces)...
.Where(publicType => !publicType.IsInterface
// ...AND that are not an abstract class (as we can't create instances of abstract types)...
&& !publicType.IsAbstract
// ...AND that have a public parameterless constructor...
&& publicType.GetConstructor(Type.EmptyTypes) != null
// ...AND are a subtype of TBase. Note that TBase can be a base class or interface
&& typeof(TBase).IsAssignableFrom(publicType))
// Take each collected type and create an instance of those types
.Select(concreteInterfaceType => assembly.CreateInstance(concreteInterfaceType.FullName))
// Since CreateInstance() returns object, cast each created instance to the common subtype TBase
.Cast<TBase>();
return documentProviders;
}
Usage
private void HandleDocuments(string assemblyFilePath)
{
foreach (IClientMethods documentProvider in GetInstancesOf<IClientMethods>(assemblyFilePath))
{
byte[] document = documentProvider.GetDocument(...);
}
}
Create a list of objects from dynamically loaded dll on runtime
Why do you use dynamic
when you know the base type? Don't do that. Use var instead:
Assembly assembly = Assembly.LoadFrom(@"D:\Library\CurrencyData.dll");
List<AYClass> listObjects = new List<AYClass>();
foreach (Type type in assembly.GetExportedTypes())
{
if (type.BaseType.ToString().Contains("AYClass"))
{
var c = (AYClass)Activator.CreateInstance(type);
listObjects.Add(c);
}
}
In general C# is a type safe language, so using dynamic
should be exceptional. Also be sure, that in your dynamic loaded dll and in your "loader" they use the exact same base-class. In any other case casting the instance will fail.
Furthermore you could replace this line: if (type.BaseType.ToString().Contains("AYClass"))
with comparing the type and not its name. This is not very safe, because a class with the same name can be declared in different assemblies and or namespaces in the same assembly.
Related Topics
Is There a Reason Image.Fromfile Throws an Outofmemoryexception for an Invalid Image Format
Adding Your Own HTMLhelper in ASP.NET MVC 3
Automatically Set Appsettings.JSON for Dev and Release Environments in ASP.NET Core
How to Get the Computer Name in .Net
How Does Native Implementation of Valuetype.Gethashcode Work
Is There Any Benefit to This Switch/Pattern Matching Idea
Swap Two Variables Without Using a Temporary Variable
Is Task.Run Considered Bad Practice in an Asp .Net MVC Web Application
C# Webbrowser Control - Form Submit Not Working Using Invokemember("Click")
Css, Images, Js Not Loading in Iis
An Object Reference Is Required to Access Non-Static Member
Scraping Webpage Generated by JavaScript with C#
Differencebetween a Mutable and Immutable String in C#
Why We Have Both Jagged Array and Multidimensional Array
How to Call an Async Method in Main
Select Multiple Items from a Datagrid in an Mvvm Wpf Project