I have a utilities library (dll) which contains the class (shown below) for working with embedded resources. In Visual Studio 2013 it works as expected and loads the resources from the assembly which calls the function in the utilities library.
In Visual Studio 2015 when I call the functions the code attempts to load the resources from the utilities library instead of the calling library. They are seperate assemblies.
Please can you help me understand why this is happening, and how I can get it to work in Visual Studio 2015?
Thanks in advance.
public static class EmbeddedResources
{
public static string[] GetAllResourceNames()
{
Assembly _assembly = Assembly.GetExecutingAssembly();
return _assembly.GetManifestResourceNames();
}
public static string ReadQueryResource(string resourceName)
{
Assembly _assembly;
StreamReader _textStreamReader;
try
{
_assembly = Assembly.GetExecutingAssembly();
_textStreamReader = new StreamReader(_assembly.GetManifestResourceStream(resourceName));
if (_textStreamReader.Peek() != -1)
return _textStreamReader.ReadToEnd();
}
catch
{
//MessageBox.Show("Error accessing resources!");
}
return null;
}
}
Assembly.GetExecutingAssembly()
Gets the assembly that contains the code that is currently executing.
Assembly.GetCallingAssembly()
Returns the Assembly of the method that invoked the currently executing method.
Related
I have a .Net Framework WPF application that I'm currently migrating to .Net6. At startup it examines certain assemblies in the executable folder looking for any with a custom assembly attribute. Those that have this are then loaded into the current appdomain. (Note that some of these assemblies may already be in the appdomain, as they are projects in the running application's solution).
This is the 4.x code:
private void LoadAssemblies(string folder)
{
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve +=
(s, e) => Assembly.ReflectionOnlyLoad(e.Name);
var assemblyFiles = Directory.GetFiles(folder, "*.Client.dll");
foreach (var assemblyFile in assemblyFiles)
{
var reflectionOnlyAssembly = Assembly.ReflectionOnlyLoadFrom(assemblyFile);
if (ContainsCustomAttr(reflectionOnlyAssembly))
{
var assembly = Assembly.LoadFrom(assemblyFile);
ProcessAssembly(assembly);
}
}
}
The custom assembly attribute (that this code is looking for) has a string property containing a path to a XAML resource file within that assembly. The ProcessAssembly() method adds this resource file to the application's merged dictionary, something like this:
var resourceUri = string.Format(
"pack://application:,,,/{0};component/{1}",
assembly.GetName().Name,
mimicAssemblyAttribute.DataTemplatePath);
var uri = new Uri(resourceUri, UriKind.RelativeOrAbsolute);
application.Resources.MergedDictionaries.Add(new ResourceDictionary { Source = uri });
Just to reiterate, all this works as it should in the .Net 4.x application.
.Net6 on the other hand doesn't support reflection-only loading, nor can you create a second app domain in which to load the assemblies. I rewrote the above code by loading the assemblies being examined into what I understand is a temporary, unloadable context:
private void LoadAssemblies(string folder)
{
var assemblyFiles = Directory.GetFiles(folder, "*.Client.dll");
using (var ctx = new TempAssemblyLoadContext(AppDomain.CurrentDomain.BaseDirectory))
{
foreach (var assemblyFile in assemblyFiles)
{
var assm = ctx.LoadFromAssemblyPath(assemblyFile);
if (ContainsCustomAttr(assm))
{
var assm2 = Assembly.LoadFrom(assemblyFile);
ProcessAssembly(assm2);
}
}
}
}
private class TempAssemblyLoadContext : AssemblyLoadContext, IDisposable
{
private AssemblyDependencyResolver _resolver;
public TempAssemblyLoadContext(string readerLocation)
: base(isCollectible: true)
{
_resolver = new AssemblyDependencyResolver(readerLocation);
}
public void Dispose()
{
Unload();
}
protected override Assembly Load(AssemblyName assemblyName)
{
var path = _resolver.ResolveAssemblyToPath(assemblyName);
if (path != null)
{
return LoadFromAssemblyPath(path);
}
return null;
}
protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
{
var path = _resolver.ResolveUnmanagedDllToPath(unmanagedDllName);
if (path != null)
{
return LoadUnmanagedDllFromPath(path);
}
return IntPtr.Zero;
}
}
(Note the ProcessAssembly() method is unchanged).
This code "works" in so much as it goes through the motions without crashing. However at a later point when the application starts creating the views, I get the following exception:
The component '..ModeSelectorView' does not have a resource identified by the URI '/.;component/views/modeselector/modeselectorview.xaml'.
This particular view resides in a project of this application's solution, so the assembly will already be in the appdomain. The assembly also contains that custom attribute so the above code will be trying to load it, although I believe that Assembly.LoadFrom() should not load the same assembly again?
Just in case, I modified the "if" block in my LoadAssemblies() method to ignore assemblies already in the app domain:
if (ContainsCustomAttr(assm) && !AppDomain.CurrentDomain.GetAssemblies().Contains(assm))
Sure enough, a breakpoint shows that the assembly in question (containing that view) is ignored and not loaded into the app domain. However I still get the same exception further down the line.
In fact I can comment out the entire "if" block so no assemblies are being loaded into the app domain, and I still get the exception, suggesting that it's caused by loading the assembly into that AssemblyLoadContext.
Also, a breakpoint shows that context is being unloaded via its Dispose() method, upon dropping out of the "using" block in the LoadAssemblies() method.
Edit: even with the "if" block commented out, a breakpoint at the end of the method shows that all the assemblies being loaded by ctx.LoadFromAssemblyPath() are ending up in AppDomain.Current. What am I not understanding? Is the context part of the appdomain and not a separate "area"? How can I achieve this "isolated" loading of assemblies in a similar way to the "reflection only" approach that I was using in .Net 4.x?
Okay, so I found the answer, which is to use MetadataLoadContext. This is essentially the .Net Core replacement for reflection-only loading:
private void LoadAssemblies(string folder)
{
// The load context needs access to the .Net "core" assemblies...
var allAssemblies = Directory.GetFiles(RuntimeEnvironment.GetRuntimeDirectory(), "*.Client.dll").ToList();
// .. and the assemblies that I need to examine.
var assembliesToExamine = Directory.GetFiles(folder, "NuIns.CoDaq.*.Client.dll");
allAssemblies.AddRange(assembliesToExamine);
var resolver = new PathAssemblyResolver(allAssemblies);
using (var mlc = new MetadataLoadContext(resolver))
{
foreach (var assemblyFile in assembliesToExamine)
{
var assm = mlc.LoadFromAssemblyPath(assemblyFile);
if (ContainsCustomAttr(assm))
{
var assm2 = Assembly.LoadFrom(assemblyFile);
AddMimicAssemblyInfo(assm2);
}
}
}
}
I am trying to use a custom DLL in a Inno Setup script during installation. I wrote a very simple function that basically checks a connection string for a MySQL database using MySQL .NET connector (there is no MySQL client on the target server). The code of this exported function is:
public class DbChecker
{
[DllExport("CheckConnexion", CallingConvention.StdCall)]
public static int CheckConnexion([MarshalAs(UnmanagedType.LPStr)] string connexionString)
{
int success;
try
{
MySqlConnection connection = new MySqlConnection(connexionString);
connection.Open();
connection.Close();
success = 0;
}
catch (Exception)
{
success = 1;
}
return success;
}
}
The function is imported this way in Inno Setup :
[Files]
Source: "..\..\MyDll\bin\x86\Release\*"; Flags: dontcopy;
and
[Code]
function CheckConnexion(connexionString: AnsiString): Integer;
external 'CheckConnexion#files:MyDll.dll,MySql.Data.dll stdcall setuponly loadwithalteredsearchpath';`
The problem is that the setup throws an exception at runtime:
Runtime Error (at 53:207):
External exception E0434352.
I think I have to use the files prefix because the function is called in the NextButtonClick event handler, before files are copied to the {app} directory.
Both MyDll.dll and MySql.Data.dll are correctly extracted to the {tmp} directory at runtime.
I tried both with and without the loadwithalteredsearchpath flag with the same result.
What I found is that this error code is a generic .NET runtime error code.
If I remove the part using MySql.Data it works perfectly fine (except that it does nothing...)
As advised on other threads I've been trying to log the error in my .NET code using EventLog and UnhandledException but I have the same exception no matter what (and no log source is created), even without the MySQL part. I checked EventLog permissions on my computer.
It seems that the exception is thrown as soon as I use anything else that "basic" C# code (whenever I try to load another DLL).
There is probably a better way, but this will do.
Implement an initialization function (Init here) that sets up AppDomain.AssemblyResolve handler that looks for an assembly in the path of the main (executing) assembly:
[DllExport("Init", CallingConvention.StdCall)]
public static void Init()
{
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.AssemblyResolve += new ResolveEventHandler(MyResolveEventHandler);
}
private static Assembly MyResolveEventHandler(object sender, ResolveEventArgs args)
{
string location = Assembly.GetExecutingAssembly().Location;
AssemblyName name = new AssemblyName(args.Name);
string path = Path.Combine(Path.GetDirectoryName(location), name.Name + ".dll");
if (File.Exists(path))
{
return Assembly.LoadFrom(path);
}
return null;
}
Import it to the Inno Setup:
procedure Init(); external 'Init#files:MyDll.dll stdcall setuponly';
And call it before calling the function that needs the dependency (CheckConnexion).
Another solution might be this:
Embedding DLLs in a compiled executable
Btw, no need for the loadwithalteredsearchpath flag. It has no effect on .NET assemblies imo. They are needed for native DLL dependencies: Loading DLL with dependencies in Inno Setup fails in uninstaller with "Cannot import DLL", but works in the installer.
I found something else that might be helpful for anyone stumbling upon this page.
In my scenario, I have several C# methods that I call from InnoSetup using DllExport. In one of those methods, I call another of the methods. This caused Inno to throw "External exception E0434352".
If I moved the code to a method not called by InnoSetup, everything worked fine.
So...
[DllExport("Fu", CallingConvention = CallingConvention.StdCall)]
public static int Fu()
{
// Stuff
}
[DllExport("Bar", CallingConvention = CallingConvention.StdCall)]
public static int Bar()
{
Fu();
}
...causes InnoSetup to cry, but:
[DllExport("Fu", CallingConvention = CallingConvention.StdCall)]
public static int Fu()
{
LocalFu();
}
private static int LocalFu()
{
// Stuff
}
[DllExport("Bar", CallingConvention = CallingConvention.StdCall)]
public static int Bar()
{
// Stuff
LocalFu();
// Other stuff
}
...is fine.
I don't know if this is caused by Inno or DllExport, so I'll forgo direct derision and blame society as a whole for my lost morning. (Or myself for being a new to this thing.)
I would like to expand upon Martin's answer. There is a way to resolve the assemblies without having to call an Init method first and that is by including a static constructor in your .NET class:
public class MyClass
{
static MyClass()
{
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.AssemblyResolve += MyResolveEventHandler;
}
private static Assembly MyResolveEventHandler(object sender, ResolveEventArgs args)
{
var location = Assembly.GetExecutingAssembly().Location;
var assemblyName = new AssemblyName(args.Name);
var path = Path.Combine(Path.GetDirectoryName(location), assemblyName.Name + ".dll");
if (File.Exists(path))
{
return Assembly.LoadFrom(path);
}
return null;
}
}
I used to have some code which scanned the bin directory of my application for assemblies which weren't loaded in the AppDomain yet and loaded them. It basically looked like:
foreach (var assemblyPath in Directory.GetFiles("path\to\bin", "*.dll"))
{
var inspected = Assembly.ReflectionOnlyLoadFrom(assemblyPath);
Assembly.Load(inspected.GetName());
}
I skipped the try/catch clauses, etc for brevity.
This allowed me to drop assemblies in the bin folder at run-time with implementations for certain interfaces and let the IoC container pick them up automatically. Now with the new Roslyn magic, there are no physical DLL's anymore when debugging. Is there any way to retrieve assembly names, project names or dependency names (in project.json) dynamically.
I guess I have to implement something like this example in the Entropy repo, but I don't know how to implement it for my scenario.
You can use the IAssemblyLoadContextAccessor interface to load ASP.NET 5 class library (.xproj) projects dynamically. The following example code works with Beta 4:
public class Startup
{
public void Configure(IApplicationBuilder app)
{
var assemblyLoadContextAccessor = app.ApplicationServices.GetService<IAssemblyLoadContextAccessor>();
var loadContext = assemblyLoadContextAccessor.Default;
var loadedAssembly = loadContext.Load("NameOfYourLibrary");
}
}
What you are looking for is ILibraryManager implementation which provides access to the complete graph of dependencies for the application. This is already flowed through the ASP.NET 5 DI system. So, you can reach out to it from there.
Sample usage can be found inside RoslynCompilationService.
I solved this issue partly using the ILibraryManager as suggested by #tugberk. I changed the approach a bit which dropped the need of scanning the bin folder for new assemblies. I just want all the loaded assemblies in the current AppDomain.
I injected an instance of the ILibraryManager interface in my type finder class and used the GetReferencingLibraries() method with the name of the core assembly, which is referenced by all the other assemblies in the application.
A sample implementation can be found here, where this is the important part:
public IEnumerable<Assembly> GetLoadedAssemblies()
{
return _libraryManager.GetReferencingLibraries(_coreAssemblyName.Name)
.SelectMany(info => info.Assemblies)
.Select(info => Assembly.Load(new AssemblyName(info.Name)));
}
For .net core users, here is my code for loading assemblies from a specific path. I had to use directives, as it's slightly different for .Net Framework and .Net Core.
In your class header you'll need to declare the using something similar to:
#if NET46
#else
using System.Runtime.Loader;
#endif
And in your function something similar to the following:
string assemblyPath = "c:\temp\assmebly.dll";
#if NET46
Assembly assembly = Assembly.LoadFrom(assemblyPath);
#else
AssemblyLoadContext context = AssemblyLoadContext.Default;
Assembly assembly = context.LoadFromAssemblyPath(assemblyPath);
#endif
Its not ASP.NET but it can be converted easily to asp.net.
bellow if function for loading an assembly, and invoke a method inside a class on that assembly.
private static FormCustomized loadLayout(global::System.String layoutFilename, global::System.String layoutNameSpace)
{
FormCustomized mainForm = default;
Type typeMainLayout = default;
FileInfo layoutFile;
layoutFile = new FileInfo(layoutFilename);
layoutFile.Refresh();
if (!layoutFile.Exists)
{
MessageBox.Show("Layout file not found. You need to reinstall the program");
return default;
}
try
{
Assembly assemblyRaw = Assembly.LoadFrom(layoutFilename);
AssemblyLoadContext context = AssemblyLoadContext.Default;
Assembly assembly = context.LoadFromAssemblyPath(layoutFilename);
Type typeMainLayoutIni = assembly.GetType(layoutNameSpace + ".initializeLayoutClass");
Object iniClass = Activator.CreateInstance(typeMainLayoutIni, true);
MethodInfo methodInfo = typeMainLayoutIni.GetMethod("AssembliesToLoadAtStart");
enVars.assemblies = (Dictionary<string, Environment.environmentAssembliesClass>)methodInfo.Invoke(iniClass, default);
typeMainLayout = assembly.GetType(layoutNameSpace + ".mainAppLayoutForm");
mainForm = Activator.CreateInstance(typeMainLayout, enVars) as FormCustomized;
}
catch (Exception ex)
{
return default;
}
return default;
}
I am currently writing an application suite with a plugin system that loads plugins at runtime using the MEF framework.
I have currently setup one of my top level WPF applications to embed it's referenced DLLs as embedded resources and load them at runtime using the method described here.
This works fine and I get my single file WPF application that runs fine.
However, another of my top level console applications uses the MEF framework to load plugins at runtime (the WPF application is fixed and includes the plugins explicitly). My plugins have several dependencies themselves on various libraries and the extensions folder that the console application loads the plugins from is littered with all the various library dlls.
I would like to embed the dependencies of each plugin within itself so that my extensions directory contains only the top level DLL files. The method that I have used above does not cater for this approach as the plugin component cannot find the required dependency as it is only the executing assembly that is being searched for these embedded resources.
My current OnResolveAssembly method looks like this:
public static Assembly OnResolveAssembly(object sender, ResolveEventArgs args)
{
Assembly executingAssembly = Assembly.GetExecutingAssembly();
var assemblyName = new AssemblyName(args.Name);
string path = assemblyName.Name + ".dll";
if (assemblyName.CultureInfo.Equals(CultureInfo.InvariantCulture) == false)
{
path = String.Format(#"{0}\{1}", assemblyName.CultureInfo, path);
}
using (Stream stream = executingAssembly.GetManifestResourceStream(path))
{
if (stream == null)
return null;
var assemblyRawBytes = new byte[stream.Length];
stream.Read(assemblyRawBytes, 0, assemblyRawBytes.Length);
return Assembly.Load(assemblyRawBytes);
}
}
I'm thinking that the best way to proceed would be to add in functionality to keep track of all assemblies loaded in a list and once a new assembly has been loaded in this way, recursively do the same; load any embedded DLLs within those as you go. You can then add these DLLs to the list which will act as a cache.
Is there perhaps a better way to proceed with this?
I have implemented a very similar solution to yours and it works very fine for me. As you can see I keep track of already loaded assemblies in a _references dictionary.
In my case, I do not need to "eagerly" load all embedded dependencies in any recursive way, but rather my embedded assemblies do register themselves with the application host on-demand.
public static class ApplicationHost
{
private static readonly Dictionary<string, Assembly> _references = new Dictionary<string, Assembly>();
[STAThread]
private static void Main()
{
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => _references.ContainsKey(args.Name) ? _references[args.Name] : null;
RegisterAssemblyAndEmbeddedDependencies();
// continue application bootstrapping...
}
public static void RegisterAssemblyAndEmbeddedDependencies()
{
var assembly = Assembly.GetCallingAssembly();
_references[assembly.FullName] = assembly;
foreach (var resourceName in assembly.GetManifestResourceNames())
{
using (var resourceStream = assembly.GetManifestResourceStream(resourceName))
{
var rawAssembly = new byte[resourceStream.Length];
resourceStream.Read(rawAssembly, 0, rawAssembly.Length);
var reference = Assembly.Load(rawAssembly);
_references[reference.FullName] = reference;
}
}
}
}
I have a huge application where one project of my solution makes reports.
I want to add new report (update report) without building my project, just add .dll files. I read about Assembly and
AppDomain, but I don't know is it really good way to add new dll for new report and how to update old report in runtime?
Here's my example, it takes my first dll, but second time it doesn't. First dll - sum, second - deducted.
static void Main(string[] args)
{
try
{
//first domain
AppDomain domain = AppDomain.CreateDomain("MyDomain");
AssemblyDll asb1 = new AssemblyDll();
Console.WriteLine(asb1.AssemblyMethod(1));
AppDomain.Unload(domain);
Console.ReadKey();
//second domain
AppDomain newDomain = AppDomain.CreateDomain("myNewDomain");
AssemblyDll asb2 = new AssemblyDll();
Console.WriteLine(asb2.AssemblyMethod(2));
AppDomain.Unload(newDomain);
Console.ReadKey();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
public class AssemblyDll
{
public string AssemblyMethod(int version)
{
//loading .dll
Assembly assembly = Assembly.LoadFrom(#"../../../../Assembly/DynamicDLL" + version + ".dll");
Type type = assembly.GetType("DynamicDLL.Dynamic");
object instance = Activator.CreateInstance(type);
MethodInfo[] methods = type.GetMethods();
//invoke method
object result = methods[0].Invoke(instance, new object[] { 5, 3 });
return result.ToString();
}
}
My .dll file comes from:
namespace DynamicDLL
{
public class Dynamic
{
public int DynamicMethod(int a, int b)
{
return a + b;
//return a - b;
}
}
}
If you want to write something like plugins and like the plugin approach, you should take a look at MEF http://msdn.microsoft.com/en/library/vstudio/dd460648.aspx
MEF allows you to use any assembly dynamically and even drop dlls into a folder and build a MEF catalog out of it.
Actually Visual Studio and uses MEF internally for extensiblility (Plugins...)
Assemblies are generally loaded into an AppDomain once and you cannot unload them once loaded.
You can create a new AppDomain and load your assemblies into this and when you release this the assemblies will be unloaded. However the caveat here is you cannot directly communicate between two AppDomain you have to marshal between the two using some other method like remoting.
There's been much wrote on this in terms of plugins and making plugins unloadable, a quick Google search presented these:
http://www.brad-smith.info/blog/archives/500
http://adrianvintu.com/blogengine/post/Unloadable-plugins.aspx
Hopefully these will aid you.