Choose appropriate platform dependent DLL at runtime - c#

I'm currently working on project for the .NET Compact Framework which uses DotNetZip for reading ZIP-files. The project is split into two parts. One platform-independent library which should be available for both, a CF project and a desktop project. This common library contains the code for extracting ZIP-files. The problem is that there are two different DLLs of the DotNetZip library, one for .NET CF and one for the .NET Desktop Framework. It's not possible to use the CF version of the library on a Desktop Framework and vice versa but they share the same interface.
How do I organize my projects in Visual Studio to be able to use the common zip-code and dynamically load the appropriate DLL (which then is used by the zip-code)? It should also be possible to execute the CF application on a PC so is there a way to choose the DLL at runtime?

UPDATE:
A technically unsupported but working solution is to p/Invoke LoadLibrary and specifically load the correct DLL early in the process. All managed and p/Invoke'd libraries are loaded "as needed". The CLR will load them for you when you invoke a method that depends on one of the types in that assembly (note: this is the "unsupported" part; this is not documented and may change in a future CLR version).
The approach works because if an assembly is loaded via LoadLibrary, it's "found" by the CLR loader and the runtime will not attempt to load it again. We used this trick on CF to pre-load assemblies before doing much memory allocation to avoid out-of-memory situations.
So, you should be able to do:
public static void Main()
{
LoadCorrectDLLs();
// .NET will ensure DotNetZip is loaded at this point.
MethodInThisAssembly();
}
public static void MethodInThisAssembly()
{
// Since MethodInThisAssembly uses DotNetZip,
// its assembly will get loaded immediately before this method is called.
IDotNetZipInterface x = null;
...
}
public static void LoadCorrectDLLs()
{
// p/Invoke LoadLibrary to load the correct version of DotNetZip.
}
Note that the following will not work:
public static void Main()
{
LoadCorrectDLLs();
// This line would force DotNetZip to get loaded before Main() is called
IDotNetZipInterface x = null;
}
Old answer; works on desktop framework only:
One trick is to place them in directories that won't be found during DLL loading (e.g., different subdirectories of your executable's directory) and handle AppDomain.AssemblyResove. David Morton has a decent write-up on his blog.

Related

How to get all the referenced assemblies using reflection ? (.net4)

I've made a piece of code that gives me the referenced assemblies of one assembly (in .net5) and it was working greatly, it gave me for exemple if I use the File.WriteAllText method the assembly "System.IO.FileSystem" (this is by using the Assembly.GetReferencedAssemblies method).
But now I need to get this code to work on .NET4 (for Unity Engine). But i've seen that the myAssembly.GetReferencedAssemblies do not give the same output as in .NET5.
It now gives me only: myAssembly.dll and mscorlib.dll
And I cannot make it work to give me like previously all the referenced assemblies (for exemple System or System.IO ...)
Here's a simple example:
using System.IO;
public class Plugin {
static Plugin() {
// Just use the File class to keep the System.IO assembly reference
File.WriteAllText("test", string.Empty);
}
}
public static void Test() {
string myPluginPath = "myAssembly.dll";
// Load the assembly
Assembly assembly = Assembly.LoadFrom(myPluginPath);
// Get all the referenced assemblies
foreach (AssemblyName name in assembly.GetReferencedAssemblies()) {
Console.WriteLine(name.Name);
// Different outputs:
//
// .NET 5
// - System.IO.FileSystem
// - ....
// .NET 4
// - mscorlib
// - Plugin
}
}
Any idea how to make the myAssembly.GetReferencedAssemblies work on .net4 ? Thanks!
Any idea how to make the myAssembly.GetReferencedAssemblies work on .net4?
System.IO.File resides in mscorlib.dll in .NET Framework so both versions work correctly. Why do you need the actual assembly names? You cannot rely on them across platforms.
On the other hand, if you need this to resolve assembly qualified type names, then you can do it by using the legacy assembly identities so Type.GetType("System.IO.File, mscorlib, Version=4.0.0.0") works also in .NET 5.
It's because even on the newer platforms there is an mscorlib.dll, which contains nothing but a bunch of [assembly:TypeForwardedTo(...)] attributes that provide a redirect to the new location.
But it will not work the other way around so do not expect that you can resolve the type File from a System.IO.FileSystem.dll in .NET Framework 4.0
Update after the comment:
In .NET Framework the best way for handling potentially harmful plugins is creating separate AppDomains for them with restricted permissions. Here is an example from my unit tests to create such a sandbox domain and here is an example usage. The AppDomains can even be unloaded along with their referenced assemblies.
The bad news is that this will not work in .NET 5 because starting with .NET Core you cannot create AppDomains anymore (not quite a problem if you already have a solution for .NET 5). But for the sake of completeness: starting with .NET Core 3.0 the AssemblyLoadContext type is the recommended way for handling plugins. Though it does not create a restricted environment the same way as AppDomain, you can use AssemblyDependencyResolver to control the assembly loading requests of the plugins.

When to use GAC?

I'm new to using GAC and I am trying to get a handle on when you should and shouldn't use it. Here is my situation:
I develop software that is primarily add-ins to another commercial product so that product loads my products into it's code. There are several assemblies that I have developed that are used by all of my applications (my license module for example). The .dll file for these libraries get installed to the main application directory so the parent program loads them from there.
The problem is when a user has two of my software titles installed, there can be a conflict since the parent program only loads the first copy of an assembly it finds regardless of version. So if they have Software A version 1.0 with License1.0.dll in it and Software B version 2.5 with License 2.0 in it that has different methods and/or arguments than License1.0.dll it only loads 1.0 and then throws an exception on Software B because it can't find the right license methods.
In initial research it seemed like GAC was intended to be the answer to this and several sites seem to say it is, but then I also found this subject and the link in the answer that seems to be saying no don't use GAC for this.
I'm confused. Can someone give some direct guidance on if I should look into using GAC for this?
I wouldn't recommend use the GAC at all as you depend on registered dlls and I often had problems with that. Anyway, you can load the assembly that you like manually. I have a parent application that is either 64 bit or 32 bit and I have to load the corresponding SQLite x64 or x86 dll.
The referenced assembly has copy local false. That's the first place where the runtime will look to resolve the reference. If there is anything then it checks the GAC. The dll isn't registered there so the AssemblyResolve event will happen where I can say what I want to load:
AppDomain.CurrentDomain.AssemblyResolve += ResoveAssembly;
private static Assembly ResoveAssembly(object sender, ResolveEventArgs e)
{
string fullPath = Assembly.GetExecutingAssembly().Location;
string path = Path.GetDirectoryName(fullPath);
if (e.Name.StartsWith("System.Data.SQLite"))
{
return Assembly.LoadFrom(Path.Combine(path, Environment.Is64BitProcess
? "x64\\System.Data.SQLite.DLL"
: "x86\\System.Data.SQLite.DLL"));}
return null;
}
}
In case someone wonders why I'm doing that: As far as I know the new SQLite NuGet package handles this issue now. But it wasn't available when we want to use it.

Lazy load a DLL that was placed on the filesystem after the program has been started

I am building an automation harness using C# and am trying to do the following:
Bootstrap the harness
Install the executable
Use a couple of DLLs that the executable lays down to establish a connection to the infrastructure that the exe connects to (large, complex software system)
The DLLs that I need to use are a few static classes that perform their own 'bootstrap' process to get connected to the rest of the servers in the test environment. What I have tried is creating a connection class that looks like this:
using CompanyName.Framework;
namespace TestNamespace{
public class ProductConnectorClass{
// Initialize a connection to the product and do stuff
public ProductConnectorClass(){
var connection = CompanyName.Framework.Initialize(...);
// Do stuff with the connection
connection.RunStuff();
}
}
}
The DLL that contains the CompanyName.Framework namespace is not accessible when the test framework is first started. It is copied via code from another class that looks lomething like this:
namespace TestNamespace{
public class TestRunnerClass{
public TestRunnerClass(){
// pseudo code here, so you get the idea:
CopyMsiToHost(msiRemotePath, msiLocalPath);
InstallMsi(msiLocalPath);
CopyDllsToTestHarnessDir();
ProductConnectorClass pcc = new ProductConnectorClass();
}
}
}
It is when the code hits the ProductConnectorClass pcc = new ProductConnectorClass(); line that I get an exception:
FileNotFoundException was unhandled
Could not load file or assembly 'CompanyName.Framework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=XXXXXXXXXXXXXXXX' or one of its dependencies. The system cannot find the file specified.
This puzzles me since I have set Specific Version: False on the DLL in my test project and I am operating under the assumption that .NET Lazy-loads DLLs (in my mind that means search for and load them at the time they are needed). At this point I am looking for a simple way to get a running .NET program to find a needed DLL that has been placed after the process started running.
Note 1: If I restart the process after the DLL is already in place, it loads fine. It looks like it is only scanning for DLLs on startup, which still puzzles me.
Note 2: This SO answer gives me some information about how DLLs are loaded, but doesn't fully answer my question. The synopsis is that if .NET has tried to load the DLL and it fails that it won't try to load it again. In my case I don't expect that a load attempt has been made on the DLL since the class where the DLL is referenced has not been instantiated yet.
From experiments in test projects that I have performed, it appears that I need to perform modularization of my code in order to get this to work. In no case am I able to reference a dll that does not currently exist, start my program then drop down the DLL.
Here are my steps to a solution:
Refactor my common classes and interfaces to their own DLL project in my Automation solution
Create another DLL project ('TestLogic') in my solution that uses the DLL I lay down after the program has started
When I need to use the DLL that is laid down after the program starts, I use reflection to load the TestLogic dll and perform the tests
For reference I did a google search for 'C# late load dll' and found this msdn social post with some helpful advice. Here is a copy/paste/modify of the most helpful reply from that thread:
Move all the code that references the DLLs that are installed after the test framework starts to a completely separate assembly. We'll call this "TestLogicAssembly".
Create a new assembly and define an interface. We'll call this "TestInterfaceAssembly".
Reference TestInterfaceAssembly in both your main DLL and your TestLogicAssembly.
Create a class in TestLogicAssembly that implements the interface created in TestInterfaceAssembly.
Don't reference TestLogicAssembly from your main application.
At runtime, check to see if the DLLs that are laid down as part of the install step of the test framework are actually installed. If they are, use Assembly.Load to load the TestLogicAssembly, then find the type that implements the interface defined in number 2. Use Activator.CreateInstance to create an instance of this type, and cast it to the interface you created.

Need to access static property of class without adding a DLL dependency

I have a class inside a DLL (Database.dll) which has a static property:
public class Database
{
public static int[] ReleasedDatabaseVersions
{
get { return new int[] { 5, 6, 7, 8 }; }
}
}
I have created a standalone executable (ValidateInstall.exe) which needs to access ReleasedDatabaseVersions in the Database class.
However, I want ValidateInstall.exe to be entirely independent of Database.dll (i.e. I want to be able to run it on a PC without this DLL installed).
In C++ inlining would make this very easy. Is this possible in C#?
I don't want to repeat this data in my executable as this would be a maintenance headache.
You could use ILMerge post-build to embed the assembly holding the Database class into your exe:
ILMerge is a utility that can be used to merge multiple .NET
assemblies into a single assembly.
The ILMerge page also links to an article by Jeffrey Richter, detailing how to achieve something similar without using ILMerge.
The only cases I know of where items from some project B are inlined into some other project A are consts and enums. It seems to me that the .NET-ish way of solving your problem is to factor ReleasedDatabaseVersions out into a third project. This third project would then be referenced by both ValidateInstall.exe and Database.dll
You can add the source code for the Database class as a link to your standalone project so both projects share a single source code file.
When using "Add Existing Item..." choose "Add as Link" rather than the standard Add.
Adding the reference and including the references to the built executable would be a way to ensure that the program runs on systems which don't have the DLL installed by including it in the build.
I can't really think of any way to distribute this application without actually duplicating the DLL or the Static content
Take a look at using DllImport attribute:
http://msdn.microsoft.com/en-us/library/aa984739(v=VS.71).aspx
UPDATED
If you don't want to use this method of late binding, there is always the Assembly class.
Assembly a = Assembly.LoadFrom("Database.dll"); // This will throw an error that you can catch if the file does not exist.
Database d = a.CreateInstance("Database") as Database;
Now, I am not sure if the reflection will bring in all of the functions or not. I have not used this method before, so I couldn't give you a correct solution.

How can a C++ windows dll be merged into a C# application exe?

I have a Windows C# program that uses a C++ dll for data i/o. My goal is to deploy the application as a single EXE.
What are the steps to create such an executable?
Single Assembly Deployment of Managed and Unmanaged Code
Sunday, February 4, 2007
.NET developers love XCOPY deployment. And they love single assembly components. At least I always feel kinda uneasy, if I have to use some component and need remember a list of files to also include with the main assembly of that component. So when I recently had to develop a managed code component and had to augment it with some unmanaged code from a C DLL (thx to Marcus Heege for helping me with this!), I thought about how to make it easier to deploy the two DLLs. If this were just two assemblies I could have used ILmerge to pack them up in just one file. But this doesn´t work for mixed code components with managed as well as unmanaged DLLs.
So here´s what I came up with for a solution:
I include whatever DLLs I want to deploy with my component´s main assembly as embedded resources.
Then I set up a class constructor to extract those DLLs like below. The class ctor is called just once within each AppDomain so it´s a neglible overhead, I think.
namespace MyLib
{
public class MyClass
{
static MyClass()
{
ResourceExtractor.ExtractResourceToFile("MyLib.ManagedService.dll", "managedservice.dll");
ResourceExtractor.ExtractResourceToFile("MyLib.UnmanagedService.dll", "unmanagedservice.dll");
}
...
In this example I included two DLLs as resources, one being an unmanaged code DLL, and one being a managed code DLL (just for demonstration purposes), to show, how this technique works for both kinds of code.
The code to extract the DLLs into files of their own is simple:
public static class ResourceExtractor
{
public static void ExtractResourceToFile(string resourceName, string filename)
{
if (!System.IO.File.Exists(filename))
using (System.IO.Stream s = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
using (System.IO.FileStream fs = new System.IO.FileStream(filename, System.IO.FileMode.Create))
{
byte[] b = new byte[s.Length];
s.Read(b, 0, b.Length);
fs.Write(b, 0, b.Length);
}
}
}
Working with a managed code assembly like this is the same as usual - almost. You reference it (here: ManagedService.dll) in your component´s main project (here: MyLib), but set the Copy Local property to false. Additionally you link in the assembly as an Existing Item and set the Build Action to Embedded Resource.
For the unmanaged code (here: UnmanagedService.dll) you just link in the DLL as an Existing Item and set the Build Action to Embedded Resource. To access its functions use the DllImport attribute as usual, e.g.
[DllImport("unmanagedservice.dll")] public extern static int Add(int a, int b);
That´s it! As soon as you create the first instance of the class with the static ctor the embedded DLLs get extracted into files of their own and are ready to use as if you deployed them as separate files. As long as you have write permissions for the execution directory this should work fine for you. At least for prototypical code I think this way of single assembly deployment is quite convenient.
Enjoy!
http://weblogs.asp.net/ralfw/archive/2007/02/04/single-assembly-deployment-of-managed-and-unmanaged-code.aspx
Try boxedapp; it allows to load all DLLs from memory. Also, it seems that you can even embed .net runtime. Good to create a really standalone applications...
Use Fody.Costura nuget
Open your solution -> Project -> Manage Nuget Packages
Search for Fody.Costura
Compile your project.
That's it !
Source:
http://www.manuelmeyer.net/2016/01/net-power-tip-10-merging-assemblies/
Have you tried ILMerge? http://research.microsoft.com/~mbarnett/ILMerge.aspx
ILMerge is a utility that can be used to merge multiple .NET assemblies into a single assembly. It is freely available for use from the Tools & Utilities page at the Microsoft .NET Framework Developer Center.
If you're building the C++ DLL with the /clr flag (all or partially C++/CLI), then it should work:
ilmerge /out:Composite.exe MyMainApp.exe Utility.dll
It will not work with an ordinary (native) Windows DLL however.
Just right-click your project in Visual Studio, choose Project Properties -> Resources -> Add Resource -> Add Existing File…
And include the code below to your App.xaml.cs or equivalent.
public App()
{
AppDomain.CurrentDomain.AssemblyResolve +=new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}
System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
string dllName = args.Name.Contains(',') ? args.Name.Substring(0, args.Name.IndexOf(',')) : args.Name.Replace(".dll","");
dllName = dllName.Replace(".", "_");
if (dllName.EndsWith("_resources")) return null;
System.Resources.ResourceManager rm = new System.Resources.ResourceManager(GetType().Namespace + ".Properties.Resources", System.Reflection.Assembly.GetExecutingAssembly());
byte[] bytes = (byte[])rm.GetObject(dllName);
return System.Reflection.Assembly.Load(bytes);
}
Here's my original blog post:
http://codeblog.larsholm.net/2011/06/embed-dlls-easily-in-a-net-assembly/
Thinstall is one solution. For a native windows application I would suggest embedding the DLL as a binary resource object, then extracting it at runtime before you need it.
Smart Assembly can do this and more. If your dll has unmanaged code, it wont let you merge the dlls to a single assembly, instead it can embed the required dependencies as resources to your main exe. Its flip-side, its not free.
You can do this manually by embedding dll to your resources and then relying on AppDomain's Assembly ResolveHandler. When it comes to mixed mode dlls, I found many of the variants and flavours of ResolveHandler approach to not work for me (all which read dll bytes to memory and read from it). They all worked for managed dlls. Here is what worked for me:
static void Main()
{
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
string assemblyName = new AssemblyName(args.Name).Name;
if (assemblyName.EndsWith(".resources"))
return null;
string dllName = assemblyName + ".dll";
string dllFullPath = Path.Combine(GetMyApplicationSpecificPath(), dllName);
using (Stream s = Assembly.GetEntryAssembly().GetManifestResourceStream(typeof(Program).Namespace + ".Resources." + dllName))
{
byte[] data = new byte[stream.Length];
s.Read(data, 0, data.Length);
//or just byte[] data = new BinaryReader(s).ReadBytes((int)s.Length);
File.WriteAllBytes(dllFullPath, data);
}
return Assembly.LoadFrom(dllFullPath);
};
}
The key here is to write the bytes to a file and load from its location. To avoid chicken and egg problem, you have to ensure you declare the handler before accessing assembly and that you do not access the assembly members (or instantiate anything that has to deal with the assembly) inside the loading (assembly resolving) part. Also take care to ensure GetMyApplicationSpecificPath() is not any temp directory since temp files could be attempted to get erased by other programs or by yourself (not that it will get deleted while your program is accessing the dll, but at least its a nuisance. AppData is good location). Also note that you have to write the bytes each time, you cant load from location just 'cos the dll already resides there.
If the assembly is fully unmanaged, you can see this link or this as to how to load such dlls.
If you want to pack an application that already exists (including its dlls and other resources, no matter what language it's coded in) into a single .exe you can use SerGreen's Appacker for that purpose. But it'll be "detected" to "run malicious code from a hacker" because it unpacks itself:
Appacker and packages created by it can be detected as malware by some antivirus software. That's because of a hacky way i used to package files: packed app reads its own executable and extracts other files from it, which antiviruses find hella suspicious. It's false positive, but it still gets in the way of using this app.
-SerGreen on GitHub
To use it you can simply open it up, click away any virus warnings (and tell Windows Defender to not delete it!), then choose a directory that should be packed and the executable to be run after unpacking.
You can optionally change the unpacking behaviour of the app (windowed/windowless unpacker, unpacking target directory, should it be repacked or changes to the unpacked files be ignored, ...)
PostBuild from Xenocode can package up both managed and unmanged into a single exe.

Categories

Resources