I have a C# application which interfaces with some hardware (USB device) as follows:
C# application -> intermediate DLL -> hardware DLL -> hardware. The intermediate DLL and hardware DLL are supplied with the USB device so I have no control over these.
The intermediate DLL is the only which which I need to include in the VS project as this is what I call. The hardware DLL is then in the same directory so must be found automatically.
A new version of hardware device is now released with a different hardware DLL. The old DLL is not compatible with the new hardware and the new DLL is not compatible with the old hardware.
How can I make my application work with both pieces of hardware? I guess that I need to load and unload each DLL as required?
Here's what I do for a similar problem. I have a chunk of code that I want to work with, but I have to load the dll at runtime. So I refer to it in my project, but I don't put it in the same directory as the rest of my assemblies. Instead, in the consuming code, I have some code that looks like this:
// constructor called from a static constructor elsewhere
MyDllLoader(string hardwareFolder) {
_hardwareFolder = hardwareFolder;
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
SeeIfAlreadyLoaded();
}
private void SeeIfAlreadyLoaded() {
// if the assembly is still in the current app domain then the AssemblyResolve event will
// never fire.
// Since we need to know where the assembly is, we have to look for it
// here.
Assembly[] assems = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly am in assems)
{
// if it matches, just mark the local _loaded as true and get as much
// other information as you need
}
}
System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) {
string name = args.Name;
if (name.StartsWith("Intermediate.dll,"))
{
string candidatePath = Path.Combine(_hardwareFolder, "Intermediate.dll");
try {
Assembly assem = Assembly.LoadFrom(candidatePath);
if (assem != null) {
_location = candidateFolder;
_fullPath = candidatePath;
_loaded = true;
return assem;
}
}
catch (Exception err) {
sb.Append(err.Message);
}
}
return null;
}
There's another solution too - it's complicated, but I've done it and done the work for you. You declare an abstract class, say MyHardwareAbstraction, that has the signatures of the methods that you want and you code against that interface. Then you write some code which given a path to an assembly, loads it and dynamically defines a new class that matches MyHardwareAbstraction and makes it map onto an instance of the actual object that you want. I wrote a blog several years ago on how to do this.
The nice thing about doing this is that you use the abstract type in your code and work against that and then the adapter compiler will, at run time, compile a new class that will complete that abstract type using some other type as the target type. It's fairly efficient too.
I you want both dll to coexist in the program, you'll have to use AppDomains, as explained here.
Else, you can simply use LoadLibrary after the user has made a clear choice about what version he needs ?
Edit:
If the intermediate DLL is a .Net Assembly, you can use the method mentioned here to specify where to look for your intermediate DLL before you call any method that uses the intermediate DLL, without having to change your existing code.
then you must not directly reference the DLL in your C# project, because .Net Assemblies are discovered and loaded before your Main method is even called. Instead, you must dynamically load the intermediate DLL using AppDomain or other methods, then use the library via reflection, or by using dynamic objects.
Apparently, this would make programming very cumbersome. However, there is an alternative method. You can write a launcher program, that loads your original application (you can load .exe files as libraries), and invokes the Main method of your original program reflectively. To make sure that the correct intermediate DLL is loaded, you can use the method mentioned here, while your launcher program is loading your original application.
The following discussion still applies to the hardware DLL.
The following is valid if:
You need only one version of the dll at a time (during the entire period your application runs), and
The two versions of the intermediate DLLs have exactly the same API.
According to MSDN, the DLL Search Path includes directories specified under the PATH environment variable. (http://msdn.microsoft.com/en-us/library/7d83bc18%28v=vs.80%29.aspx). Hence, you may put the two versions of the intermediate DLLs under seperate sub-directories under your application directory, but with exactly the same name under each directory, for example:
bin\
hardware-intermediate-v1\
intermediate.dll
hardware-intermediate-v2\
intermediate.dll
Then, at start up, after your application has determined which version to use, you may add one of the above directories to your PATH environment variable,
using System;
using System.Reflection;
using System.IO;
...
Environment.SetEnvironmentVariable(
"PATH",
Environment.GetEnvironmentVariable("PATH") + ";" +
Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) +
"\\hardware-intermediate-v1"
);
Then, calls to P-Invoke methods (DLLImport) will result in the corresponding version of the DLL to be loaded. To immediately load all DLLs, you may refer to DllImport, how to check if the DLL is loaded?.
However, if you wish to use the two version of the DLLs together without restarting your application, or if there are any API difference between the two DLLs on the level of method name and/or parameter count/type, you must create two seperate sets of P-Invoke methods, each binding to its corresponding version of the intermediate DLL.
Related
I want to replace dll "a" to their new version while the application is running, but when I trying to make it, I getting error: IOException that this file already using by process.
How can I replace it without getting this error?
Even if you were able to do what you're describing, and you're not, but even if you were, the code that's already executed, along with a large portion of the code physically around it has already been loaded in memory by the pre-loader and JIT. A large part of that was probably also already JITed. So you replacing the file would only affect new page faults, leading to internal corruption when they resolve against your new library.
Instead .Net provides AppDomain instances to let you load and unload libraries into for a moderate performance cost when using them, allowing you to unload your libraries when you want to do your update magic.
However I cannot stress enough how unviable your solution is. The standard update process is, and always has been:
download your new files in a separate folder
start a second, smaller, preferably C++ application (less prerequisites) that:
waits for the main application to close (you can use a named mutex)
copies the files from the temp folder over to the main folder
handles any errors, and deletes the temp folder if everything went alright
starts the main application again
as soon as the second application is started, exit out of the main application
One of possible solutions.
Note: in this solution constructor must be parameterless. Logic with input parameters must be separated to Initialize() method.
Separate all models/interfaces/enums etc. from
project "a" to another project (let's call it "Models").
Make reference from Models to both project a and your executable project.
Remove reference from a to your executable project (but make sure that your a.dll will be placed in same folder as executable).
In your executable project add next Helper:
using System;
using System.Linq;
using System.Reflection;
...
public static class DynamicLoader
{
public const string MyLibName = "a.dll";
public static T Create<T>(this T type)
{
Assembly asm = Assembly.LoadFrom(MyLibName);
Type assemblyType;
assemblyType = typeof(T).IsInterface ? asm.GetTypes().First(p => typeof(T).IsAssignableFrom(p)) : typeof(T);
return (T)asm.CreateInstance(assemblyType.FullName);
}
}
...
//Usage example:
Models.SomeType instance = typeof(Models.SomeType).Create();
instance.Initialize(15);
With this you can replace your "a.dll" and next call of Create() returns instance from new file.
I am working on a project. The task is to create a DLL Project. In that project, I am having an existing DLL with set of methods. With the use of existing DLL I can call some methods and create some methods in the new DLL.
Is that Possible in C#? What are the possibilities and methods to create such a project?
If you want to hide that DLL in the contents of your own DLL, you can simply put it into resources. From the standpoint of resources, a DLL is just a file like any other and you can simply add it to program resources and simply drop the file where you need it.
However this will prohibit you from using implicit linking and you will have to link the DLL explicitly. MSDN already offers a quite reasonable tutorial already and here.
using System;
using System.Reflection;
public class Asmload0
{
public static void Main()
{
// Use the file name to load the assembly into the current
// application domain.
Assembly a = Assembly.Load("example");
// Get the type to use.
Type myType = a.GetType("Example");
// Get the method to call.
MethodInfo myMethod = myType.GetMethod("MethodA");
// Create an instance.
object obj = Activator.CreateInstance(myType);
// Execute the method.
myMethod.Invoke(obj, null);
}
}
If you want to create your own DLL that just uses the old one you may just Add Reference. Then you can set up "Use Copy Local", but you will have to distribute two files:
And if you want to make "static link" simply by compiler/linker (build in to visual studio) you need to use a statically linked library (LIB) and not a dynamically linkws library (DLL)...
Or you can try reading "How to link a .DLL statically?" which seems to provide some guidance (proprietary software) on how to do this.
I am working on a game engine + editor in C#. The idea is that the user writes all his game code in a project that the editor will then load, so it can list the user-defined classes, as well as instantiate them in a scene editor.
Messing around with the concept of (re)loading a DLL, I can load the DLL, instantiate and invoke methods on a class defined in an external assembly, rebuild the external assembly, and load it again in the host, without restarting the host. And this work (atleast it looks like it), however it is not freeing up memory from the previously loaded assembly.
Loader class:
public class Loader
{
// This DLL contains an implementation of ILoadable - ILoadable is declared in a shared project.
private const string AsmPath = #"D:\Dev\AsmLoading\AsmLoadingImpl\bin\Debug\AsmLoadingImpl.dll";
public ILoadable GetExternalLoadable()
{
var asmBytes = File.ReadAllBytes(AsmPath);
Assembly asm = Assembly.Load(asmBytes, null);
var loadableInterface = typeof(ILoadable);
var loadableImpl = asm.GetTypes().First(loadableInterface.IsAssignableFrom);
return (ILoadable)Activator.CreateInstance(loadableImpl);
}
}
Running the GetExternalLoadable() 2 or 3 times, the Task Manager reveals a ~1mb increase in RAM usage in my host program, repeating the same action will increase it further, without it ever decreasing.
Is there any way to work around this? I know Unity is doing similar things, except they actually compile the external assembly themselves, but the Unity editor does not consume additional memory when I trigger the recompilation a few times.
So, what I am trying to accomplish is "live reloading" of external assemblies.
.NET 4 added support for collectible assemblies, that can be unloaded by the GC.
However, these are heavily restricted:
They are intended for dynamic code generation, you cannot just load a .dll file
They cannot define COM interop types, P/Invoke methods, thread-static fields, ...
If you have a .dll that follows these restrictions, you might be able to load and re-emit the IL code so that it looks like a dynamically generated assembly to .NET.
Apart from dynamic methods and collectible assemblies, it is impossible to unload code without unloading the whole AppDomain.
I'm developing a solution where a service runs continuously in background, and plugin DLLs can be added/deleted at runtime. The service would load the necessary plugins when needed, run them and unload them. This is the unloading part is what currently gives me trouble: once certain class is successfully loaded first time (variable tc), it never is reloaded, even though the DLL file is updated. I think I am not unloading the class/assembly/appdomain properly, so I'd appreciate some advice for walking this last mile.
Edit: I updated the post to reflect the recent changes in code and explain when exactly the unloading has no effect: the problem does not appear on Linux Ubuntu (via Mono), but it is present on Windows 2008 Server, when I am trying to replace a certain plugin DLL with a newer file version. It seems that .NET framework has cached somewhere the assembly and is happy without reloading it. The DLL file name does not change, but the File Version property is different, so I'd expect the runtime to compare the previously loaded DLL version with the one being loaded and in case of different version numbers use the newer version. If I change the code slightly to load the assembly from a DLL file with different name, the reloading happens as expected.
using System;
using System.Reflection;
namespace TestMonoConsole
{
public interface ITestClass
{
void Talk();
}
class MainClass
{
public static void Main (string[] args)
{
string pluginPath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
string classAssembly = "TestClass";
string className = "TestMonoConsole.TestClass";
string command = "";
do
{
try
{
System.AppDomain domain = System.AppDomain.CreateDomain(classAssembly);
string pluginAssemblyFile = pluginPath + "/" + classAssembly + ".dll";
System.IO.StreamReader reader = new System.IO.StreamReader(pluginAssemblyFile, System.Text.Encoding.GetEncoding(1252), false);
byte[] b = new byte[reader.BaseStream.Length];
reader.BaseStream.Read(b, 0, System.Convert.ToInt32(reader.BaseStream.Length));
domain.Load(b);
reader.Close();
ITestClass tc = (ITestClass) Activator.CreateInstance(domain, classAssembly, className).Unwrap();
tc.Talk();
System.AppDomain.Unload(domain);
}
catch (System.IO.FileNotFoundException e)
{
Console.WriteLine (String.Format("Error loading plugin: assembly {0} not found", classAssembly));
}
command = Console.ReadLine();
} while (command == "");
}
}
}
You are creating the types directly into the host domain. You need to specify the domain when using Activator.CreateInstance per http://msdn.microsoft.com/en-us/library/ms224132(v=vs.90).aspx
You are instantiating the plugin types within your own AppDomain. After implementing plugin frameworks several times, I highly suggest looking into using MEF(managed extensibility framework) as a solution since it handles these common problems of code isolation. If you don't want MEF, one way to do this is by implementing a "Remote Control" class which would act as the communicator between app domains. You could call a method on your remote class that would instantiate and run the code within the secondary app domain.
MEF Documentation
Also, instead of re-inventing this mechanism, you can utilize the Managed Add-In Framework (MAF, System.AddIn) to do this for you. Look here for a quickstart.
Types and assemblies cannot be unloaded from AppDomain.
So you need to create new AppDomain, load types, do all the stuff, and then unload that domain.
According to this MSDN article, the CLR does not use shadow copying by default. You could try to enable it by following these steps:
Create an AppDomainSetup.
In the AppDomainSetup instance set ShadowCopyFiles to true.
In the AppDomainSetup instance set ShadowCopyFilesDirectories to the directory path(s) that contain the assemblies you want to be able to ovewrite at run time. This could simply be the directory where the plug-in assembly is located.
In the AppDomainSetup instance set everything else according to your needs.
Use one of the overloads of AppDomain.CreateAppDomain that take an AppDomainSetup as an argument.
Try to replace the plug-in assembly.
The fact that it works on Mono and not on CLR (Windows) could mean that the CLI does not specify if shadow copying should be enabled by default.
I have an IronPython 2.6/2.7 script I am writing which imports a lot of assemblies.
In other words, at the top of the script it does this...
clr.AddReference( "System.Xml" )
import System.Xml
Except it doesn't do this for 1 assembly, but for 10 assemblies.
Some of the modules are built-in .NET assembllies and some are assemblies I have made.
I'd like to simplify my script so that it loads one assembly that I will build. I want to then call a method in that assembly that will do the "AddReference" and "import" for the 10 assemblies. The primary goal of all this is to minimize the length/complexity of the script.
So in the end I would see it working like this
clr.AddReferenceToFileAndPath( "d:\\myassembly" )
import MyAssembly
MyAssembly.ImportAllAssembliesIReallyWant()
My core problemis despite reading all the information I could find on ScriptRuntime, ScriptEngine, scopes, etc. - I still can't figure out how to write a method in "MyAssembly" that affects what modules are loaded in the calling script.
One way to go about this would be to create a built-in module which does this. You can do this with:
[assembly: PythonModule("mymodule", typeof(MyModuleType)]
public static class MyModuleType {
[SpecialName]
public static void PerformModuleReload(PythonContext context, PythonDictionary dict) {
context.DomainManager.LoadAssembly(typeof(TypeInAssemblyToLoad));
}
}
Just add appropriate LoadAssembly calls for all of the assemblies you care about. The assembly could also populate members in dict that you want available.
Another (and possibly simpler) way would be to simply have a .py file which does all of the clr.AddReference calls you need and have every module import that one file. The import mechanism will do the appropriate caching so it will only load once but will ensure all of the assemblies are available for each module which needs them.
I think that the only way to do this is going to be to access the ScriptEngine from within your ImportAllAssemblies() method and execute the commands that would normally be executed. You should be able to dynamically generate the statements based on what assemblies you want to load and that are referenced etc.
Hope that helps point you in the right direction.