Inject a class with a method using dnlib - c#

I'm trying to inject a class with a method in a file. I'm aware that there is a working solution in ConfuserEx's source code but it kinda requires editing dnlib's code which I want to avoid.
ModuleDef manifestModule = assembly.ManifestModule;
Importer importer = new Importer(manifestModule);
IMethod method = importer.Import(typeof(AntiDumpRuntime).GetMethod("Initialize"));
TypeDef type = new TypeDefUser("AntiDump");
type.Methods.Add(method.ResolveMethodDefThrow()); // dnlib.DotNet.MemberRefResolveException: 'Could not resolve method: System.Void Obfuscator.Core.Protections.AntiDump.AntiDumpRuntime::Initialize() (Obfuscator, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null)'
manifestModule.Types.Add(type);
In the above snippet I tried to do that via Importer class, but it throws an exception right on the commented row.
Here is the ConfuserEx's solution:
https://github.com/yck1509/ConfuserEx/blob/master/Confuser.Core/Helpers/InjectHelper.cs
And here is the modification needed to be done in dnlib: https://github.com/yck1509/dnlib/blob/master/src/DotNet/Importer.cs#L72

The newest version of dnlib contain the changes you require to get it working. The importer has an constructor that allows setting a ImportMapper implementation. Using this you can properly inject code with the default version of dnlib.
I'm maintaining a fork of ConfuserEx that uses the dnlib without modifications. So it works fine.

Related

TypeForwardedTo, explicitly implemented interfaces and events

I am trying to refactor a code base. This requires moving types to different assemblies to fix some dependency issues. I want to avoid requiring my customers to recompile as a result of these changes. I've noticed that interfaces that are explicitly implemented and declare events do not forward correctly. For example:
Foo.dll defines:
public interface IFooInterface
{
void Foo();
event EventHandler FooEvent;
}
My customer's FooProgram.exe depends on Foo.dll and defines:
public class Foo : IFooInterface
{
event EventHandler IFooInterface.FooEvent
{
add { }
remove { }
}
void IFooInterface.Foo() { }
}
Now I move the type. I create a new assembly NewFoo.dll. I move IFooInterface from the old assembly to the new assembly and put it under the same namespace. Foo.csproj takes a project reference to NewFoo.csproj and I add the forwarding attribute:
[assembly: TypeForwardedTo(typeof(IFooInterface))]
I place Foo.dll and NewFoo.dll in the bin directory for Foo.exe and Foo.exe errors with:
Unhandled Exception: System.TypeLoadException: Method 'add_FooEvent' in type 'FooProgram.Foo' from assembly 'FooProgram, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation.
at FooProgram.Program.Main(String[] args)
Why does this happen? If I implicitly implement the interface on Foo it works, but that doesn't do me any good as it means anyone who wrote vb.net code against my library will break. What other constructs have this problem with Type Fowrarding? Is there a way to get the behavior I want?
It looks like this is a bug in the old version of .NET Framework we're using (4.5). I upgraded to 4.8 and it works as expected. I found this blog post by Rick Strahl that describes how TypeForwardTo is what makes .NET Standard capable of resolving to the correct targets. This made me realize it must work for all types on a more modern version of the framework.

Why are these Type objects not equal?

I have an application where I load plugins by reading their DLL file and then loading the bytes using AppDomain.CurrentDomain.Load(bytes). Note that the application and the plugin are loaded in the same AppDomain. The plugin contains several classes which register themselves in a service locator system using a static constructor.
Later, my main application tries to find and instantiate one of these service classes using the service locator, but it cannot find the class. Upon manual inspection, I can see that the registry entry is present in the locator, so it was registered, but for some unknown reason the types aren't equal.
I then put a breakpoint at the place where the type is registered and discovered the following weirdness:
How can typeof(IViewFor<CompactDashboardViewModel>) not be equal to itself?
I then tested a few more things:
t == t
true
typeof(IViewFor<CompactDashboardViewModel>) == typeof(IViewFor<CompactDashboardViewModel>)
true
t.AssemblyQualifiedName == typeof(IViewFor<CompactDashboardViewModel>).AssemblyQualifiedName
true
In fact, everything about these 2 Type objects seems to be equal, except the m_handle and m_cache fields.
typeof(IViewFor<CompactDashboardViewModel>).TypeHandle
{System.RuntimeTypeHandle}
Value: 0x08690784
m_type: {Name = "IViewFor`1" FullName = "ReactiveUI.IViewFor`1[[PluginMTSICS.ViewModel.CompactDashboardViewModel, PluginMTSICS, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]"}
t.TypeHandle
{System.RuntimeTypeHandle}
Value: 0x0f8cf5a8
m_type: {Name = "IViewFor`1" FullName = "ReactiveUI.IViewFor`1[[PluginMTSICS.ViewModel.CompactDashboardViewModel, PluginMTSICS, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]"}
Does anybody know what is happening here? I am using .NET 4.7.1.
I am trying to create an MCVE, but unsuccessfully so far.
Maybe this works:
Type t = typeof(IViewFor<CompactDashboardViewModel>);
//this should evaluate to true:
bool result = t.Equals(typeof(IViewFor<CompactDashboardViewModel>));
Type.Equals docs:
https://msdn.microsoft.com/en-us/library/3ahwab82(v=vs.110).aspx
EDIT:
After reading this post Type Checking: typeof, GetType, or is? i would expect this to work:
Type t = typeof(IViewFor<CompactDashboardViewModel>);
//this should evaluate to true:
bool result = t is IViewFor<CompactDashboardViewModel>;
OK, so I fixed the issue. Here is what I did:
My main application had a reference to a library project, which in turn referenced the plugin project. This probably caused the assembly to be loaded twice, in different load contexts (see links below for more info). I removed the reference. The problem was not fixed, and now weird stuff was happening such as typeof(CompactDashboardViewModel) == null.
My plugin loading code originally used appdomain.Load(bytes). I replaced this with Assembly.LoadFrom. typeof() now worked correctly, and works as expected. However, Type.GetType() still returns null sometimes.
I replaced Assembly.LoadFrom with Assembly.Load and added my plugin directory to the probing path using the <probing> tag in app.config. Everything works correctly now, however I can't load the plugins by filepath, as Assembly.Load requires the assembly name. Not ideal, but I can live with that.
Useful sources:
Best practices for assembly loading
<probing> element
Type.GetType() docs
LoadFrom isolation
Choosing a binding context

ComposeParts method not working

I've got an ITagger and an IWpfTextViewMargin, both are exported as MEF components. I want to import the ITagger in my Margin code, and then use some members in that Tagger.
Now I tried to use ComponentContainer in the Margin class, then import the IViewTaggerProvider. I used the following code, which can be found in many MEF tutorials
[Import(typeof(IViewTaggerProvider))]
public IViewTaggerProvider vt_provider { get; set; }
var catalog = new AggregateCatalog();
catalog.Catalogs.Add(new AssemblyCatalog(typeof(TestMargin).Assembly));
_container = new CompositionContainer(catalog);
//Fill the imports of this object
try
{
this._container.ComposeParts(this);
}
catch (CompositionException compositionException)
{
System.Diagnostics.Trace.WriteLine(compositionException.Message);
}
and the export code.
[Export(typeof(IViewTaggerProvider))]
[ContentType...
The exported class is defined in another namespace but same assembly.
Here I got problem that ComposeParts(this) throws ImportCardinalityMismatchException. I don't know why the parameter is this. I tried to pass the catalog to it, there is no exception but the import is also null. I also referred to debug mef failures and believe that the exported class has the right contract name and export type identity.
After checking the assembly with Visual MEFx and debugging, I found that probably it's because the IViewTaggerProvider imports a Visual Studio IClassificationTypeRegistryService, which is also an MEF part and results in a rejection of the IViewTaggerProvider.
[Primary Rejection]
[Exception] System.ComponentModel.Composition.ImportCardinalityMismatchException: No valid exports were found that match the constraint '((exportDefinition.ContractName == "Microsoft.VisualStudio.Text.Classification.IClassificationTypeRegistryService") AndAlso (exportDefinition.Metadata.ContainsKey("ExportTypeIdentity") AndAlso "Microsoft.VisualStudio.Text.Classification.IClassificationTypeRegistryService".Equals(exportDefinition.Metadata.get_Item("ExportTypeIdentity"))))', invalid exports may have been rejected.
So one solution is to add the assembly that exports IClassificationTypeRegistryService. It's a Visual Studio core editor service but I cannot find which assembly exports it. Anyone knows this?
Or any better possible solutions?
Try VisualMEFx. Here is a short blog entry about getting started https://ihadthisideaonce.com/2012/02/22/getting-started-with-visual-mefx/. Once you have it up and running, use VisualMEFx to load the TestMargin assembly and see if any IViewTaggerProvider is exported from that assembly.
Also remember that ImportCardinalityMistmatch doesn't only mean that an export is missing. It can also mean that there are too many exports available that can satisfy the import and MEF has no way of choosing which one to use. So when you are examining your composition in VisualMEFx, check to see if there are too many.
This parameter:
void Bootstrap()
{
var catalog = new AggregateCatalog();
catalog.Catalogs.Add(new AssemblyCatalog(typeof(TestMargin).Assembly));
_container = new CompositionContainer(catalog);
//Fill the imports of this object
try
{
var objectToSatisfy = this;
// var objectToSatifsy = new SomeOtherObjectWithImports();
this._container.ComposeParts(objectToSatisfy);
}
catch (CompositionException compositionException)
{
System.Diagnostics.Trace.WriteLine(compositionException.Message);
}
}
When you call ComposeParts you pass an object to the method. MEF will take the object that you pass and see if there are any imports on it that need to be satisfied. If it finds any imports, it will look in the catalog and try to satisfy them. You can pass any object you want to the ComposeParts method. So I've modified your sample code a little to show two different options. One option is to create some object that needs to be satisfied, and then give it to the container for composition. This is what I have done in the commented out line var objectToSatisfy = new SomeOtherObjectWithImports(). But it is often the case that the object we want to compose is the same object that's calling ComposeParts. So we don't need to create a new object to pass to the container, we already have the object, we just need a reference to it. In C# we can get a reference to the current object instance using the keyword this (in VB.NET the keyword is Me). So, when we want to satisfy imports on the same object that is calling ComposeParts, we can do so by using the this reference as the argument to ComposeParts.
The argument to the ComposeParts method is a parameter array. Informally, this just means that when you write container.ComposeParts(this) it is interpreted as if you had written container.ComposeParts(new object[] { this }). In practice this means you can pass multiple objects to MEF at once, like this:
container.ComposeParts(this, objectToSatifsy, thirdObjectToCompose);
If the object calling ComposeParts has no imports on it, then you should not be using this as the argument. Instead, create an object of the type that you want to compose and pass that to the method. Also, unless all the parts that you want to compose are available in the TestMargin assembly, you need to create more AssemlbyCatalogs for the assemblies that do provide the parts and add them to your AggregateCatalog.

Unable to load executing assembly into new AppDomain, FileNotFoundException

I am trying to host a text template class proxy inside a new AppDomain.
I have some old scripting code that does something similar, that contains this working code:
_ScriptAppDomain = AppDomain.CreateDomain(scriptDomainFriendlyName);
_ScriptProxy = (IScriptEngineProxy)_ScriptAppDomain.CreateInstanceAndUnwrap(
Assembly.GetExecutingAssembly().FullName,
"LVK.Scripting.ScriptEngineProxy");
However, when I try this with my new class, with the following
_TemplateDomain = AppDomain.CreateDomain(templateDomainFriendlyName);
_TemplateProxy = (ITemplateProxy)_TemplateDomain.CreateInstanceAndUnwrap(
Assembly.GetExecutingAssembly().FullName,
"TextTemplate.TemplateProxy");
I just get "FileNotFoundException", with the following details:
Could not load file or assembly 'TextTemplate, Version=1.0.0.0, Culture=neutral, PublicKeyToken=bb70a2e62a722ace' or one of its dependencies. The system cannot find the file specified.
What am I missing?
Basically, I have a Template class in the TextTemplate namespace (and assembly), which tries to load a TemplateProxy class (descending from MarshalByRefObject) into the new appdomain, but it appears my main assembly is not loaded into this domain.
This works if I use the older code, but not with this new one, but I can't spot the difference.
Here's some more details:
Assembly is not registered with the GAC (neither was the old one, which works)
I have not overridden any AssemblyResolve event (neither did the old one, which works)
I'm not averse to handling the AssemblyResolve event, if that is what is needed. I just found it odd that my old code worked, and this didn't.
Assumying your assembly is in the same directory as your current application base, try specifying Application Base:
AppDomain.CreateDomain(templateDomainFriendlyName, null,
new AppDomainSetup
{
ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase
});
If you don't know what you are doing, the best way to create a new domain is to copy all settings from the current one, like this:
var newDomain = AppDomain.CreateDomain("NAME",
AppDomain.CurrentDomain.Evidence,
AppDomain.CurrentDomain.SetupInformation);
I had similar issues, and making a copy resolved them for me

Can't load multiple MEF parts

I have a Winforms desktop application that is loading multiple MEF parts with the same Interface type.
Problem:
When I try to load more than one of the same type I get the following exception:
The composition remains unchanged. The changes were rejected because of the following error(s): The composition produced a single composition error. The root cause is provided below. Review the CompositionException.Errors property for more detailed information.
1) No valid exports were found that match the constraint '((exportDefinition.ContractName = "BOCA.TaskPilot.Common.Extensions.IFolderViewExtension") && (exportDefinition.Metadata.ContainsKey("ExportTypeIdentity") && "BOCA.TaskPilot.Common.Extensions.IFolderViewExtension".Equals(exportDefinition.Metadata.get_Item("ExportTypeIdentity"))))', invalid exports may have been rejected.
Resulting in: Cannot set import 'TaskPilot.Windows.MainForm.FolderViewExtension (ContractName="BOCA.TaskPilot.Common.Extensions.IFolderViewExtension")' on part 'TaskPilot.Windows.MainForm'.
Element: TaskPilot.Windows.MainForm.FolderViewExtension (ContractName="BOCA.TaskPilot.Common.Extensions.IFolderViewExtension") --> TaskPilot.Windows.MainForm
Here is the code to load the parts:
AggregateCatalog catalog = new AggregateCatalog();
catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
//string myExecName = Assembly.GetExecutingAssembly().Location;
//string myPath = Path.GetDirectoryName(myExecName);
catalog.Catalogs.Add(new DirectoryCatalog(#"C:\Data\TaskPilot\Development\Source\BOCA.TaskPilot.FolderView\bin\Debug"));
catalog.Catalogs.Add(new DirectoryCatalog(#"C:\Data\TaskPilot\Development\Source\BOCA.TaskPilot.TaskView\bin\Debug"));
// Uncomment below line and it works without exceptions raised
//catalog.Catalogs.Add(new DirectoryCatalog(#"C:\Data\TaskPilot\Development\Source\BOCA.FileManager\bin\Debug"));
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
Here's the code at the class for each of the MEF parts:
[Export(typeof(IFolderItemsViewExtension))
public partial class TaskTreeView : DevExpress.XtraEditors.XtraUserControl, IFolderItemsViewExtension, IPartImportsSatisfiedNotification]
Here's the Import used on the Main form:
[ImportMany(AllowRecomposition = true)]
private IEnumerable<IFolderItemsViewExtension> TaskViewExtensions = null;
If I uncomment the last Catalog.Catalogs.Add line it throws the exception. If I run it without that it runs just fine. That line loads a different user control that implements the IFolderItemsViewExtension Interface. I've tried to just load a dummy project that all it has is the user control and that interface and I still get the same exception. No matter what I do I still get this exception.
It seems that everything runs fine as long as I'm not loading more than one of the same type of MEF part export.
This is using the latest version of 2009.22.10.0 of the System.ComponentModel.Composistion from the MEF download.
The error indicates that it can't find an export of type IFolderViewExtension. Note that this is different from the import of IFolderItemsViewExtension you have shown.
My guess is that the problem is not that you have multiple IFolderItemsViewExtensions, but that you have multiple IFolderViewExtensions, or there is some other contract that you have more than one of that you are using with an import that requires exactly one.
This may be caused because you have the same assembly in more than one of your directory catalogs. It is easy for this to happen if you have a reference to an assembly and copy local is set to true.
I guess might have more than one export statement in your Export class.
I was facing the same issue and this solved when i removed all other expert statement from that export class. and now it is working fine.

Categories

Resources