Как извлечь код класса IL из загруженной сборки и сохранить на диск?

Как мне извлечь IL-код для классов, которые генерируются во время выполнения путем отражения, чтобы я мог сохранить его на диск? Если вообще возможно. У меня нет контроля над частью кода, который генерирует эти классы.

В конце концов, я хотел бы загрузить этот IL-код с диска в другую сборку.

Я знаю, что могу сериализовать/десериализовать классы, но я хочу использовать чисто IL-код. Я не беспокоюсь о последствиях для безопасности.

Запуск моно 2.10.1


person Steven Ross    schedule 02.04.2011    source источник
comment
Это просто неправильно, код != данные.   -  person Hans Passant    schedule 02.04.2011
comment
Как прокомментировал Ханс, IL не будет содержать имя пользователя и пароль. Даже сериализация - плохая идея для имени пользователя и пароля. Вероятно, объяснив свою настоящую проблему, вы поможете сообществу найти лучшее решение и ответ.   -  person Sanjeevakumar Hiremath    schedule 02.04.2011
comment
@Sanjeevakumar Это был просто пример. Я сказал, что хочу заполнить его свойства расчетными значениями во время выполнения. Я не думал, что код IL содержит данные, под этим я имел в виду, что хочу создать экземпляр класса, который я только что внедрил, и изменить его значения с помощью отражения. В целом, я хочу иметь возможность извлекать IL-код любого объекта, который может содержать любое количество свойств, полей и методов.   -  person Steven Ross    schedule 02.04.2011
comment
Можете ли вы указать разницу между хранением/загрузкой DLL и хранением/загрузкой IL?   -  person Akash Kava    schedule 02.04.2011
comment
@Akash Я отредактировал свой вопрос, указав, что классы, в которых я пытаюсь извлечь код IL, могут быть созданы или не созданы путем отражения. Следовательно, код IL для классов, созданных с помощью отражения, не будет храниться в DLL.   -  person Steven Ross    schedule 02.04.2011
comment
@ Стивен, вы сказали, что сериализация была одним из вариантов в вашем исходном вопросе, поэтому было трудно понять, нужны ли вам данные или код.   -  person Sanjeevakumar Hiremath    schedule 02.04.2011
comment
@Steven, ваше редактирование не отвечает на мой вопрос, также IL не будет работать без слияния всех упомянутых IL в вашей dll.   -  person Akash Kava    schedule 02.04.2011
comment
По какой-то причине я до сих пор не могу понять этот вопрос, может ли кто-нибудь отредактировать его, чтобы он имел какой-то смысл?   -  person Sanjeevakumar Hiremath    schedule 02.04.2011
comment
@Akash @Senjeevakimar @Hans Я снова отредактировал вопрос, чтобы попытаться упростить его. Пожалуйста, примите мои извинения за то, что я не совсем ясно выразился. Однако, как заметил Акаш, кажется, что то, что я хочу сделать, невозможно. :(   -  person Steven Ross    schedule 02.04.2011


Ответы (2)


Или еще лучше, используйте Mono.Cecil.

Это позволит вам получить отдельные инструкции, даже манипулируя ими и разбирая их (с помощью добавление моно декомпилятора).

Обратите внимание, что декомпилятор находится в стадии разработки (в прошлый раз, когда я проверял, он не полностью поддерживал лямбда-выражения и блоки исключений Visual Basic), но вы можете довольно легко получить декомпилированный вывод в C#, если вы не попадаете в эти граничные условия. . Кроме того, работа продвинулась с тех пор.

Mono Cecil в целом позволяет вам также написать IL для новой сборки, которую вы затем можете загрузить в свой домен приложения, если вам нравится играть с передовым преимуществом.

Обновление Я решил попробовать это. К сожалению, я думаю, что нашел, с какой проблемой вы столкнулись. Оказывается, нет способа получить байты IL для сгенерированного типа, если только сборка не была записана где-то, откуда вы можете ее загрузить.

Я предположил, что вы можете просто получить биты с помощью отражения (поскольку классы поддерживают необходимые методы), однако связанные методы просто вызывают исключение The invoked member is not supported in a dynamic module. при вызове. Вы можете попробовать это с помощью приведенного ниже кода, но вкратце, я полагаю, это означает, что этого не произойдет, если вы не хотите к черту Marshal::GetFunctionPointerForDelegate(). Вам придется бинарно выгружать инструкции и вручную дизассемблировать их как коды операций IL. Там будут драконы.

Фрагмент кода:

using System;
using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Cil;
using System.Reflection.Emit;
using System.Reflection;

namespace REFLECT
{
    class Program
    {
        private static Type EmitType()
        {
            var dyn = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("Emitted"), AssemblyBuilderAccess.RunAndSave);
            var mod = dyn.DefineDynamicModule("Emitted", "Emitted.dll");
            var typ = mod.DefineType("EmittedNS.EmittedType", System.Reflection.TypeAttributes.Public);
            var mth = typ.DefineMethod("SuperSecretEncryption", System.Reflection.MethodAttributes.Public | System.Reflection.MethodAttributes.Static, typeof(String), new [] {typeof(String)});

            var il = mth.GetILGenerator();
            il.EmitWriteLine("Emit was here");
            il.Emit(System.Reflection.Emit.OpCodes.Ldarg_0);    
            il.Emit(System.Reflection.Emit.OpCodes.Ret);
            var result = typ.CreateType();
            dyn.Save("Emitted.dll");
            return result;
        }

        private static Type TestEmit()
        {
            var result = EmitType();
            var instance = Activator.CreateInstance(result);
            var encrypted = instance.GetType().GetMethod("SuperSecretEncryption").Invoke(null, new [] { "Hello world" });
            Console.WriteLine(encrypted); // This works happily, print "Emit was here" first

            return result;
        }

        public static void Main (string[] args)
        {
            Type emitted = TestEmit();

              // CRASH HERE: even if the assembly was actually for SaveAndRun _and_ it 
              // has actually been saved, there seems to be no way to get at the image
              // directly:
            var ass = AssemblyFactory.GetAssembly(emitted.Assembly.GetFiles(false)[0]);

              // the rest was intended as mockup on how to isolate the interesting bits
              // but I didn't get much chance to test that :)
            var types = ass.Modules.Cast<ModuleDefinition>().SelectMany(m => m.Types.Cast<TypeDefinition>()).ToList();
            var typ = types.FirstOrDefault(t => t.Name == emitted.Name);

            var operands = typ.Methods.Cast<MethodDefinition>()
                .SelectMany(m => m.Body.Instructions.Cast<Instruction>())
                .Select(i => i.Operand);

            var requiredTypes = operands.OfType<TypeReference>()
                .Concat(operands.OfType<MethodReference>().Select(mr => mr.DeclaringType))
                .Select(tr => tr.Resolve()).OfType<TypeDefinition>()
                .Distinct();
            var requiredAssemblies = requiredTypes
                .Select(tr => tr.Module).OfType<ModuleDefinition>()
                .Select(md => md.Assembly.Name as AssemblyNameReference);

            foreach (var t in types.Except(requiredTypes))
                ass.MainModule.Types.Remove(t);

            foreach (var unused in ass.MainModule
                     .AssemblyReferences.Cast<AssemblyNameReference>().ToList()
                     .Except(requiredAssemblies))
                ass.MainModule.AssemblyReferences.Remove(unused);

            AssemblyFactory.SaveAssembly(ass, "/tmp/TestCecil.dll");
        }
    }
}
person sehe    schedule 02.04.2011
comment
К сожалению, поскольку в Mono.Cecil отсутствует официальная документация, я не могу найти пример, который позволил бы мне сохранить код IL, представляющий определенный класс, в файл. Позволяет ли он вводить код IL в текущий домен приложения? - person Steven Ross; 02.04.2011
comment
Репозиторий Mono содержит cecil-roundtrip.cs, который я предположим, должно быть довольно всеобъемлющим, чтобы начать вещи. Я посмотрю, смогу ли я сбить небольшой образец - person sehe; 03.04.2011
comment
Обновлено с плохими новостями. Извините :) Еще одно предложение, но я не уверен, что пошел бы туда - person sehe; 03.04.2011

Если вам нужен только IL для вашего класса User, он у вас уже есть. Это в dll, в которую вы его скомпилировали.

Из другой сборки вы можете загрузить dll с классом User динамически и использовать его посредством отражения.

ОБНОВЛЕНИЕ:

Если у вас есть динамический класс, созданный с помощью Reflection.Emit, у вас есть AssemblyBuilder, который вы можете использовать для сохраните его на диск.

Если вместо этого ваш динамический тип был создан с помощью Mono.Cecil, у вас есть AssemblyDefinition, который вы можете сохранить на диск с помощью myAssemblyDefinition.Write("MyAssembly.dll") (в Mono.Cecil 0.9).

person Jordão    schedule 02.04.2011