Удобный способ внесения изменений в чужую C#-библиотеку
От: Albeoris  
Дата: 15.04.16 23:00
Оценка:
Доброго времени суток.

Есть у меня на руках DLL от Unity-игры. Необходимо её модифицировать. В первом приближении — заставить дампить тексты из ресурсных файлов в текстовые файлики, а потом читать их из этих самых файликов вместо оригинальных ресурсов.

Делаю я это сейчас посредством Reflexil, тобишь наживаю редактируя код в каждом конкретном случае. Это долго, муторно, и неудобно.
В своё время я экспериментировал с Mono.Cecil и описывал собственную систему инъекции кода на основе атрибутов, попутно делая все классы родной либины публичными, а методы — виртуальными для перегрузки. Это был танец над пропастью и не всегда удавалось добиться желаемого эффекта.

В связи с чем возникает вопрос — есть ли сейчас какой-нибудь современный способ для проделывания подобных операций для C# DLL вообще или Unity-игр в частности?

Отдельно стоит вопрос о существовании инструмента, который позволил бы вынести из это библиотеки типы в собственную сборку, перебросив ссылки на неё. Таким образом, чтобы можно было без перекомпиляции всей сборки выносить отдельные типы в свою собственную, не нарушая взаимодействия между ними?
"Хаос всегда побеждает порядок, поскольку лучше организован." (с) Терри Пратчетт
il injection dll mono.cecil reflexil reverse engineering
Re: Удобный способ внесения изменений в чужую C#-библиотеку
От: Sinix  
Дата: 17.04.16 14:07
Оценка:
Здравствуйте, Albeoris, Вы писали:

A>В связи с чем возникает вопрос — есть ли сейчас какой-нибудь современный способ для проделывания подобных операций для C# DLL вообще или Unity-игр в частности?

Навскидку — или type forwarding, или post-build weaving, например, ч/з fody.

по type forwarding см также
http://www.codeproject.com/Articles/67999/Forwarding-a-type-from-one-assembly-to-another-Typ
http://stackoverflow.com/questions/4553463/what-is-the-correct-way-to-use-typeforwardedtoattribute

С clr и с mono-рантаймом должно работать, с свежей трансляцией в натив (il2cpp иликактамего) будет облом.
Re[2]: Удобный способ внесения изменений в чужую C#-библиотеку
От: Albeoris  
Дата: 17.04.16 19:08
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Навскидку — или type forwarding, или post-build weaving, например, ч/з fody.

S>по type forwarding см также
S>http://www.codeproject.com/Articles/67999/Forwarding-a-type-from-one-assembly-to-another-Typ
S>http://stackoverflow.com/questions/4553463/what-is-the-correct-way-to-use-typeforwardedtoattribute
Спасибо, посмотрю.

S>С clr и с mono-рантаймом должно работать, с свежей трансляцией в натив (il2cpp иликактамего) будет облом.

Кстати, что нас ждёт с рождением этой чудотехнологии? Возможно ли будет разобрать откомпилированный машинный код обратно в IL? Или всеми любимые dotPeek, ILSpy, Reflector уйдут на свалку истории?
"Хаос всегда побеждает порядок, поскольку лучше организован." (с) Терри Пратчетт
Re[3]: Удобный способ внесения изменений в чужую C#-библиотеку
От: Sinix  
Дата: 17.04.16 20:18
Оценка: 2 (1)
Здравствуйте, Albeoris, Вы писали:

S>>С clr и с mono-рантаймом должно работать, с свежей трансляцией в натив (il2cpp иликактамего) будет облом.

A>Кстати, что нас ждёт с рождением этой чудотехнологии? Возможно ли будет разобрать откомпилированный машинный код обратно в IL? Или всеми любимые dotPeek, ILSpy, Reflector уйдут на свалку истории?

С большой долей вероятности:
1. Для трансляции в натив придётся сделать несколько телодвижений, особенно если в проекте явно или неявно используется рефлексия, expressions tree, динамическая загрузка сборок и тыды и тыпы.
2. В обозримом будущем в натив будет транслироваться только приложение целиком, не отдельные библиотеки (не то чтоб это было принципиально невозможно, просто команде .net core сейчас не до допиливания ilc-тулчайна, кроме них универсальное решение никто не потянует).
3. Для оттранслированного в натив кода стоимость работ по реверс-инжинерингу будет сопоставима с обычными c++ — бинарниками.
Re[2]: Удобный способ внесения изменений в чужую C#-библиоте
От: Albeoris  
Дата: 25.04.16 19:57
Оценка: +1
Здравствуйте, Sinix, Вы писали:

S>Навскидку — или type forwarding, или post-build weaving, например, ч/з fody.

К сожалению, оба сценария не совсем подходят.
Оба предполагают, что у меня исходники изменяемой DLL я могу её перекомпилировать.
У меня же есть исходники лишь DLL на которую происходит перенаправление.
Вот как бы средствами Mono.Cecil воткнуть type forwarding в чужую DLL.

Хочется переписать тип в своей сборке (на нормальном C#), сохранив сигнатуры оригинальных методов.
Проблема в том, что TypeReference фигурирует во множестве мест: аргументы методов, возвращаемые значения, отражение (отдельная песня >_>), определение generic'ов. И очень хочется оставить его в старой сборке в виде заглушки: перенаправить сюда.

Попробую воспроизвести TypeForwarding и посмотреть — что меняется в этом случае в старой DLL.
Возможно, я смогу воспроизвести это посредством Mono.Cecil. В противном случае придётся руками проходить по всем возможным местам обитания ссылки на старый тип и менять их на новый.

---
Ага, нашёл отличную статью от Нестерука:
https://nesteruk.wordpress.com/2011/04/06/on-type-forwarding/

В исходной сборке появляется директива:
.class extern forwarder SomeNamespace.C
{
  .assembly extern SecondAssembly
}
"Хаос всегда побеждает порядок, поскольку лучше организован." (с) Терри Пратчетт
Отредактировано 25.04.2016 20:10 Albeoris . Предыдущая версия .
Re[3]: Удобный способ внесения изменений в чужую C#-библиоте
От: Albeoris  
Дата: 25.04.16 22:45
Оценка: +1
Разобрался. Вдруг, кому-нибудь пригодится.

Данная пометка приводит к тому, что в сборке появляются так называемые ExportedTypes.
Средствами Mono.Cecil их пожно достать следующим образом:
oldAssembly.MainModule.ExportedTypes


Для того чтобы перебросить вызовы в новое место, необходимо:
1) Создать тип с тем же именем и в том же пространстве имён в новой сборке.
2) Удалить его из списка типов старой сборки.
3) Импортировать в старую сборку тип из новой (module.Import)
4) Добавить в ExportedTypes ссылку на тип из новой сборки

new ExportedType("namespace", "typeName", newAssembly.MainModule, /*ссылка на новую сборку*/)


Ссылку можно получить, к примеру из данного свойства после импорта типа:
oldAssembly.MainModule.AssemblyReferences
"Хаос всегда побеждает порядок, поскольку лучше организован." (с) Терри Пратчетт
Re[4]: Удобный способ внесения изменений в чужую C#-библиоте
От: Albeoris  
Дата: 25.04.16 23:52
Оценка:
Мда, вот только с Unity-играми это работает до той поры, пока в дело не вступает MonoBehaviour.
Уж не знаю какие страшные вещи с ним делает Unity в своих потрохах, но при попытке получить компонент UIKeyTrigger, мы падаем с исключением.
Есть подозрение, что я не совсем правильно реализовал задуманное, так как просто удалить тип из сборки мне не дали — увы, именно в рамках этой сборки необходимо вручную перебросить все ссылки на тип. -_-
Поэтому у меня в ней остались как оригинальные классы, так и перенаправление. Судя по StackOverflow, используются оба.
Буду чистить этот зоопарк. Посмотрим, может быть случится чудо и после чистки проблема исчезнет, ведь эта сборка теперь будет ссылаться напрямую на мою.

  public static UIKeyTrigger Input
  {
    get
    {
      if ((UnityEngine.Object) PersistenSingleton<UIManager>.Instance.gameObject == (UnityEngine.Object) null)
        return (UIKeyTrigger) null;
      return PersistenSingleton<UIManager>.Instance.gameObject.GetComponent<UIKeyTrigger>();
    }
  }

    [SecuritySafeCritical]
    public unsafe T GetComponent<T>()
    {
      CastHelper<T> castHelper = new CastHelper<T>();
      this.GetComponentFastPath(typeof (T), new IntPtr((void*) &castHelper.onePointerFurtherThanT));
      return castHelper.t;
    }

    [WrapperlessIcall]
    [MethodImpl(MethodImplOptions.InternalCall)]
    internal void GetComponentFastPath(System.Type type, IntPtr oneFurtherThanResultValue);
"Хаос всегда побеждает порядок, поскольку лучше организован." (с) Терри Пратчетт
Re[5]: Удобный способ внесения изменений в чужую C#-библиоте
От: Albeoris  
Дата: 26.04.16 01:36
Оценка:
A>Буду чистить этот зоопарк. Посмотрим, может быть случится чудо и после чистки проблема исчезнет, ведь эта сборка теперь будет ссылаться напрямую на мою.

Нашёл такой вот монстоузный код:
https://groups.google.com/forum/#!msg/mono-cecil/3D0RLfvN054/0Fxv1g0fYk8J

Приплыли:
Ну и как мне патчить UnresolvedMethodBody?
   в Mono.Cecil.MetadataBuilder.LookupToken(IMetadataTokenProvider provider)
   в Mono.Cecil.Cil.CodeReader.PatchRawCode(ByteBuffer buffer, Int32 code_size, CodeWriter writer)
   в Mono.Cecil.Cil.CodeReader.PatchRawFatMethod(ByteBuffer buffer, MethodSymbols symbols, CodeWriter writer, MetadataToken& local_var_token)
   в Mono.Cecil.Cil.CodeReader.PatchRawMethodBody(MethodDefinition method, CodeWriter writer, MethodSymbols& symbols)
   в Mono.Cecil.Cil.CodeWriter.WriteUnresolvedMethodBody(MethodDefinition method)
   в Mono.Cecil.Cil.CodeWriter.WriteMethodBody(MethodDefinition method)
   в Mono.Cecil.MetadataBuilder.AddMethod(MethodDefinition method)
   в Mono.Cecil.MetadataBuilder.AddMethods(TypeDefinition type)
   в Mono.Cecil.MetadataBuilder.AddType(TypeDefinition type)
   в Mono.Cecil.MetadataBuilder.AddNestedTypes(TypeDefinition type)
   в Mono.Cecil.MetadataBuilder.AddType(TypeDefinition type)
   в Mono.Cecil.MetadataBuilder.AddTypeDefs()
   в Mono.Cecil.MetadataBuilder.BuildTypes()
   в Mono.Cecil.MetadataBuilder.BuildModule()
   в Mono.Cecil.ModuleWriter.<BuildMetadata>b__0(MetadataBuilder builder, MetadataReader _)
   в Mono.Cecil.ModuleDefinition.Read[TItem,TRet](TItem item, Func`3 read)
   в Mono.Cecil.ModuleWriter.WriteModuleTo(ModuleDefinition module, Stream stream, WriterParameters parameters)
   в Mono.Cecil.ModuleDefinition.Write(String fileName, WriterParameters parameters)
   в Mono.Cecil.AssemblyDefinition.Write(String fileName)
   в Memoria.Patcher.Program.Patch(String directory) в C:\Git\C#\Memoria\Memoria.Patcher\Program.cs:строка 107


---
UPD:
А, нет, это я забыл по вложенным типам пройтись. х.х

  Устаревший мусор
Но после обработки получаю ошибку
[quote]"Member '{0}' is declared in another module and needs to be imported"
в Mono.Cecil.MetadataBuilder.LookupToken(IMetadataTokenProvider provider)
в Mono.Cecil.SignatureWriter.WriteTypeSignature(TypeReference type)
в Mono.Cecil.SignatureWriter.WriteGenericInstanceSignature(IGenericInstance instance)
в Mono.Cecil.MetadataBuilder.GetMethodSpecSignature(MethodSpecification method_spec)
в Mono.Cecil.MetadataBuilder.CreateMethodSpecRow(MethodSpecification method_spec)
в Mono.Cecil.MetadataBuilder.GetMethodSpecToken(MethodSpecification method_spec)
в Mono.Cecil.MetadataBuilder.LookupToken(IMetadataTokenProvider provider)
в Mono.Cecil.Cil.CodeWriter.WriteOperand(Instruction instruction)
в Mono.Cecil.Cil.CodeWriter.WriteInstructions()
в Mono.Cecil.Cil.CodeWriter.WriteResolvedMethodBody(MethodDefinition method)
в Mono.Cecil.Cil.CodeWriter.WriteMethodBody(MethodDefinition method)
в Mono.Cecil.MetadataBuilder.AddMethod(MethodDefinition method)
в Mono.Cecil.MetadataBuilder.AddMethods(TypeDefinition type)
в Mono.Cecil.MetadataBuilder.AddType(TypeDefinition type)
в Mono.Cecil.MetadataBuilder.AddTypeDefs()
в Mono.Cecil.MetadataBuilder.BuildTypes()
в Mono.Cecil.MetadataBuilder.BuildModule()
в Mono.Cecil.ModuleWriter.<BuildMetadata>b__0(MetadataBuilder builder, MetadataReader _)
в Mono.Cecil.ModuleDefinition.Read[TItem,TRet](TItem item, Func`3 read)
в Mono.Cecil.ModuleWriter.WriteModuleTo(ModuleDefinition module, Stream stream, WriterParameters parameters)
в Mono.Cecil.ModuleDefinition.Write(String fileName, WriterParameters parameters)
в Mono.Cecil.AssemblyDefinition.Write(String fileName)[/quote]

Проблема в том, что в процессе замены инструкций я добавил проверку после каждой:
MemberReference op = instructions[i].Operand as MemberReference;
if (op != null)
{
    if (op.Module != module)
        throw new InvalidCastException();
}


И я, честно говоря, не понимаю, по какой причине у меня эта проверка проходит нормально, а Mono падает — откуда он берёт этот проклятущий MemberReference с левым модулем?

Хотя это явно только начало — автор игноирует Generic'и, аттрибуты, исключения, но это можно будет дописать по мере возникновения проблем.

---
Ох, блин...
if (member == null || member.Module != this.module)
throw MetadataBuilder.CreateForeignMemberException(member);

Возможно, причина в том, что member == null

---
Тьфу, пропасть!
Точно. ldfld null
Буду разбираться — в какой момент (и где) я его потерял...
---
Ура! Прорвался.
НЕЛЬЗЯ удалять из сборки старый тип, до того, как все ссылки на него не будут заменены на новый.
Как и ожидалось, впилился в Generic'и, но это прогресс. Едем дальше!

   в Mono.Cecil.MetadataBuilder.LookupToken(IMetadataTokenProvider provider)
   в Mono.Cecil.SignatureWriter.WriteTypeSignature(TypeReference type)
   в Mono.Cecil.SignatureWriter.WriteGenericInstanceSignature(IGenericInstance instance)
   в Mono.Cecil.MetadataBuilder.GetMethodSpecSignature(MethodSpecification method_spec)
   в Mono.Cecil.MetadataBuilder.CreateMethodSpecRow(MethodSpecification method_spec)
   в Mono.Cecil.MetadataBuilder.GetMethodSpecToken(MethodSpecification method_spec)
   в Mono.Cecil.MetadataBuilder.LookupToken(IMetadataTokenProvider provider)
   в Mono.Cecil.Cil.CodeWriter.WriteOperand(Instruction instruction)
   в Mono.Cecil.Cil.CodeWriter.WriteInstructions()
   в Mono.Cecil.Cil.CodeWriter.WriteResolvedMethodBody(MethodDefinition method)
   в Mono.Cecil.Cil.CodeWriter.WriteMethodBody(MethodDefinition method)
   в Mono.Cecil.MetadataBuilder.AddMethod(MethodDefinition method)
   в Mono.Cecil.MetadataBuilder.AddMethods(TypeDefinition type)
   в Mono.Cecil.MetadataBuilder.AddType(TypeDefinition type)
   в Mono.Cecil.MetadataBuilder.AddTypeDefs()
   в Mono.Cecil.MetadataBuilder.BuildTypes()
   в Mono.Cecil.MetadataBuilder.BuildModule()
   в Mono.Cecil.ModuleWriter.<BuildMetadata>b__0(MetadataBuilder builder, MetadataReader _)
   в Mono.Cecil.ModuleDefinition.Read[TItem,TRet](TItem item, Func`3 read)
   в Mono.Cecil.ModuleWriter.WriteModuleTo(ModuleDefinition module, Stream stream, WriterParameters parameters)
   в Mono.Cecil.ModuleDefinition.Write(String fileName, WriterParameters parameters)
   в Mono.Cecil.AssemblyDefinition.Write(String fileName)
"Хаос всегда побеждает порядок, поскольку лучше организован." (с) Терри Пратчетт
Отредактировано 26.04.2016 2:38 Albeoris . Предыдущая версия . Еще …
Отредактировано 26.04.2016 2:20 Albeoris . Предыдущая версия .
Отредактировано 26.04.2016 1:51 Albeoris . Предыдущая версия .
Отредактировано 26.04.2016 1:46 Albeoris . Предыдущая версия .
Отредактировано 26.04.2016 1:42 Albeoris . Предыдущая версия .
Re[6]: Удобный способ внесения изменений в чужую C#-библиоте
От: Albeoris  
Дата: 26.04.16 02:42
Оценка: 66 (1)
Заключительная версия, которая заменяет ссылки на старый тип новыми.
Неполная (отсутствует поддержка исключений, атрибутов). Но в моём сценарии пока работает. Буду допиливать по мере необходимости.
---
Обновил — добавил поддержку массивов.
---
Обновил — добавил поддержку Generic'ов.
---
Внимание: Перед заменой типа нужно заменить все его публичные вложенные (Nested) типы. Вложенные типы нужно импортировать, но не экспортировать!

    public sealed class TypeReplacer
    {
        private readonly String _fullName;
        private readonly TypeReference _newReference;
        private readonly TypeDefinition _newDefination;
        private readonly List<ArrayType> _newArrayReferences;
        private readonly Dictionary<TypeReference, TypeDefinition> _arrayDefinitions;

        private ModuleDefinition _module;
        private MethodDefinition _method;

        public TypeReplacer(String fullName, TypeReference newReference)
        {
            _fullName = fullName;
            _newReference = newReference;
            _newDefination = newReference.Resolve();
            _newArrayReferences = new List<ArrayType>();
            _arrayDefinitions = new Dictionary<TypeReference, TypeDefinition>();
        }

        public void Replace(ModuleDefinition module)
        {
            _module = module;
            foreach (TypeDefinition type in _module.Types)
                ProcessType(type);
        }

        private void ProcessType(TypeDefinition type)
        {
            if (type.FullName == _fullName)
                return;

            ProcessProperties(type);
            ProcessFields(type);
            ProcessMethods(type);
            ProcessNestedTypes(type);
        }

        private void ProcessNestedTypes(TypeDefinition type)
        {
            if (!type.HasNestedTypes)
                return;

            foreach (TypeDefinition nested in type.NestedTypes)
                ProcessType(nested);
        }

        private void ProcessProperties(TypeDefinition type)
        {
            foreach (PropertyDefinition p in type.Properties)
            {
                TypeReference newType;
                if (TryGetNewType(p.PropertyType, out newType))
                    p.PropertyType = newType;
            }
        }

        private void ProcessFields(TypeDefinition type)
        {
            foreach (FieldDefinition f in type.Fields)
            {
                TypeReference newType;
                if (TryGetNewType(f.FieldType, out newType))
                    f.FieldType = newType;
            }
        }

        private void ProcessMethods(TypeDefinition type)
        {
            foreach (MethodDefinition m in type.Methods)
            {
                _method = m;
                ProcessMethod();
            }
        }

        private void ProcessMethod()
        {
            TypeReference newType;
            if (TryGetNewType(_method.ReturnType, out newType))
                _method.ReturnType = newType;

            ProcessMethodParameters();
            ProcessMethodBody();
        }

        private void ProcessMethodParameters()
        {
            if (!_method.HasParameters)
                return;

            foreach (ParameterDefinition p in _method.Parameters)
            {
                TypeReference newType;
                if (TryGetNewType(p.ParameterType, out newType))
                    p.ParameterType = newType;
            }
        }

        private void ProcessMethodBody()
        {
            if (!_method.HasBody)
                return;

            ProcessMethodBodyVariables();
            ProcessMethodBodyInstructions();
        }

        private void ProcessMethodBodyVariables()
        {
            if (!_method.Body.HasVariables)
                return;

            foreach (VariableDefinition v in _method.Body.Variables)
            {
                TypeReference newType;
                if (TryGetNewType(v.VariableType, out newType))
                    v.VariableType = newType;
            }
        }

        private void ProcessMethodBodyInstructions()
        {
            TypeReference newType;
            MethodBody body = _method.Body;
            Collection<Instruction> instructions = body.Instructions;
            for (int i = 0; i < instructions.Count; i++)
            {
                Instruction inst = instructions[i];
                switch (inst.OpCode.OperandType)
                {
                    case OperandType.InlineField:
                    case OperandType.InlineMethod:
                    case OperandType.InlineTok:
                    case OperandType.InlineType:
                        break;
                    default:
                        continue;
                }

                IGenericInstance genericInstance = inst.Operand as IGenericInstance;
                if (genericInstance != null && genericInstance.HasGenericArguments)
                {
                    for (int g = 0; g < genericInstance.GenericArguments.Count; g++)
                    {
                        if (TryGetNewType(genericInstance.GenericArguments[g], out newType))
                            genericInstance.GenericArguments[g] = newType;
                    }
                }

                TypeReference typeReference = inst.Operand as TypeReference;
                if (typeReference != null)
                {
                    if (TryGetNewType(typeReference, out newType))
                        instructions.Replace(i, RecreateTypeOperand(inst, newType));
                    continue;
                }

                MethodReference methodReference = inst.Operand as MethodReference;
                if (methodReference != null)
                {
                    if (TryGetNewType(methodReference.DeclaringType, out newType))
                        instructions.Replace(i, RecreateMethodOperand(inst, newType));
                    continue;
                }

                FieldReference fieldReference = inst.Operand as FieldReference;
                if (fieldReference != null)
                {
                    if (TryGetNewType(fieldReference.DeclaringType, out newType))
                        instructions.Replace(i, RecreateFieldOperand(inst, newType));

                    continue;
                }

            }
        }

        private Instruction RecreateMethodOperand(Instruction inst, TypeReference newType)
        {
            MethodDefinition method = ResolveType(newType).GetMethod((MethodReference)inst.Operand);
            MethodReference methodReference = _module.Import(method);
            return Instruction.Create(inst.OpCode, methodReference);
        }

        private Instruction RecreateFieldOperand(Instruction inst, TypeReference newType)
        {
            String oldName = ((FieldReference)inst.Operand).Name;
            FieldDefinition field = ResolveType(newType).GetField(oldName);
            FieldReference fieldReference = _module.Import(field);
            return Instruction.Create(inst.OpCode, fieldReference);
        }

        private Instruction RecreateTypeOperand(Instruction inst, TypeReference newType)
        {
            return Instruction.Create(inst.OpCode, newType);
        }

        private Boolean TryGetNewType(TypeReference oldType, out TypeReference newType)
        {
            Int32 rank = 0;
            while (oldType.IsArray)
            {
                rank++;
                oldType = oldType.GetElementType();
            }

            if (oldType.FullName == _fullName)
            {
                newType = rank > 0 ? GetArrayType(rank) : _newReference;
                return true;
            }

            GenericInstanceType genericType = oldType as GenericInstanceType;
            if (genericType != null)
            {
                for (int index = 0; index < genericType.GenericArguments.Count; index++)
                {
                    TypeReference argument = genericType.GenericArguments[index];
                    if (TryGetNewType(argument, out newType))
                        genericType.GenericArguments[index] = newType;
                }
            }

            newType = null;
            return false;
        }

        private TypeDefinition ResolveType(TypeReference type)
        {
            if (type == _newReference)
                return _newDefination;

            TypeDefinition definition;
            if (!_arrayDefinitions.TryGetValue(type, out definition))
            {
                definition = type.Resolve();
                _arrayDefinitions.Add(type, definition);
            }
            return definition;
        }

        private ArrayType GetArrayType(int rank)
        {
            for (int i = _newArrayReferences.Count; i <= rank; i++)
                _newArrayReferences.Add(new ArrayType(_newReference, i + 1));

            return _newArrayReferences[rank];
        }
    }

public static MethodDefinition GetMethod(this TypeDefinition td, MethodReference descriptor)
        {
            foreach (MethodDefinition method in td.Methods)
            {
                if (method.Name != descriptor.Name)
                    continue;

                if (method.ReturnType.FullName != descriptor.ReturnType.FullName)
                    continue;

                if (method.HasGenericParameters)
                {
                    if (!descriptor.HasGenericParameters)
                        continue;

                    if (method.GenericParameters.Count != descriptor.GenericParameters.Count)
                        continue;

                    for (int i = 0; i < method.GenericParameters.Count; i++)
                    {
                        if (method.GenericParameters[i].FullName != descriptor.GenericParameters[i].FullName)
                            continue;
                    }
                }
                else if (descriptor.HasGenericParameters)
                {
                    continue;
                }

                if (method.HasParameters)
                {
                    if (!descriptor.HasParameters)
                        continue;

                    if (method.Parameters.Count != descriptor.Parameters.Count)
                        continue;

                    for (int i = 0; i < method.Parameters.Count; i++)
                    {
                        ParameterDefinition p1 = method.Parameters[i];
                        ParameterDefinition p2 = descriptor.Parameters[i];
                        if (p1.ParameterType.FullName != p2.ParameterType.FullName)
                            continue;

                        if (p1.Attributes != p2.Attributes)
                            continue;
                    }
                }
                else if (descriptor.HasParameters)
                {
                    continue;
                }

                return method;
            }

            throw new InvalidOperationException("Sequence contains more than one matching element.");
        }
"Хаос всегда побеждает порядок, поскольку лучше организован." (с) Терри Пратчетт
Отредактировано 14.05.2016 12:56 Albeoris . Предыдущая версия . Еще …
Отредактировано 05.05.2016 21:30 Albeoris . Предыдущая версия .
Отредактировано 27.04.2016 10:39 Albeoris . Предыдущая версия .
Отредактировано 26.04.2016 14:35 Albeoris . Предыдущая версия .
Отредактировано 26.04.2016 9:14 Albeoris . Предыдущая версия .
Re[7]: Удобный способ внесения изменений в чужую C#-библиоте
От: Lexey Россия  
Дата: 26.04.16 11:09
Оценка: 1 (1)
Здравствуйте, Albeoris, Вы писали:

A>
A>    public sealed class TypeReplaser
A>


Может быть все-таки TypeReplacer?
"Будь достоин победы" (c) 8th Wizard's rule.
Re[8]: Удобный способ внесения изменений в чужую C#-библиоте
От: Albeoris  
Дата: 26.04.16 14:36
Оценка:
Здравствуйте, Lexey, Вы писали:

L>Может быть все-таки TypeReplacer?

Справедливо. (=
"Хаос всегда побеждает порядок, поскольку лучше организован." (с) Терри Пратчетт
Re[7]: Удобный способ внесения изменений в чужую C#-библиоте
От: Albeoris  
Дата: 14.05.16 13:01
Оценка: 22 (1)
A>Заключительная версия, которая заменяет ссылки на старый тип новыми.
A>Неполная (отсутствует поддержка исключений, атрибутов). Но в моём сценарии пока работает. Буду допиливать по мере необходимости.
A>---
A>Обновил — добавил поддержку массивов.
A>---
A>Обновил — добавил поддержку Generic'ов.
A>---
A>Внимание: Перед заменой типа нужно заменить все его публичные вложенные (Nested) типы. Вложенные типы нужно импортировать, но не экспортировать!

Сейчас воткнулся в одно не слишком очевидное место в Mono.Cecil.
В случае вложенных типов, правильный алгоритм замены следующий:
1) Импортируем замещающий родительский тип.
2) Импортируем замещающий вложенный тип.
3) Заменяем ссылки на вложенный тип в исходной сборке.
4) Заменяем ссылки на родительский тип в исходной сборке.
5) НЕ УДАЛЯЕМ вложенный тип.
6) НЕ ЭКСПОРТИРУЕМ вложенный тип.
7) Удаляем родительский тип.
8) Экспортируем родительский тип.

В противном случае, мы получим ошибку — BadImageFormatException: Duplicate type with name "...", так как ссылки на вложенный тип всегда идут через его экспортированного родителя, но явный экспорт вложенного типа создаст две ссылки на него, который CLR не сможет корректно разрешить.
"Хаос всегда побеждает порядок, поскольку лучше организован." (с) Терри Пратчетт
Отредактировано 14.05.2016 13:02 Albeoris . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.