Я пишу простое настольное клиент-серверное приложение на C #. В целях самообразования я построил свою собственную систему сериализации для сообщений (определенных как классы), отправляемых туда и обратно между двумя приложениями через соединение сокета tcp / ip. Система использует отражение во время инициализации для создания методов сериализации / десериализации для каждого типа сообщения путем передачи IL.
Первая версия этой системы использовала DynamicMethod, передавая значение true в конструктор, чтобы разрешить сгенерированный IL (который работает с произвольным поля в типе сообщения), чтобы игнорировать разрешения доступа. Это сработало, и люди обрадовались, но я был недоволен тем, насколько болезненно непрозрачной была отладка полученных функций. Поэтому я решил отказаться от DynamicMethod и использовать классы * Builder для создания динамической сборки, которую я мог бы при желании сохранить на диск и исследовать с помощью такого инструмента, как .NET Reflector.
Я реорганизовал систему, а затем ударил ее по кирпичной стене. . Каждый раз, когда одна из новых функций сериализации пытается получить доступ к частному полю или методу в одном из моих типов сообщений, я получаю FieldAccessException или MethodAccessException. После долгих поисков в Google и скрежета зубов я думаю, что сузил проблему до одного из разрешений; в частности, я думаю, что в моей динамически созданной сборке отсутствует разрешение ReflectionPermissionFlag.MemberAccess относительно вызывающей / строящей сборки (где находятся все отраженные типы).
К сожалению, могу ' Кажется, я понял, как изменить процесс создания динамической сборки, чтобы сборка имела разрешение на отражение обратно в создаваемую сборку. Параметры разрешений для DefineDynamicAssembly, похоже, связаны с ограничением разрешения, а не с его предоставлением, что оставляет нам параметр Evidence. Доказательства, кажется, волшебным образом превращаются в набор разрешений, но я не могу найти никаких полезных примеров или объяснений того, как это происходит.
Итак, мои вопросы:
(1) Правильно ли я полагаю, что моя проблема отсутствует разрешение на мою динамически созданную сборку?
(2) Если да, как мне, как вызывающей сборке, предоставить необходимое разрешение для моей динамической сборки?
Текущий код создания динамической сборки:
AssemblyName assembly_name = new AssemblyName( "LCSerialization" );
assembly_name.Version = new Version( 1, 0, 0, 0 );
m_SerializationAssembly = current_domain.DefineDynamicAssembly( assembly_name, AssemblyBuilderAccess.RunAndSave ); // Fix me
m_SerializationModule = m_SerializationAssembly.DefineDynamicModule( "MainModule", "LCSerialization.dll" );
m_SerializationWrapperClass = m_SerializationModule.DefineType( "CSerializationWrapper", TypeAttributes.Public );
Обратите внимание, что мой проект ориентирован на .NET 3.5; в документации утверждается .NET 4. 0 использует другое понятие безопасности и не поддерживает методы на основе Evidence / PemissionSet в DefineDynamicAssembly.
Чтобы дать конкретный пример, предположим, что у меня есть класс вроде:
[NetworkMessage]
public class CTestMessage
{
public CTestMessage( int cheeseburgers )
{
m_CheeseBurgers = cheeseburgers
}
private int m_CheeseBurgers = 0;
}
, тогда моя система сериализации, столкнувшись с этим во время отражения инициализации, примерно завершит выполнение (вырезать и вставить невозможно здесь) следующее с type = typeof (CTestMessage):
MethodBuilder serialization_builder = m_SerializationWrapperClass.DefineMethod( "Serialize_" + type.Name,
MethodAttributes.Public | MethodAttributes.Static,
null,
new [] { type, typeof( BinaryWriter ) } );
ILGenerator s_il_gen = serialization_builder.GetILGenerator();
BindingFlags binding_flags_local_non_static = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly;
s_il_gen.Emit( OpCodes.Ldarg_1 ); // Eval Stack: BinaryWriter
s_il_gen.Emit( OpCodes.Ldarg_0 ); // Eval Stack: BinaryWriter, testmessage
s_il_gen.Emit( OpCodes.Ldfld, type.GetField( "m_CheeseBurgers", binding_flags_local_non_static ) ); // Eval Stack: BinaryWriter, int
s_il_gen.Emit( OpCodes.Callvirt, typeof( BinaryWriter ).GetMethod( "Write", new Type[] { typeof( Int32 ) } ) ); // Eval Stack:
s_il_gen.Emit( OpCodes.Ret );
При последующем выполнении метода исключение выдается в инструкции Ldfld.
Изменить: Более подробная информация, демонстрирующая, что то, что я прошу, должно быть возможным . Возьмите приведенный выше фрагмент кода, но замените MethodBuilder на DynamicMethod:
DynamicMethod serialization_builder = new DynamicMethod( "Serialize_" + type.Name, null, new [] { type, typeof( BinaryWriter ) }, true );
Теперь создайте делегат из DynamicMethod:
delegate void TestDelegate( CTestMessage, BinaryWriter );
TestDelegate test_delegate = serialization_builder.CreateDelegate( typeof( TestDelegate ) );
Этот делегат получает JIT-тест и выполняется правильно, без ошибок:
CTestMessage test_message = new CTestMessage( 5 );
BinaryWriter writer = new BinaryWriter( some_stream );
test_delegate( test_message, writer );