I have two dlls (xNet.dll and ag.dll), which I want to use in my project.
I add them to resourses, stated that build action is Embedded Resource.
Next I have such code to load the first dll:
public Form1()
{
AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolve;
InitializeComponent();
}
private static Assembly AssemblyResolve(object sender, ResolveEventArgs args)
{
Assembly assembly = Assembly.GetExecutingAssembly();
string assemblyName = args.Name.Split(',')[0];
using (Stream stream = assembly.GetManifestResourceStream("Yandex.dll.xNet.dll"))
{
if (stream == null)
return null;
byte[] rawAssembly = new byte[stream.Length];
stream.Read(rawAssembly, 0, (int)stream.Length);
return Assembly.Load(rawAssembly);
}
}
How to load the second dll?
You should match on the requested assembly name and return the correct assembly.
I don't know your assembly names so i'm doing only a very simple matching but it should look something like :
private static Assembly AssemblyResolve(object sender, ResolveEventArgs args)
{
if (args.Name.Contains("xNet"))
{
return LoadAssemblyFromResource("Yandex.dll.xNet.dll");
}
if (args.Name.Contains("ag"))
{
return LoadAssemblyFromResource("ag.dll");
}
return null;
}
private static Assembly LoadAssemblyFromResource(string resourceName)
{
Assembly assembly = Assembly.GetExecutingAssembly();
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
{
if (stream == null)
return null;
byte[] rawAssembly = new byte[stream.Length];
stream.Read(rawAssembly, 0, (int)stream.Length);
return Assembly.Load(rawAssembly);
}
}
Why not extract the dll's to a temporary path and then load them.Assume you've two dll's,namely firstDll and secondDll with both having build action set to Resource.
Then extract those dll's to a temporary path,like this;
byte[] firstAssembly=Properties.Resources.firstDll;
File.WriteAllBytes(#"C:\Temp\firstDll.dll",firstAssembly);
byte[] secondAssembly=Properties.Resources.secondDll;
File.WriteAllBytes(#"C:\Temp\secondDll.dll",secondAssembly);
After this use Reflection to load those assembles and work with them.
Related
I'm trying to develop an application that supports dynamic loading of external modules. I have read several articles from loading external assemblies using C# (.NET v4.5) and got the code below. However, it is not working, not detecting my subclass on the external module.
Here is the code for loading external assembly:
byte[] array = <HERE I LOAD THE DLL>
Assembly asb = Assembly.Load(array);
Type[] types = GetAssemblyTypes(asb);
for( int i = 0; i < types.Length; i++ )
{
Type t = types[i];
if( t != null && typeof(App).IsAssignableFrom(t) /*t.IsSubclassOf(typeof(App))*/ )
{
app.AppClass = (App)Activator.CreateInstance(t);
return true;
}
}
Here is the GetAssemblyTypes()
private Type[] GetAssemblyTypes(Assembly asb)
{
Type[] types;
try
{
types = asb.GetTypes();
}
catch( ReflectionTypeLoadException ex )
{
types = ex.Types;
}
return types;
}
Here is the class on the MAIN APPLICATION (This class will be used by the modules)
namespace MyApplication.API
{
public class App
{
// CODE
}
}
Here is the example of my module:
using MyApplication.API;
namespace HelloWorld
{
class HelloWorld : App
{
}
}
Important points are:
1 - I don't know the class name of the module, I just know that it will be a subclass of the App class.
The issue is that although the types.Length gives me 1, when I tries to access by types[i] it gives a null pointer. Am I missing something here?
I have done something similar to find out if the assembly is derived from my base assembly
var assembly = System.Reflection.Assembly.LoadFrom(file);
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += CurrentDomain_ReflectionOnlyAssemblyResolve;
var derivedAssemblies = assembly.GetExportedTypes().Where(w => w.IsSubclassOf(typeof(AddressManager.Base.Connector.ConnectorBase))).Count();
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve -= CurrentDomain_ReflectionOnlyAssemblyResolve;
assembly = null;
if (derivedAssemblies > 0)
{
Manager.LoadAssembly(file, "Connectors");
Trace.TraceInformation(" Success! Library loaded.");
}
else
Trace.TraceInformation(" Skipped! Not a subclass of '" + typeof(AddressManager.Base.Connector.ConnectorBase).Name + "'.");
And handle the ReflectionOnlyAssemblyResolve Event:
private System.Reflection.Assembly CurrentDomain_ReflectionOnlyAssemblyResolve(object sender, ResolveEventArgs args)
{
var assembly = AppDomain.CurrentDomain.GetAssemblies().Where(w => w.FullName == args.Name).FirstOrDefault();
return assembly;
}
I got a semi working version based on #CadBurry code.
byte[] bytes = <HERE I LOAD THE DLL>
Assembly asb = Assembly.Load(bytes);
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += CurrentDomain_ReflectionOnlyAssemblyResolve;
IEnumerable<Type> types = asb.GetExportedTypes().Where(w => w.IsSubclassOf(typeof(App)));
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve -= CurrentDomain_ReflectionOnlyAssemblyResolve;
AppDomain.CurrentDomain.AssemblyResolve -= CurrentDomain_AssemblyResolve;
if(types.Count() > 0)
{
Type type = types.FirstOrDefault();
if( type == null )
return false;
app.AppClass = (App)Activator.CreateInstance(type);
return true;
}
with the methods:
private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
if( args.Name.Contains(typeof(MyApplication).Assembly.GetName().Name) )
{
return Assembly.GetExecutingAssembly();
}
return null;
}
private Assembly CurrentDomain_ReflectionOnlyAssemblyResolve(object sender, ResolveEventArgs args)
{
Assembly asb = AppDomain.CurrentDomain.GetAssemblies().Where(w => w.FullName == args.Name).FirstOrDefault();
return asb;
}
Using the above code I can load the assembly even when it is not on the current directory of the main application (eg. Plugins folders).
I want to upload image from the WP page to the database via WCF service. I have the following service implementation that compiles fine:
public bool Upload(Stuff picture)
{
FileStream fileStream = null;
BinaryWriter writer = null;
string filePath;
try
{
filePath = HttpContext.Current.Server.MapPath(".") +
ConfigurationManager.AppSettings["PictureUploadDirectory"] +
picture.stuffName;
if (picture.stuffName != string.Empty)
{
fileStream = File.Open(filePath, FileMode.Create);
writer = new BinaryWriter(fileStream);
writer.Write(picture.stuffPhoto);
}
return true;
}
catch (Exception)
{
return false;
}
finally
{
if (fileStream != null)
fileStream.Close();
if (writer != null)
writer.Close();
}
}
But on the WP page I have errors. I'm following this tutorial: http://www.silverlightshow.net/items/Uploading-and-downloading-images-from-WCF-in-Silverlight.aspx Here is my code:
private void uploadBtn_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog(); // error 1,2
openFileDialog.Filter = "JPEG files|*.jpg";
if (openFileDialog.ShowDialog() == true)
{
Stream stream = (Stream)openFileDialog.File.OpenRead();
byte[] bytes = new byte[stream.Length];
stream.Read(bytes, 0, (int)stream.Length);
string fileName = openFileDialog.File.Name;
ServiceReference1.Stuff pictureFile = new ServiceReference1.Stuff();
ServiceReference1.Stuff.stuffName = fileName; //error 3,4
ServiceReference1.Stuff.stuffPhoto = bytes;
ServiceReference1.Service1Client client = new ServiceReference1.Service1Client();
client.UploadCompleted += new EventHandler
<System.ComponentModel.AsyncCompletedEventArgs>(client_UploadCompleted); // error 5
client.UploadAsync(pictureFile);
}
}
void client_UploadCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
if (e.Error == null)
{
if (e.Result) // error 6
{
ResultTextlock.Text = "Upload succeeded :)";
}
else
{
ResultTextBlock.Text = "Upload failed :(";
}
}
}
Errors are:
1,2) The type or namespace name 'OpenFileDialog' could not be found (are you missing a using directive or an assembly reference?).
3,4) An object reference is required for the non-static field, method, or property 'PhoneApp1.ServiceReference1.Stuff.stuffName.get'
5) Cannot implicitly convert type 'System.EventHandler' to 'System.EventHandler'
6)'System.ComponentModel.AsyncCompletedEventArgs' does not contain a definition for 'Result' and no extension method 'Result' accepting a first argument of type 'System.ComponentModel.AsyncCompletedEventArgs' could be found (are you missing a using directive or an assembly reference?)
7) The type or namespace name 'Forms' does not exist in the namespace 'System.Windows' (are you missing an assembly reference?) (I have tis statement: using System.Windows.Forms;)`
Windows Phone does not have OpenFileDialog, you have to use the Photo Chooser task
You are referencing the ServiceReference1.Stuff as a static object. I think you mean pictureFile.stuffName etc.
The types of the event are wrong, as the error message states.
Windows Phone doesn't have Windows Forms available.
You can't just copy/paste code from Silverlight to Windows Phone and assume it works. You have to learn how to work with WP. Also these are basic errors that are explained clearly in the error messages. I suggest brushing up on the basics.
I would like to add an assembly with ResourceManager I have this code but it obviously doesn't work. Please help!
Loading the resource and trying to use it as an assembly:
static ResourceManager resourceManager = new ResourceManager("res", Assembly.GetExecutingAssembly());
static void Main()
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}
static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
AppDomain domain = (AppDomain)sender;
if(args.Name.Contains("System.Data.SQLite"))
{
return domain.Load(resourceManager.GetObject("System.Data.SQLite"));
}
return null;
}
Putting the resource in a ResourceManager:
using (ResourceWriter w = new ResourceWriter("res.resources"))
{
w.AddResource("System.Data.SQLite", File.ReadAllText("System.Data.SQLite.dll"));
}
if (CodeDom.Compile(outputValueTb.Text, Properties.Resources.src, iconValueTb.Text, "res.resources"))
{
//File.Copy("System.Data.SQLite.dll", System.IO.Path.GetDirectoryName(outputValueTb.Text) + "/System.Data.SQLite.dll");
File.Delete("res.resources");
success("Built");
}
Edit
So I changed my code to this
w.AddResource("System.Data.SQLite", File.ReadAllBytes("System.Data.SQLite.dll"));
But I still don't know how to use the resource into an assembly with this code:
AppDomain domain = (AppDomain)sender;
if(args.Name.Contains("System.Data.SQLite"))
{
return domain.Load(resourceManager.GetObject("System.Data.SQLite")); //should be a resource not bytes[]
}
return null;
If you have added the dll as a project item file and marked it "Embedded Resource" on the Build Action type (under properties), then you can use the following. Resolver is the event handler that you already have. Note: I haven't checked syntax, just typed it here; so fix syntax if needed.
internal static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
var name = Assembly.GetExecutingAssembly()
.GetManifestResourceNames()
.FirstOrDefault(f => f.Contains("System.Data.SQLite"));
if (!string.IsNullOrEmpty(name) && args.Name.Contains("SQLite"))
{
using(var strm = Assembly.GetExecutingAssembly().GetManifestResourceStream(name))
{
var bytes = new byte[strm.Length];
stream.Read(bytes, 0, strm.Length);
return Assembly.Load(bytes);
}
}
return null;
}
I have a C# console program that uses the nuget plugin SocketIO4Net
When I build the exe and move it to my Windows 2008 server, it doesn't work, whereas on my local machine, it works.
Unhandled Exception: System.IO.FileNotFoundException: Could not load file or assembly 'SocketIOClient, Version=0.6.26.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.
Is there any way I can bake all my dependencies into the exe?
I tried doing:
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
var resName = "converter.SocketIOClient.dll";
var thisAssembly = Assembly.GetExecutingAssembly();
using (var input = thisAssembly.GetManifestResourceStream(resName))
{
return input != null
? Assembly.Load(StreamToBytes(input))
: null;
}
};
But that didn't work. Perhaps I'm getting the resourceName wrong?
Here is my example which is based off of Embedding one dll inside another as an embedded resource and then calling it from my code but has some helpful screenshots.
using System;
using System.IO;
using System.Reflection;
using System.Windows.Forms;
using MyEmbbedFile;
namespace ProjectNameSpace
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
var resName = "ProjectNameSpace.MyEmbbedFile.dll";
var thisAssembly = Assembly.GetExecutingAssembly();
using (var input = thisAssembly.GetManifestResourceStream(resName))
{
return input != null
? Assembly.Load(StreamToBytes(input))
: null;
}
};
}
private void button1_Click(object sender, EventArgs e)
{
MyEmbbedFileApp app = new MyEmbbedFileApp();
app.DoStuff();
}
private static byte[] StreamToBytes(Stream input)
{
var capacity = input.CanSeek ? (int)input.Length : 0;
using (var output = new MemoryStream(capacity))
{
int readLength;
var buffer = new byte[4096];
do
{
readLength = input.Read(buffer, 0, buffer.Length);
output.Write(buffer, 0, readLength);
}
while (readLength != 0);
return output.ToArray();
}
}
}
}
There are 2 other things you will need to do:
You will still need to make sure you add your assembly as a reference so your code compiles. Just make sure it does not copy to the output directory.
The second thing you need to do is add your reference to the project as a normal file. Then set it's build action to Embedded Resource under properties.
Yes.
Use AppDomain.AssemblyResolve to 'hydrate' embedded assemblies at runtime.
This project SQLDiagCmd at Github contains an example of doing this. It is based on Jeffrey Ricther's method:
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
String resourceName = "AssemblyLoadingAndReflection." +
new AssemblyName(args.Name).Name + ".dll";
using (var stream = Assembly.GetExecutingAssembly()
.GetManifestResourceStream(resourceName))
{
Byte[] assemblyData = new Byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
return Assembly.Load(assemblyData);
}
};
The 'trick' is where the embedded assembly is located and (as you have found), the string used to refer to it in the AssemblyResolve handler. [I don't have time right now but will look again later...]
I have A C# Visual Studio 2012 Solution that relies on a native dll that I use PInvoke to access. When I deploy the app I will have to ensure that this Dll is in the app folder.
Is there anyway I can merge this Dll into the executable?
perhaps as a resource?
I have heard of ILMerge but I am told it cant cope with native code.
Any help would be appreciated.
You can create a Setup package project with Visual Studio that deploys all your files to the correct location or use other third party packaging software (like full InstallShield or alternatives)
However, your question reminds me on the Open Hardware Monitor project where they include drivers as embedded resource and extract them when the user starts the application. It works like this: they've added WinRing0.sys and WinRing0x64.sys to the project and set their Build Action to Embedded Resource, then they have a method that extracts the driver from the resource:
private static bool ExtractDriver(string fileName) {
string resourceName = "OpenHardwareMonitor.Hardware." +
(OperatingSystem.Is64BitOperatingSystem() ? "WinRing0x64.sys" :
"WinRing0.sys");
string[] names =
Assembly.GetExecutingAssembly().GetManifestResourceNames();
byte[] buffer = null;
for (int i = 0; i < names.Length; i++) {
if (names[i].Replace('\\', '.') == resourceName) {
using (Stream stream = Assembly.GetExecutingAssembly().
GetManifestResourceStream(names[i]))
{
buffer = new byte[stream.Length];
stream.Read(buffer, 0, buffer.Length);
}
}
}
if (buffer == null)
return false;
try {
using (FileStream target = new FileStream(fileName, FileMode.Create)) {
target.Write(buffer, 0, buffer.Length);
target.Flush();
}
} catch (IOException) {
// for example there is not enough space on the disk
return false;
}
// make sure the file is actually writen to the file system
for (int i = 0; i < 20; i++) {
try {
if (File.Exists(fileName) &&
new FileInfo(fileName).Length == buffer.Length)
{
return true;
}
Thread.Sleep(100);
} catch (IOException) {
Thread.Sleep(10);
}
}
// file still has not the right size, something is wrong
return false;
}
They're reading the resource into a buffer, write that buffer to disk and wait until the file has been flushed to disk.
My solution is conceptually similar to the one presented by Wouter.
It's what we use in our own app, and we can use native/mixed-mode and c# dlls all embedded in the same .exe.
It extracts the dlls into a temp dir everytime the application is run. Obviously you might not want to do this in the production version, where the dlls will be stable; you might choose a different directory there (probably somewhere in %AppData%). It will use an existing dll with the same version number, though (e.g. it's only done the first time when opening the app multiple times between booting the computer).
Since we're doing
AppDomain.CurrentDomain.AssemblyResolve += (sender, args)
this function is getting called wherever the system tries to resolve a dll. And since it's initalised in the static Program class, it all works automagically.
Program.cs:
namespace MyApp
{
internal class Program
{
static Program()
{
LoadAssemblyResource.Initialize("MyApp");
}
//....
}
}
LoadAssemblyResource.cs
namespace MyAppStartup
{
public static class LoadAssemblyResource
{
private readonly static String _version_string =
Assembly.GetExecutingAssembly().GetName().Version.ToString();
private readonly static String _dll_path = Path.GetTempPath()
+ "\\MyApp\\" + _version_string;
static public String last_error_msg = null;
public static bool WriteBytesToFile(string filename, byte[] bytes)
{
try
{
var fs = new FileStream(filename, FileMode.Create, FileAccess.Write);
fs.Write(bytes, 0, bytes.Length);
fs.Close();
return true;
}
catch (Exception e)
{
Console.WriteLine("Writing file failed. Exception: {0}", e.ToString());
}
return false;
}
public static Assembly LoadUnsafe(String assembly_name, Byte[] assembly)
{
if (!Directory.Exists(_dll_path))
{
Directory.CreateDirectory(_dll_path);
Console.WriteLine("Created tmp path '" + _dll_path + "'.");
}
String fullpath = _dll_path + "\\" + assembly_name;
if (!File.Exists(fullpath))
{
Console.WriteLine("Assembly location: " + fullpath + ".");
if (!WriteBytesToFile(fullpath, assembly))
return null;
}
return Assembly.UnsafeLoadFrom(fullpath);
}
public static void Initialize(String exe_name)
{
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
String assembly_name = new AssemblyName(args.Name).Name + ".dll";
String resource_name = exe_name + "." + assembly_name;
using (var stream =
Assembly.GetExecutingAssembly().GetManifestResourceStream(resource_name))
{
if (stream == null)
return null;
Byte[] assembly_data = new Byte[stream.Length];
stream.Read(assembly_data, 0, assembly_data.Length);
try
{
Assembly il_assembly = Assembly.Load(assembly_data);
return il_assembly;
}
catch (System.IO.FileLoadException ex)
{
// might have failed because it's an mixed-mode dll.
last_error_msg = ex.Message;
}
Assembly mixed_mode_assembly = LoadUnsafe(assembly_name, assembly_data);
return mixed_mode_assembly;
}
};
}
}
}