Problems with dynamically loading MVVM Light assemblies - c#

I planned to write a WPF application using MVVM Light and wanted to distribute it with just the executable only, meaning only the "MyApp.exe" (without all the other MVVM Light assemblies).
The idea I had was to embed all the assemblies in the project resources and then dynamically load it when the app starts up, such as calling the following block of code within App.xaml.cs.
private static void LoadAllAssemblies()
{
var assemblies = Assembly.GetExecutingAssembly().GetManifestResourceNames().Where(x => x.EndsWith(".dll")).ToList();
foreach (var assembly in assemblies)
{
using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(assembly))
{
if (stream == null)
continue;
var data = new Byte[stream.Length];
stream.Read(data, 0, data.Length);
Assembly.Load(data);
}
}
}
The problem now is that whenever I try to run it, it keeps on giving me a exception with the following message, "
The invocation of the constructor on type
'MyApp.Framework.ViewModelLocator' that matches the specified binding
constraints threw an exception.
Any idea why this is happening and how this could be done? Or is it even doable?

Related

Embedding SQLite.Interop.dll in 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.

Get All Configuration Files Across Application

I have a modularized application where each module is it's own project. Each module can then be packaged into a single dll.
I have added my own custom configuration file for each module called "Scripts.config" within the root of the project. I was wondering if there was a way to get all the Scripts.config files in the application.
The following code is used to get all implementations of a particular type:
private IList<Type> GetTypes<T>() {
return BuildManager.GetReferencedAssemblies().Cast<Assembly>().SelectMany(a => a.GetExportedTypes().Where(t => typeof(T).IsAssignableFrom(t) && !t.IsAbstract)).ToList();
}
I guess i'm looking for the equivalent but to get all the "Script.config" files. I guess I could embed the Scripts.config file but I was wondering if there was a better solution.
Sorry if I haven't explained this clearly. I'd really appreciate any help. Thanks
As I thought embedding the resource was the best way to go. I simply get the required assemblies and then use the following code for each assembly to retrieve the content of the Scripts.config file.
using (var stream = assembly.GetManifestResourceStream(assembly.GetManifestResourceNames().Single(n => n.EndsWith("Scripts.config")))) {
using (var sr = new StreamReader(stream)) {
var content = sr.ReadToEnd();
...
}
}

Visual studio locking DLL because of embedding views using "embedded resource"

PROJECT A contains a View , let's call it View1.ascx marked as "Embedded Resource" in the properties window
both PROJECT A and PROJECT B and C load that view1 from the PROJECTA.DLL using a custom resource provider
This way I can reuse my views across projects.
Sadly, this causes visual studio to be unable to build PROJECT B , OR C the first time around, after each change to the PROJECTA.dll
"Error 12 Could not copy "C:\GIT\PROJECTA\PROJECTA\bin\PROJECTA.dll" to "bin\PROJECTA.dll". Exceeded retry count of 10. Failed."
Is there any way to make this work? or should I somehow move all "re-used" views to a seperate assembly? The views use classes from PROJECT A so that's why I kept them inside PROJECT A
To make everything clear: Building it a second time around usually works, and the code and views are all working, it's just a really big waste of time to have to wait 10 seconds for the first build attempt to fail.
Apparantly my assemblyresourceprovider used a AssemblyResourceVirtualFile:VirtualFile oebject that was loading my dll from Assembly.LoadFile instead of using the recommended way of loading dlls in memory as described here: http://fzysqr.com/2010/04/26/asp-net-mvc2-plugin-architecture-tutorial/
I left the old line of code in comment for you guys to see where the problem was
public override System.IO.Stream Open()
{
string[] parts = path.Split('/');
string assemblyName = parts[2];
string resourceName = parts[3];
assemblyName = Path.Combine(HttpRuntime.BinDirectory, assemblyName);
byte[] assemblyBytes = File.ReadAllBytes(assemblyName);
System.Reflection.Assembly assembly = Assembly.Load(assemblyBytes);
/*System.Reflection.Assembly assembly = System.Reflection.Assembly.LoadFile(assemblyName);*/
if (assembly != null)
{
Stream resourceStream = assembly.GetManifestResourceStream(resourceName);
return resourceStream;
}
return null;
}

How to use an DLL load from Embed Resource?

I have a DLL >> System.Data.SQLite.dll
To use it in a normal way > just add it as reference and
using System.Data.SQLite;
then, I can use all the functions inside this DLL.
But, I want to merge my app.exe and this DLL into one single file.
I have tried using ILmerge, but fail. As I know, ILmerge cannot merge unmanage DLL.
So, I tried another method > make the DLL as embbed resource.
I am able to load it as an assembly with the below code:
Stream stm = Assembly.GetExecutingAssembly().GetManifestResourceStream("MyApp.System.Data.SQLite.dll");
byte[] ba = null;
byte[] buffer = new byte[16 * 1024];
using (MemoryStream ms = new MemoryStream())
{
int read;
while ((read = stm.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
ba = ms.ToArray();
}
Assembly sSQLiteDLL = Assembly.Load(ba);
but, how am I going to use the functions in SQLiteDLL?
I also tried add the DLL as resource in properties and load it like this:
public Form1()
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
InitializeComponent();
}
System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
AppDomain domain = (AppDomain)sender;
if (args.Name.Contains("System_Data_SQLite"))
{
return domain.Load(MyApp.Properties.Resources.System_Data_SQLite);
}
return null;
}
The above explained what I've got so far and I don't know what to do next to use the DLL? I still can't use the functions inside the DLL.
For example, when I type this:
SQLiteCommand cmd = new SQLiteCommand();
The Visual Studio says:
Error 21 The type or namespace name 'SQLiteCommand' could not be found (are you missing a using directive or an assembly reference?)
Can you share your insight? Thanks.
You can embed an assembly AND reference it (in VS) at the same time... for the way you want to use it you need to reference it! Any reason you don't reference the Assembly ?
Using a Type from an embedded Assembly (managed) without referencing it is a bit harder but possible using Reflection etc. - see these links (they include reference material AND some sample code etc.):
http://msdn.microsoft.com/en-us/library/system.activator.createinstance.aspx
https://stackoverflow.com/a/57450/847363
http://msdn.microsoft.com/en-us/library/h538bck7.aspx (loads an ssembly from a byte array so there is no need to write that assembly to the filesystem)
http://msdn.microsoft.com/en-us/library/system.reflection.assembly.gettype.aspx
http://www.codeproject.com/Articles/32828/Using-Reflection-to-load-unreferenced-assemblies-a
On embedding managed DLLs you have several options:
use ILMerge (free)
For howto see here and here
OR
use some tool like SmartAssembly (commercial)
it can embed and merge among other things (no need to change your source code)
OR
code that yourself in less than 10 lines (free but minimal source code change)
mark all needed dependencies as "embedded resource" - this way they are included in the EXE file... you need to setup an AssemblyResolve handler which at runtime reads from Resources and returns the needed DLLs to the .NET runtime...
This is not a method to use assemblies loaded in AppDomain.
Please read this article: How to: Load Assemblies into an Application Domain
in short you should call GetMethod() with method name (for example SqlCommand) and then call it via .Invoke() method.

Using an embedded DLL?

Is there any detailed guide on how to use a resource embedded dll within a c# source? All the guides I find on Google don't seem to help much. It's all "make a new class" or "ILMerge" this and ".NETZ" that. But I'm not sure on how to use the ILMerge and .NETZ stuff, and the guides on classes leave out what to do after making the class file, since I find nothing new after doing so. For example, this. After adding the class and function, I have no idea on how to reach out to get the dll from my resources.
So, to be specific, what I'm looking for is a guide on how to use a .dll file that was embedded into Resources to be able to call a class, without and parts left out. Please keep in mind that I am not very experienced with C# coding. Thanks in advance. :D
PS. Try not to use those big words. I tend to get lost easily.
You can get a Stream to the DLL using Assembly.GetManifestResourceStream, but in order to do anything with it you'll either need to load it into memory and call Assembly.Load, or extract it to the file system (and then quite possibly still call Assembly.Load or Assembly.LoadFile, unless you've actually already got a dependency on it).
After loading the assembly you'd have to use reflection to create instances of classes or call methods etc. All of this is quite fiddly - and in particular I can never remember the situations in which to call the various overloads of Assembly.Load (or similar methods). Jeff Richter's "CLR via C#" book would be a useful resource to have at your desk.
Could you give more information about why you need to do this? I've used manifest resources for various things, but never to include code... is there any reason you can't ship it alongside your executable?
Here's a complete example, albeit without error checking:
// DemoLib.cs - we'll build this into a DLL and embed it
using System;
namespace DemoLib
{
public class Demo
{
private readonly string name;
public Demo(string name)
{
this.name = name;
}
public void SayHello()
{
Console.WriteLine("Hello, my name is {0}", name);
}
}
}
// DemoExe.cs - we'll build this as the executable
using System;
using System.Reflection;
using System.IO;
public class DemoExe
{
static void Main()
{
byte[] data;
using (Stream stream = typeof(DemoExe).Assembly
.GetManifestResourceStream("DemoLib.dll"))
{
data = ReadFully(stream);
}
// Load the assembly
Assembly asm = Assembly.Load(data);
// Find the type within the assembly
Type type = asm.GetType("DemoLib.Demo");
// Find and invoke the relevant constructor
ConstructorInfo ctor = type.GetConstructor(new Type[]{typeof(string)});
object instance = ctor.Invoke(new object[] { "Jon" });
// Find and invoke the relevant method
MethodInfo method = type.GetMethod("SayHello");
method.Invoke(instance, null);
}
static byte[] ReadFully(Stream stream)
{
byte[] buffer = new byte[8192];
using (MemoryStream ms = new MemoryStream())
{
int bytesRead;
while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, bytesRead);
}
return ms.ToArray();
}
}
}
Building the code:
> csc /target:library DemoLib.cs
> csc DemoExe.cs /resource:DemoLib.dll
You might need to use ILMerge. Looks like it will solve the problem naturally for you.
http://research.microsoft.com/en-us/people/mbarnett/ILMerge.aspx

Categories

Resources