Embedding SQLite.Interop.dll in C# - c#

I have an application that has all the DLL files it needs embedded into it so that it is a standalone exe. Here is how I am loading everything now:
public static Assembly ExecutingAssembly = Assembly.GetExecutingAssembly();
public static string[] EmbeddedLibraries = ExecutingAssembly.GetManifestResourceNames().Where(x => x.EndsWith(".dll")).ToArray();
public static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) {
// Get assembly name
var assemblyName = new AssemblyName(args.Name).Name + ".dll";
// Get resource name
var resourceName = EmbeddedLibraries.FirstOrDefault(x => x.EndsWith(assemblyName));
if (resourceName == null) {
return null;
}
// Load assembly from resource
using (var stream = ExecutingAssembly.GetManifestResourceStream(resourceName)) {
var bytes = new byte[stream.Length];
stream.Read(bytes, 0, bytes.Length);
return Assembly.Load(bytes);
}
}
public static void Main(string[] arg) {
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
RealMain();
}
So the first interesting thing I noticed is that it only loads the DLL files that are used in the first page. It isn't enough to put using System.Data.SQLite; at the top to get it to include System.Data.SQLite.dll, you also have to do something that touches that namespace right away.
The next issue involves SQLite.Interop.dll which it won't load into the assembly. When you try you get the following error:
Exception thrown: 'System.BadImageFormatException' in mscorlib.dll
An unhandled exception of type 'System.BadImageFormatException' occurred in mscorlib.dll
The module was expected to contain an assembly manifest. (Exception from HRESULT: 0x80131018)
Now I could unpack the DLL and copy it to the c:\Windows\System32 directory, but since everything else is self contained, it would be nice if this was as well. I noticed this SDK that claims to be able to create it as a virtual file:
https://www.boxedapp.com/boxedappsdk/usecases/embed_system.data.sqlite.dll_dependencies.html
Is there a way to do this without the extra SDK? Or as an alternative, is there another way of creating and using SQLite databases without that DLL using a different package? I tried 3-4 different packages yesterday and didn't get anywhere.

SOLUTION
Ok, so this is disappointing that it is so easy, and yet nowhere else did I see the solution in dozens of other SO questions. On the SQLite.org website, there are a couple different downloads to choose from. When you use the NuGet package manager, you get the wrong version if what you want to do is embed everything.
To do it right, go to: https://system.data.sqlite.org/index.html/doc/trunk/www/downloads.wiki
On that page you will want to download the BUNDLE version. In my case that was sqlite-netFx46-static-binary-bundle-Win32-2015-1.0.112.0.zip
Manually add that as a resource, include in your application as an Embedded Resource (Those are 2 separate steps), set it to not copy to output directory and use the code from the question to have it added.

Related

Runtime error with a very basic reflection example in C#

I'm trying to learn a bit more about System.Reflection using the official microsoft docs. Specifically I'm trying to run the following example:
// Loads an assembly using its file name.
Assembly a = Assembly.LoadFrom("MyExe.exe");
// Gets the type names from the assembly.
Type[] types2 = a.GetTypes();
foreach (Type t in types2)
{
Console.WriteLine(t.FullName);
}
So I made a new console app using dotnet new console -o=customconsole. I then removed ImplicitUsings from my project file (because I don't like that), and came up with the following code:
using System;
using System.Reflection;
namespace get_type_from_assembly
{
internal class Program
{
static void Main(string[] args)
{
// load assembly using full file name
Assembly a = Assembly.LoadFrom("C:\\Users\\bobmarley\\desktop\\temp\\csharp-reflection\\get-type-from-assembly\\bin\\Debug\\net6.0\\console-custom.exe");
// get type names from assembly
Type[] types2 = a.GetTypes();
foreach (Type t in types2)
{
Console.WriteLine(t.FullName);
}
}
}
}
Then I tried to run the generated executable using dotnet run --project=customconsole. I got the following runtime error:
Unhandled exception. System.BadImageFormatException: Bad IL format. The format of the file 'C:\Users\bobmarley\desktop\temp\csharp-reflection\get-type-from-assembly\bin\Debug\net6.0\console-custom.exe' is invalid.
at System.Runtime.Loader.AssemblyLoadContext.LoadFromPath(IntPtr ptrNativeAssemblyLoadContext, String ilPath, String niPath, ObjectHandleOnStack retAssembly)
at System.Runtime.Loader.AssemblyLoadContext.LoadFromAssemblyPath(String assemblyPath)
at System.Reflection.Assembly.LoadFrom(String assemblyFile)
at get_type_from_assembly.Program.Main(String[] args) in C:\Users\bobmarley\desktop\temp\csharp-reflection\get-type-from-assembly\Program.cs:line 11
make: *** [Makefile:5: run] Error 1
I'm not sure why this occurs, because I checked and the executable does exist in the specified path. What is happening here and how can I fix it?
A likely reason is that the your project and the loaded assembly target different platforms, i.e. x86 vs x64, In my experience that is a common reason for BadImageFormatException. Another possible reasons is that one targets .net core while the other targets .net framework.
Dynamically loading an assembly will require that it is compatible with your project. If you want to read arbitrary assemblies you probably want some tool that can extract whatever information you are after from the CIL code directly, without actually loading it.

AppDomain exception is thrown

I've found a lot of similar questions but couldn't find any solution.
I have following code:
string file = "c:\\abc.dll";
AppDomainSetup ads = new AppDomainSetup();
ads.PrivateBinPath = Path.GetDirectoryName(file);
AppDomain ad2 = AppDomain.CreateDomain("AD2", null, ads);
ProxyDomain proxy = (ProxyDomain)ad2.CreateInstanceAndUnwrap(typeof(ProxyDomain).Assembly.FullName, typeof(ProxyDomain).FullName);
Assembly asm = proxy.GetAssembly(file); // exception File.IO assembly not found is thrown here after succesfully running the funktion GetAssembly.
public class ProxyDomain : MarshalByRefObject
{
public Assembly GetAssembly(string assemblyPath)
{
try
{
Assembly asm = Assembly.LoadFile(assemblyPath);
//...asm is initialized correctly, I can see everything with debugger
return asm;
}
catch
{
return null;
}
}
}
The most Interesting thing that then my GetAssembly funktion returns some other type, even my custom serializable class, everything is fine. Does someone know what I'm missing? Or It's just impossible to return loaded assembly to another domain?
Thank you
I imagine File.IO is sitting in your main application's bin directory? If so, your abc.dll will not know where to find it (unless your main application is also sitting in C:\\).
You need to do one of
Bind to the AppDomain.AssemblyResolve event and manually load the referenced dll
Change the AppDomainSetup's base directory (which is one of the places .NET knows to look for dlls)
Install File.IO to the GAC (which is another one of the places .NET knows to look for dlls)
Add the location of File.IO to your AppDomainSetup's private probing path (which is another one of the places .NET will try to look for dlls).

Assembly loaded using Assembly.LoadFrom() on remote machine causes SecurityException

I am loading an assembly using Assembly.LoadFrom(fileName). When fileName is on the local machine, everything works fine. When, however, the identical file (and dependencies) are on a remote network share, I get a System.Security.SecurityException the moment I try to create a new SqlConnection from the remote assembly:
System.Security.SecurityException: Request for the permission of type
'System.Data.SqlClient.SqlClientPermission, System.Data,
Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'
failed.
What's the cure?
You could load the assembly as bytes and load it with Assembly.Load(bytes), maybe this works.
Or you give the application the requested permission.
Edit:
I made a little test and it worked for me. Here is some code:
static Dictionary<Assembly, String> _Paths = new Dictionary<Assembly, String>();
static void Main(string[] args)
{
AppDomain current = AppDomain.CurrentDomain;
current.AssemblyResolve += new ResolveEventHandler(HandleAssemblyResolve);
// This line loads a assembly and retrieves all types of it. Only when
// calling "GetTypes" the 'AssemblyResolve'-event occurs and loads the dependency
Type[] types = LoadAssembly("Assemblies\\MyDLL.dll").GetTypes();
// The next line is used to test permissions, i tested the IO-Permissions
// and the Reflection permissions ( which should be denied when using remote assemblies )
// Also this test includes the creation of a Form
Object instance = Activator.CreateInstance(types[0]);
}
private static Assembly LoadAssembly(string file)
{
// Load the assembly
Assembly result = Assembly.Load(File.ReadAllBytes(file));
// Add the path of the assembly to the dictionary
_Paths.Add(result, Path.GetDirectoryName(file));
return result;
}
static Assembly HandleAssemblyResolve(object sender, ResolveEventArgs args)
{
// Extract file name from the full-quallified name
String name = args.Name;
name = name.Substring(0, name.IndexOf(','));
// Load the assembly
return LoadAssembly(Path.Combine(_Paths[args.RequestingAssembly], name + ".dll"));
}
Something important:
There may be files which do not have a matching name and file name, but you can resolve this by checking all files in folder with AssemblyName.GetAssemblyName(file).
The assembly is being loaded with LocalIntranet permission set. This restricts certain APIs. See:
http://msdn.microsoft.com/en-us/library/03kwzyfc.aspx and http://msdn.microsoft.com/en-us/library/0x4t63kb%28v=vs.80%29.aspx
Well, I found a workaround. I could not find any way to circumvent the SecurityException problem - so instead of loading the assembly from the remote folder, I simply copied the remote folder contents at runtime to the local computer and ran from there. Simple, neat and works perfectly. Plus, it's arguably a better way of working, so that the clients are running off their local copy instead of putting load on the server, and it makes it much easier to deploy an updated version of the original, without any file locking.
Caveat to anyone who tries to follow in my footsteps: don't try and copy to a subfolder of your app directory; for some reason this causes errors with dependencies. Rather copy to 'Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)` or some other folder to which you know you have write access.

Load an assembly from an embedded resource

I would like to load a dll file (Test.dll) as an assembly. I am able to do this using both direct Visual Studio references (ie. loading the dll as a reference to my C# application) as well as loading the dll using the Assembly.LoadFile(filename) method. Now, I would like to add my dll file as an embedded resource to my Visual Studio application, and load the dll file as an assembly. I know how to load this resource as a byte array, is there some correlation between the byte array and the assembly that I could use? Furthermore, I need to be able to call a method located within the dll file. See the code below - it will further explain what I am doing.
Assembly SampleAssembly = Assembly.LoadFrom("WindowsFormsApplication2.ThisisaTESTDLL.dll");
Type myType = SampleAssembly.GetTypes()[0];
MethodInfo Method = myType.GetMethod("myVoid");
object myInstance = Activator.CreateInstance(myType,null);
Method.Invoke(myInstance,new object[] { "param1", "param1"});
If I am missing anything here, please respectfully let me know and I will edit the original post.
Assembly.GetExecutingAssembly().GetManifestResourceStream(...)
That should get you a Stream object. You can read a byte array from that.
You can load that using Assembly.Load
I embedded AxInterop.WMPLib.dll and Interop.WMPLib.dll into my exe and got them loaded by using the following code. The code is placed just at the beginning of static void Main() in Program.cs file. Target framework is .NET 3.5 in my case. This code helped me bundle the dlls into the exe itself without having to deploy them through installers. I have hardcoded my names. In the code below "res" is the name of my resource "res.resx" that contains the two embedded dlls.
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(
(s, a) =>
{
if (a.Name.Substring(0, a.Name.IndexOf(",")) == "AxInterop.WMPLib")
{
return Assembly.Load(res.AxInterop_WMPLib);
}
if (a.Name.Substring(0, a.Name.IndexOf(",")) == "Interop.WMPLib")
{
return Assembly.Load(res.Interop_WMPLib);
}
return null;
});

Assembly.GetTypes() - ReflectionTypeLoadException

We implement a plugin framework for our application and load plugin assemblies using Assembly.Loadfrom. We then use GetTypes() and further examine the types with each plugin file for supported Interfaces.
A path for the plugins is provided by the user and we cycle through each of the files in the folder to see if it (the plugin) supports our plugin interface. If it does, we create an instance, if not we move onto the next file.
We build two versions of software from the one code base (appA_1 and appA_2).
Loading the plugins works well when the plugins are loaded by the application that was built at the same time as the plugin file. However if we build appA_2 and point to the plugin folder of appA_1, we get an exception when GetTypes() is called.
A basic version of our code is;
var pluginAssembly = Assembly.LoadFrom(FileName);
foreach (var pluginType in pluginAssembly.GetTypes())
{
We get a "ReflectionTypeLoadException" exception.
This is concerning because we want our application to be able to load the types of any plugin, built by anyone. Is there something we are missing?
EDIT:
After iterating through the LoaderExceptions we have discovered that there is a single file libPublic.dll that generates a System.IO.FileNotFoundException exception. The strange thing is that this file resides in the application directory and the plugin is referenced to the project file.
EDIT 2:
In the exception log we find the following
"Comparing the assembly name resulted in the mismatch: Revision Number"
A few things:
Make sure you don't have duplicate assemblies in the plugin directory (i.e. assemblies that you're already loading in your main app from your app directory.) Otherwise, when you load your plugin, it may load an additional copy of the same assembly. This can lead to fun exceptions like:
Object (of type 'MyObject') is not of type 'MyObject'.
If you're getting the exception when instantiating a type, you may need to handle AppDomain.AssemblyResolve:
private void App_Startup(object sender, StartupEventArgs e)
{
// Since we'll be dynamically loading assemblies at runtime,
// we need to add an appropriate resolution path
// Otherwise weird things like failing to instantiate TypeConverters will happen
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
}
private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
var domain = (AppDomain) sender;
foreach (var assembly in domain.GetAssemblies())
{
if (assembly.FullName == args.Name)
{
return assembly;
}
}
return null;
}
I realize it's a bit strange to have to tell the CLR that, in order to resolve an assembly, find the assembly with the name we're using to resolve, but I've seen odd things happen without it. For example, I could instantiate types from a plugin assembly, but if I tried to use TypeDescriptor.GetConverter, it wouldn't find the TypeConverter for the class, even though it could see the Converter attribute on the class.
Looking at your edits, this is probably not what's causing your current exception, though you may run into these issues later as you work with your plugins.
Thanks to this post I could solve the ReflectionTypeLoadException that I was getting in a UITypeEditor. It's a designer assembly (a winforms smart-tag used at design-time) of a custom class library, that scan for some types.
/// <summary>
/// Get the types defined in the RootComponent.
/// </summary>
private List<Type> getAssemblyTypes(IServiceProvider provider)
{
var types = new List<Type>();
try
{
IDesignerHost host = (IDesignerHost)provider.GetService(typeof(IDesignerHost));
ITypeResolutionService resolution = (ITypeResolutionService)provider.GetService(typeof(ITypeResolutionService));
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
foreach (var assembly in ((AppDomain)sender).GetAssemblies())
{
if (assembly.FullName == args.Name)
{
return assembly;
}
}
return null;
};
Type rootComponentType = resolution.GetType(host.RootComponentClassName, false);
types = rootComponentType.Assembly.GetTypes().ToList();
}
catch
{
}
return types;
}
You are getting an assembly version mismatch. Since your plugins refer to this libPublic.dll, you must version it carefully and in particular not bump its revision/build/etc. numbers at every compile.

Categories

Resources