C# Перехватить/изменить/перенаправить метод

Допустим, у меня есть частный, «экземпляр», нестатический метод bool в сторонней dll. Все, что делает этот метод, это возвращает значение. Ничего больше. Как мне перехватить вызовы этого метода, изменить его IL OpCodes/тело метода или перенаправить его на дополнительный, переопределенный или производный метод.

Я не хочу декомпилировать стороннюю dll, вручную менять исходники и перекомпилировать. Я также предпочел бы не сохранять сборку на диск, так как это также потребует использования «перекомпилированной» сборки вместо оригинала.

Я в основном хочу иметь возможность использовать оригинальную dll - без замен или изменений файлов. Я просто хочу сделать то, что я упомянул выше.

Есть ли способ сделать это? Если да, можете ли вы уточнить или опубликовать ссылки/учебники/и т. д.

Кроме того, я знаю модификаторы virtual, override и new, но помните, что у меня: нет исходного кода указанной сторонней dll, я не могу получить доступ к исходному коду, не хочу декомпилировать с помощью чего-то вроде dotPeek и перекомпилировать.

Спасибо!

Редактировать: я забыл упомянуть об остальной инфраструктуре: MainProgram загружает ThirdPartyDLL. MainProgram также загружает MyPluginDLL. Я пытаюсь изменить метод в ThirdPartyDLL из MyPluginDLL, чтобы, когда MainProgram вызывает указанный метод, он вызывал измененный метод. Я хочу иметь возможность сделать это БЕЗ сохранения новой сборки и перезапуска MainProgram с новой сборкой. По сути, я хочу сделать это либо при запуске, либо во время работы MainProgram.


person user3150838    schedule 12.01.2014    source источник
comment
Бьюсь об заклад, вы ищете Mono.Cecil. Учебники здесь   -  person Measuring    schedule 13.01.2014
comment
@Измерение, я знаю, что да. Но куда я смотрю? Я знаю только о ReadAssembly и SaveAssembly. Помните, что я не хочу сохранять и использовать новую сборку. Я хочу использовать тот же файл сборки/dll без сохранения измененного.   -  person user3150838    schedule 13.01.2014
comment
Вы пытаетесь перехватить ВСЕ вызовы этого метода или только ВАШИ вызовы этого метода?   -  person Pete Garafano    schedule 13.01.2014
comment
Вы можете хранить сборку в памяти и изменять ее, выполнять ее. Это в Интернете. Я нашел это раньше. Сделайте это так: загрузите сборку в память, внедрите метод в метод, который вы хотите отслеживать, и заставьте его вызывать ваш код.   -  person Measuring    schedule 13.01.2014
comment
@TheGreatCO, все звонки. Но имейте в виду, что моя конечная цель — просто изменить возвращаемое значение этого метода.   -  person user3150838    schedule 13.01.2014
comment
@ Измерение, я не могу найти больше об этом, не могли бы вы расширить, пожалуйста?   -  person user3150838    schedule 13.01.2014


Ответы (1)


Вот пример кода для изменения сборки в памяти и выполнения на ней методов с помощью Mono Cecil. Обратите внимание, что изменение и сохранение сборок происходит очень медленно. Это должно быть сделано при запуске вашего приложения.

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);
    }
}

Добавьте следующую DLL с именем TargetDLL.dll в тот же каталог. Содержит этот код:

namespace TargetDLL
{
    public static class Foo
    {
        public static int Bar()
        {
            return 0;
        }
    }
}

Изменить

Я думаю, что ваша ошибка связана с версией Mono.Cecil. Я использую 9.5.0 из основной ветки на GitHub. Загрузите ZIP-файл и создайте проект по мере необходимости.

person Measuring    schedule 13.01.2014
comment
Кроме того, пожалуйста, проверьте редактирование, которое я сделал в исходном сообщении. Надеюсь, это поможет вам понять - person user3150838; 13.01.2014
comment
Кроме того, будет ли работать ваш метод, если класс и метод нестатичны? - person user3150838; 13.01.2014
comment
@user3150838 user3150838 Да, вы можете найти любой тип с помощью Reflection, если укажете право BindingFlags. Произнесите: private int Hello() = BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.NonPublic - person Measuring; 13.01.2014
comment
Спасибо, но я все еще не могу заставить этот метод работать. Ошибка все еще сохраняется. Даже если бы ошибки не было, я не думаю, что это сработает с моей инфраструктурой. Подробнее об инфраструктуре в исходном посте - person user3150838; 13.01.2014