I looking for an API to extract a resource from an assembly but don’t want to load the assembly itself, not even in a separate AppDomain.
Is there a way of reading Assembly ManifestResourceStreams that isn’t Assembly.GetManifestResourceStream, which requires loading the Assembly?
Example of what I’m looking for:
var r = new AssemblyResourceReader(“Test.dll”);
Stream s = r.GetResourceStream(“image.png”);
Found an answer: the System.Reflection.Metadata package has such utilities
using System.IO;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
public static class AssemblyMetadataReader
{
public static byte[] GetManifestResource(string asmPath, string resourceName)
{
using (var fs = File.OpenRead(asmPath))
{
PEReader per = new PEReader(fs);
MetadataReader mr = per.GetMetadataReader();
foreach (var resHandle in mr.ManifestResources)
{
var res = mr.GetManifestResource(resHandle);
if (!mr.StringComparer.Equals(res.Name, resourceName))
continue;
PEMemoryBlock resourceDirectory = per.GetSectionData(per.PEHeaders.CorHeader.ResourcesDirectory.RelativeVirtualAddress);
var reader = resourceDirectory.GetReader((int)res.Offset, resourceDirectory.Length - (int)res.Offset);
uint size = reader.ReadUInt32();
return reader.ReadBytes((int)size);
}
}
return null;
}
}
Found this from another answer :
You can use some API, for example CCI that allows you to look inside managed dll(s) without loading it using reflection mechanism.
Ref : how to read the assembly manifest without loading the .dll
Related
I am trying to modify assembly before using it.
Main file:
using IlGenTestTarget;
using Lokad.ILPack;
using System.Reflection;
using Mono.Cecil;
using IlGenTest;
Assembly inAssembly = Assembly.GetAssembly(typeof(Class1));
AssemblyGenerator assemblyGenerator = new AssemblyGenerator();
byte[] b = assemblyGenerator.GenerateAssemblyBytes(inAssembly);
AssemblyDefinition assemblyDefinition = AssemblyDefinition.ReadAssembly(new MemoryStream(b));
foreach (ModuleDefinition module in assemblyDefinition.Modules) {
IlGenTestUtils.RemoveRemovedElements(module.Types);
foreach (TypeDefinition type in module.Types) {
IlGenTestUtils.RemoveRemovedElements(type.Methods);
IlGenTestUtils.RemoveRemovedElements(type.Fields);
}
}
MemoryStream ms = new MemoryStream();
assemblyDefinition.Write(ms);
byte[] res = ms.ToArray();
Assembly resAssembly = Assembly.Load(res);
Module resModule = resAssembly.GetModules()[0];
Type resType = resModule.GetType("IlGenTestTarget.Class1");
MethodInfo resMethod = resType.GetMethod("Method1");
resMethod.Invoke(null, null);
IlGenTestUtils:
using Mono.Cecil;
using Mono.Collections.Generic;
namespace IlGenTest
{
public class IlGenTestUtils
{
public static List<T> GetRemovedElements<T>(Collection<T> collection) where T : ICustomAttributeProvider
{
return collection
.Where(t => t.CustomAttributes.Any(attr => attr.AttributeType.Name == "RemovedAttribute"))
.ToList();
}
public static void RemoveRemovedElements<T>(Collection<T> collection) where T : ICustomAttributeProvider
{
foreach (T t in GetRemovedElements<T>(collection))
{
collection.Remove(t);
}
}
}
}
When I put breakpoint on Method1, everything works fine, but progam is not paused on it. When I invoke Method1 directly, without creating new assembly, program is paused on breakpoint as expected. Is there a way to make breakpoints work with dynamic assembly?
The link between "source line at which a breakpoint is placed" and "assembly file contents" is defined by the .pdb file for the assembly. After modifying the assembly, I would actually be surprised if the link between them would still work.
You would need to also rebuild the .pdb file, which seems hard if not impossible.
In a MVC controller I use AssemblyLoadContext.Default.LoadFromAssemblyPath(pathToDll); to load an assembly. I want to delete or replace the given .dll file during runtime. This is not possible because the file is not disposed. Is there any way to dispose the .dll file? There are solutions using the AppDomain class, which is not available in asp.net core.
Background:
The user is able to upload a custom .dll file which contains implementations of a given interface. The user should also be able to replace his file. I use the following code in a controller to access the implementations:
var conventions = new ConventionBuilder();
conventions
.ForTypesDerivedFrom<IPluginContract>()
.Export<IPluginContract>()
.Shared();
var configuration = new ContainerConfiguration().WithAssembliesInPath(path, conventions);
using (var container = configuration.CreateContainer())
{
var plugins = container.GetExports<IPluginContract>();
return plugins;
}
With
public static ContainerConfiguration WithAssembliesInPath(
this ContainerConfiguration configuration,
string path, AttributedModelProvider conventions,
SearchOption searchOption = SearchOption.TopDirectoryOnly)
{
var fileNames = Directory
.GetFiles(path, "*.dll", searchOption);
List<Assembly> assemblies = new List<Assembly>();
foreach (string relativePath in fileNames)
{
Assembly assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(Path.GetFullPath(relativePath));
assemblies.Add(assembly);
}
configuration = configuration.WithAssemblies(assemblies, conventions);
return configuration;
}
OPTION 1:
Try loading dll with method LoadFromStream, then you can remove dll without exceptions.
Ex:
foreach (string relativePath in fileNames)
{
using (var fs = File.Open(relativePath , FileMode.Open))
{
Assembly assembly = AssemblyLoadContext.Default.LoadFromStream(fs);
assemblies.Add(assembly);
}
File.Delete(relativePath); //It doesn't throw exception
}
NOTE: tested with Net Core 3.1 but could work with previous versions.
OPTION 2:
If you have a problem when try to reload assemblies with LoadFromStream you should try to call AssemblyLoadContext.Default.Unload() before to LoadFromStream()
But I'm not sure if it works with AssemblyLoadContext.Default, so if you still keep any exception you should create any class that inherit from AssemblyLoadContext with flag isCollectible to true like this:
public class PluginLoadContext : AssemblyLoadContext
{
public PluginLoadContext() : base(isCollectible: true)
{
}
}
And the code should be:
//var pluginContext = new PluginLoadContext(); //In some place to call unload later
pluginContext.Unload();
foreach (string relativePath in fileNames)
{
using (var fs = File.Open(relativePath , FileMode.Open))
{
Assembly assembly = pluginContext.LoadFromStream(fs);
assemblies.Add(assembly);
}
File.Delete(relativePath); //It doesn't throw exception
}
OPTION 3:
There is another option that override Load method of your custom PluginLoadContext, you only need to load your entry dll, and the reference dll is knew with deps.json file of your entry dll.
In this example is using MemoryStream to prevent attach plugin dll.
public class PluginLoadContext : AssemblyLoadContext
{
private AssemblyDependencyResolver _resolver;
public PluginLoadContext(string pluginPath) : base(isCollectible: true)//isCollectible doesn't appear in netstandard2.1
{
_resolver = new AssemblyDependencyResolver(pluginPath);
}
protected override Assembly Load(AssemblyName assemblyName)
{
string assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
if (assemblyPath != null)
{
//Using MemoryStream to prevent attach dll to this .exe
MemoryStream ms = new MemoryStream();
using (var fs = File.Open(assemblyPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
fs.CopyTo(ms);
}
ms.Position = 0;
return LoadFromStream(ms);
}
return null;
}
}
Then you can load your entry plugin dll like this.
var dllPath = "<path to your entry dll>" // dll and deps.json file together .
var pc = new PluginLoadContext(dllPath);
var assembly = pc.LoadFromAssemblyName(new AssemblyName(Path.GetFileNameWithoutExtension(dllPath)));
//You can load a reference dll too if you need it
var referenceAssembly = pc.LoadFromAssemblyName(new AssemblyName(Path.GetFileNameWithoutExtension("<path of reference dll>")));
REF:
https://learn.microsoft.com/en-us/dotnet/core/tutorials/creating-app-with-plugin-support#load-plugins
When you load a dll into your application domain, this dll is not free before the appDomain is being destroyed (i.e. your process is stopped) there is no dispose for a dll.
For references on how to reach your desired functionality please have a look at these questions that are answered already:
Using AppDomain to dynamically load and unload dll
Hot unload and reload of a dll used by an application
It sounds very similar to MEF ( Managed Extensibility Framework ). It allows inject DLL's and also helps to manage the lifecycle.
Example:
public static class MefInjection
{
private static CompositionContainer mycontainer;
public static CompositionContainer MyContainer
{
get
{
if (mycontainer == null)
{
var catalog =
new DirectoryCatalog(".", "MyMEFProject.*");
mycontainer = new CompositionContainer(catalog);
}
return mycontainer;
}
}
}
The preceding code will grab all the exported values from all the assemblies in the same directory starting with "MyMEFProject". Then you can use mycontainer to get loaded DLL's functionality.
I have a set of XSDs that validate against XMLSPY and against Java code. I need to bring this set of XSDs as an embedded resource in Visual Studio 2012 .net. Unfortunately I am getting an error that a global element has already been declared when trying to resolve them with a custom XmlResolver to deal with the xsd:include. Error is strange because the element is declared only once.
Visual Studio Solution
|----------- Visual Studio Project
|----------- Schemas (Embedded Resource)
|----------- Directory A
|------------ set of XSDs that are referenced by XSDs in Directory B and to a globaltype definition file located in this directory
|----------- Directory B
|------------- set of XSDs that reference each other and those in Directory A, the XSD call from the main is located here
Validating Util Class
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Schema;
namespace ABC.XYZ.Utils
{
public static class XmlUtil
{
private static bool isValid;
public static bool ValidateXml(string targetNamespace, string schemaUri, string xml)
{
isValid = true;
var schemaReaderSettings = new XmlReaderSettings() { ValidationType = ValidationType.Schema };
schemaReaderSettings.ValidationEventHandler += MyValidationHandler;
schemaReaderSettings.Schemas.XmlResolver = new XmlResourceResolver();
var schemaReader = XmlReader.Create(GetSchemaStream(schemaUri), schemaReaderSettings);
schemaReaderSettings.Schemas.Add(targetNamespace, schemaReader);
var x = XElement.Parse(xml);
var sr = new System.IO.StringReader(x.ToString());
XmlReader validatingReader = XmlReader.Create(sr, schemaReaderSettings);
while (validatingReader.Read())
{
}
validatingReader.Close();
return isValid;
}
private static void MyValidationHandler(object sender, ValidationEventArgs args)
{
Console.WriteLine("***Validation error");
Console.WriteLine("\tSeverity:{0}", args.Severity);
Console.WriteLine("\tMessage:{0}", args.Message);
isValid = false;
}
private static Stream GetSchemaStream(string relativeFileName)
{
var resourceFileName =
Assembly.GetExecutingAssembly()
.GetManifestResourceNames()
.FirstOrDefault(p => p.EndsWith(relativeFileName));
return Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceFileName);
}
}
}
Custom XmlResolver
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
namespace ABC.XYZ.Utils
{
public class XmlResourceResolver : XmlResolver
{
public const string AssemblyDefaultNamespace = "ABC.XYZ";
public const string SchemasNamespace = "Schemas";
public override Uri ResolveUri(Uri baseUri, string relativeUri)
{
var result = new UriBuilder("res://", AssemblyDefaultNamespace, -1, SchemasNamespace.Replace(".", "/"));
result.Path += "/" + relativeUri.Replace("../", "/").TrimStart('/');
return result.Uri;
}
public override object GetEntity(Uri absoluteUri, string role, Type ofObjectToReturn)
{
if (absoluteUri.Scheme != "res") return null;
Debug.WriteLine("Loading resource based on location {0}", absoluteUri);
var assembly = Assembly.GetExecutingAssembly();
var name = String.Format(CultureInfo.InvariantCulture, "{0}{1}",
absoluteUri.Host,
absoluteUri.GetComponents(UriComponents.PathAndQuery, UriFormat.Unescaped).Replace("/", "."));
// try for an exact match based on schemaLocation hint path
var resourceName = (from x in assembly.GetManifestResourceNames()
where name.Equals(x, StringComparison.OrdinalIgnoreCase)
select x).FirstOrDefault();
// if not match based on filename alone
if (resourceName == null)
{
var schemaDocumentName = Path.GetFileName(absoluteUri.AbsolutePath);
Debug.WriteLine("Unable to locate exact match, looking for match based on filename {0}", schemaDocumentName);
resourceName = (from x in assembly.GetManifestResourceNames()
where x.Contains(SchemasNamespace) &&
x.EndsWith("." + schemaDocumentName, StringComparison.OrdinalIgnoreCase)
select x).FirstOrDefault();
}
Debug.WriteLine("Loading resource {0}", resourceName);
var stream = assembly.GetManifestResourceStream(resourceName);
return stream;
}
}
}
Any insights into this problem with be greatly appreciated.
XSD 1.0 encourages but does not require validators to detect multiple inclusions (or imports) of the same schema document and include them only once.
The result is that including the same schema document more than once from multiple other schema documents is the simplest way to create interoperability nightmares with XSD. (Not the only way, just the simplest way.) If you own the schema documents, segregate all inclusions and all schema-location information on imports into a driver file and delete all includes and all schema-location hints on imports from the 'normal' schema documents.
The problem is that when you're doing it with XMLSpy, the XSDs are files in the file system; what's happening then, for each file there's a base URI, and therefore resolvers will use that information to ensure that once an XSD is loaded, the same one is not loaded again, based on URI compare.
Now, the way you're doing it by loading as a stream from an assembly, all that information is gone (your stream doesn't have a base URI). Your resolver will keep loading the same XSD over and over again, from different places, thus creating this clash.
All the XSD processors I know, do not employ any other means to filter multiple inclusions of the same XSD content, but base source URI.
In .NET, the easiest way might be (again, depending on how complex your graph is) to try the solution in this post; the whole idea is to provide a base URI, which should give the info required to avoid multiple inclusions.
Another alternative might be to make sure in your custom resolver that for any given URI you're resolving, you only return once a stream (return null in all other cases). This is guaranteed to work as long as you're not using xsd:redefine composition (in which case the solution is to make a topological sort of the schema file graph and ensure all xsd:redefines are loaded first).
To #CMSperbergMcQueen point, an approach that is guaranteed to work is to refactor ALL the XSDs such that there's only one XSD embedded resource per namespace; each XSD would have all imports removed (technique called "dangling"). Add those XSDs to an XML Schema set as independent XSDs and compile. Unless you run into a .NET bug, the result should be a compiled XmlSchemaSet.
In my project I need to use plugins. But to use these in my project I need to import an reference of the plugin. Since I can't know how many or which plugins the project uses beforehand I would like to import them dynamically in my project.
String path = Application.StartupPath;
string[] pluginFiles = Directory.GetFiles(path, "*.dll");
ipi = new IPlugin[pluginFiles.Length];
Assembly asm;
for (int i = 0; i < pluginFiles.Length; i++)
{
string args = pluginFiles[i].Substring(
pluginFiles[i].LastIndexOf("\\") + 1,
pluginFiles[i].IndexOf(".dll") -
pluginFiles[i].LastIndexOf("\\") - 1);
asm = Assembly.LoadFile(pluginFiles[i]);
Type[] types = asm.GetTypes();
In this code example I searched all the .dll files and put them into a string list.
But how can I now load all these .dll files? Or is there a way to use these .dll files without really importing them?
The MEF (Managed Extensibility Framework) Method:
You'll want to add references to System.ComponentModel.Composition to your projects that utilize the import/export functionality of MEF.
First, the bootstrapper/loader (in my case, I just added it to the Main class).
Program.cs:
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using MEFContract;
namespace ConsoleApplication5
{
class Program
{
static void Main(string[] args)
{
var prgm = new Program();
// Search the "Plugins" subdirectory for assemblies that match the imports.
var catalog = new DirectoryCatalog("Plugins");
using (var container = new CompositionContainer(catalog))
{
// Match Imports in "prgm" object with corresponding exports in all catalogs in the container
container.ComposeParts(prgm);
}
prgm.DoStuff();
Console.Read();
}
private void DoStuff()
{
foreach (var plugin in Plugins)
plugin.DoPluginStuff();
}
[ImportMany] // This is a signal to the MEF framework to load all matching exported assemblies.
private IEnumerable<IPlugin> Plugins { get; set; }
}
}
The IPlugin interface is the contract between the imports & exports. All plugins will implement this interface. The contract is pretty simple:
IPlugin.cs:
namespace MEFContract
{
public interface IPlugin
{
void DoPluginStuff();
}
}
Finally, you can create as many plugins as you like in different assemblies. They must implement the contract interface and also be decorated with the "Export" attribute to indicate to MEF that they should be matched up with any corresponding imports. Then drop the dlls in a "Plugins" folder (this folder should reside in the same location as the executable). Here's a sample plugin:
Plugin.cs:
using System;
using System.ComponentModel.Composition;
using MEFContract;
namespace Plugin
{
[Export(typeof(IPlugin))]
public class Plugin : IPlugin
{
public void DoPluginStuff()
{
Console.WriteLine("Doing my thing!");
}
}
}
Let's assume for the sake of simplicity that all of the implementations of IPlugin have default constructors (public and no parameters).
That said, you really want to find all types that implement this interface and create an instance of them. You're on the right track somewhat, but you can simplify this tremendously with a little LINQ:
String path = Application.StartupPath;
string[] pluginFiles = Directory.GetFiles(path, "*.dll");
ipi = (
// From each file in the files.
from file in pluginFiles
// Load the assembly.
let asm = Assembly.LoadFile(file)
// For every type in the assembly that is visible outside of
// the assembly.
from type in asm.GetExportedTypes()
// Where the type implements the interface.
where typeof(IPlugin).IsAssignableFrom(type)
// Create the instance.
select (IPlugin) Activator.CreateInstance(type)
// Materialize to an array.
).ToArray();
That said, you might be better off using a dependency injection framework; they usually allow for dynamic loading and binding to interface implementations in assemblies not referenced at compile time.
Also, while a bit convoluted (in my opinion), you might want to look at the System.AddIn namespaces, as they are built specifically for this purpose. However, the dependency injection route is usually much easier if you don't have to worry about version control of contracts and the like.
I have an application which can not only load plugin at runtime, but also hot-load and unload them as user drop them in the folder, take them out or erase them. So, when I need to recompile my plugin, I don't need to re-launch my application. In my case, all plugin derive from the Plugin abstract, so they are easy to find in .DLL.
Here's my loading method:
private static void LoadPlugins(FileInfo file)
{
try
{
Assembly assembly = Assembly.LoadFrom(file.FullName);
foreach (Type type in assembly.GetTypes())
{
if (type.IsSubclassOf(typeof(Plugin)) && type.IsAbstract == false)
{
Plugin b = type.InvokeMember(null,
BindingFlags.CreateInstance,
null, null, null) as Plugin;
plugins.Add(new PluginWrapper(b, file));
b.Register();
}
}
}
catch (ReflectionTypeLoadException ex)
{
StringBuilder sb = new StringBuilder();
foreach (Exception exSub in ex.LoaderExceptions)
{
sb.AppendLine(exSub.Message);
if (exSub is FileNotFoundException)
{
FileNotFoundException exFileNotFound = exSub as FileNotFoundException;
if (!string.IsNullOrEmpty(exFileNotFound.FusionLog))
{
sb.AppendLine("Fusion Log:");
sb.AppendLine(exFileNotFound.FusionLog);
}
}
sb.AppendLine();
}
string errorMessage = sb.ToString();
Log.Error("Plugins Manager", errorMessage);
}
}
I have a few dll files and I want to export all public classes with methods separated by namespaces (export to html / text file or anything else I can ctrl+c/v in Windows :) ).
I don't want to create documentation or merge my dlls with xml file. I just need a list of all public methods and properties.
What's the best way to accomplish that?
TIA for any answers
Very rough around the edges, but try this for size:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
namespace GetMethodsFromPublicTypes
{
class Program
{
static void Main(string[] args)
{
var assemblyName = #"FullPathAndFilenameOfAssembly";
var assembly = Assembly.ReflectionOnlyLoadFrom(assemblyName);
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += new ResolveEventHandler(CurrentDomain_ReflectionOnlyAssemblyResolve);
var methodsForType = from type in assembly.GetTypes()
where type.IsPublic
select new
{
Type = type,
Methods = type.GetMethods().Where(m => m.IsPublic)
};
foreach (var type in methodsForType)
{
Console.WriteLine(type.Type.FullName);
foreach (var method in type.Methods)
{
Console.WriteLine(" ==> {0}", method.Name);
}
}
}
static Assembly CurrentDomain_ReflectionOnlyAssemblyResolve(object sender, ResolveEventArgs args)
{
var a = Assembly.ReflectionOnlyLoad(args.Name);
return a;
}
}
}
Note: This needs refinement to exclude property getters/setters and inherited methods, but it's a decent starting place
Have you had a look at .NET Reflector from RedGate software. It has an export function.
You can start here with Assembly.GetExportedTypes()
http://msdn.microsoft.com/en-us/library/system.reflection.assembly.getexportedtypes.aspx