问题
Let's say I have a private, "instance," non-static, bool method in a third-party dll. All this method does is return a value. Nothing else. How would I go about intercepting calls to this method, changing it's IL OpCodes/method body, or redirecting it to an extra, overridden or derived method.
I do not want to decompile the third-party dll, manually change the source, and recompile it. I also would rather not to save the assembly to disk as this would also involve using a "recompiled" assembly instead of the original.
I basically want to be able to use the original dll - no replacements or file changes. I just want to do what I mentioned above.
Is there any way to go about this? If so, can you elaborate or post references/tutorials/etc.
Also, I know of virtual, override, and new modifiers, but remember that I: do not have the source of said third-party dll, cannot get access to the source, do not want to decompile with something such as dotPeek and recompile.
Thanks!
Edit: I forgot to mention the rest of the infrastructure: MainProgram loads ThirdPartyDLL. MainProgram also loads MyPluginDLL. I'm trying to change a method in ThirdPartyDLL from MyPluginDLL so that when MainProgram calls said method, it will call the changed method. I want to be able to do this WITHOUT saving a new assembly and restarting the MainProgram with the new assembly. Essentially, I want to do this either at startup or when MainProgram is running.
回答1:
Here is a code example to change an assembly in memory and execute methods on it, using Mono Cecil. Note that modifying and saving Assemblies is very slow. This should be done at startup of your application.
class Program
{
static void Main(string[] args)
{
Assembly assembly;
using (MemoryStream assemblyStream = new MemoryStream(File.ReadAllBytes("TargetDLL.dll")))
{
// 1. Get the reference to third-party method.
AssemblyDefinition assemblyDef = AssemblyDefinition.ReadAssembly(assemblyStream);
TypeDefinition targetDLLType = assemblyDef.Modules[0].GetType("TargetDLL.Foo");
MethodDefinition barMethod = targetDLLType.Methods[0];
// 2. Let's see what Foo.Bar returns...
assembly = Assembly.Load(assemblyStream.ToArray());
Console.WriteLine(CallMethod<int>(assembly.GetType("TargetDLL.Foo"), "Bar"));
// 3. Boot up the IL processor.
var processor = barMethod.Body.GetILProcessor();
// 4 View the unmodified IL.
Console.WriteLine("Original code");
PrintIL(processor);
// 5. Modify the code.
// 5.a Clear the method of all IL.
processor.Body.Instructions.Clear();
// 5.b Inject our custom return value.
processor.Emit(OpCodes.Ldc_I4, 1337);
processor.Emit(OpCodes.Ret);
// 6. And how does it look now?
Console.WriteLine();
Console.WriteLine("New code");
PrintIL(processor);
// 7. Save it.
assemblyDef.Write(assemblyStream);
assembly = Assembly.Load(assemblyStream.ToArray());
// 8. Result value.
Console.WriteLine(CallMethod<int>(assembly.GetType("TargetDLL.Foo"), "Bar"));
}
Console.WriteLine("END");
Console.ReadKey(true);
}
static void PrintIL(ILProcessor processor)
{
foreach (var instruction in processor.Body.Instructions)
{
Console.WriteLine(instruction);
}
}
static T CallMethod<T>(Type type, string method)
{
return (T)type.InvokeMember("Bar", BindingFlags.Static | BindingFlags.InvokeMethod | BindingFlags.Public, null, null,
null);
}
}
Add the following DLL with the name TargetDLL.dll in the same directory. Containing this code:
namespace TargetDLL
{
public static class Foo
{
public static int Bar()
{
return 0;
}
}
}
Edit
I think your error has to do with the version of Mono.Cecil. I'm using 9.5.0 from the master branch on GitHub. Download the ZIP file and build the project as required.
来源:https://stackoverflow.com/questions/21081773/c-sharp-intercept-change-redirect-a-method