Компоненты COM И .NET Сборки

Сборки представляют собой файлы, которые состоят из нескольких РЕ-файлов. Сборка включает в себя: декларацию, один или несколько модулей, и возможно один или несколько ресурсов.    

Модуль имеет расширение .netmodule и представляет собой DLL.

Декларацию сборки можно хранить:

§  в однофайловой сборке (для простого приложения или DLL);

§  для многофайловых сборок – как самостоятельная часть сборки или в составе одного из модулей сборки.

В декларации содержится (частично информация указывается в свойствах проекта, диалоге Assembly Information):

§  имя сборки;

§  информация о версии;

§  совместно используемое имя и подписанный хэш сборки;

§  список всех файлов сборки;

§  список ссылок на внешние сборки, встречающихся в декларации;

§  список всех типов, используемых в сборке, и указание модуля, содержащего данный тип;

§  список запрещенных прав доступа к сборке;

§  атрибуты, введенные пользователем;

§  доп. информация о приложении (название, авторские права, фирма и т.п.).

 

Развертываемые сборки могут быть:

§  закрытыми (по умолчанию);

§  совместно используемыми( shared assembly).

В памяти сборки хранятся в формате РЕ-файлов, как и обычные EXE-файлы и DLL-модули. Формат РЕ-файлов является основным форматом хранения выполнимых файлов в Windows. Формат PE-файлов также может служить для представления объектных модулей, компилируемых далее в одну выполняемую программу.

При выполнении программы данные из PE-файла размещаются в памяти компьютера. Управление памятью в Windows выполняет менеджер виртуальной памяти. Вся оперативная память подразделяется на блоки – физические страницы, размером в 4096 байт. При недостатке оперативной памяти менеджер виртуальной памяти использует файл подкачки (размещаемый на жестком диске), перемещая туда временно неиспользуемые блоки оперативной памяти.

Каждый процесс в Windows запускается в своем виртуальном адресном пространстве. Под каждый процесс выделяется 4 Гб памяти: из них 2 Гб выделяются непосредственно процессу, а 2 Гб используются операционной системой.

При выполнении файла загрузчик операционной системы выполняет отображение отдельных частей PE-файла на адресное пространство процесса. При этом он использует таблицу релокаций PE-файла для корректировки абсолютных адресов в исполняемом коде и передает управление на точку входа в выполняемую программу.  На следующей схеме представлено примерное размещение отдельных частей в PE-файле и в памяти процесса.

 

              PE-файл сборки                                                    Память процесса

 

 

Секция N

Секция экспорта

 

 

Секция импорта

 

 

 

 

Секция 3

Секция таблицы релокаций

 

 

Секция N

 

Секция 2

. . .

 

 

Секция 3

 

Секция 1

Секция 2

 

 

Секция 1

 

 

Таблица секций

 

Таблица секций

Дополнительный заголовок PE-файла

 

Дополнительный заголовок PE-файла

Заголовок PE-файла

 

Заголовок PE-файла

Заголовок MS DOS

 

Заголовок MS DOS

 

Секции могут содержать код, данные или служебную информацию. Количество секций указывается в заголовке PE-файла. При отображении секций из PE-файла в память процесса секции выравниваются по блокам физической памяти. Поэтому в оперативной памяти данные занимают больше места, чем в PE-файле. Для доступа к данным вычисляется – RVA-адрес (Relative Virtual Address), определяющий относительный виртуальный адрес элемента в памяти процесса.

PE-файл может содержать различное количество секций, но всегда содержит хотя бы одну секцию с кодом. Секция определяется именем и атрибутами. В зависимости от средства, создавшего PE-файл названия секций могут различаться. Visual Studio при создании выполнимых файлов именует секции кода как .text, константы -как .rdata, таблицы импорта – как .idata, таблицу экспорта как .edata, и таблицу релокаций – как .reloc.  

Для сборок .NET в секции .idata всегда описывается импортируемая функция из библиотеки mscoree.dll. Эта функция определяет точку входа, на которую передается управление при запуске сборки. Для выполнимых EXE-файлов указывается функция _CorExeMain, а для DLL-библиотек – функция _CorDllMain. Именно функция _CorExeMain выполняет запуск CLR. Далее CLR производит JIT-компиляцию программы и выполняет ее.

Выполняемый файл всегда загружается по некоторому указанному базовому адресу, находящемуся в заголовке PE-файла. DLL-библиотеки грузятся в адресное пространство выполняемого файла, поэтому указанный в них базовый адрес может быть уже использован, и загрузчик размещает DLL-библиотеку по другому адресу. При этом требуется скорректировать абсолютные адреса, имеющиеся в файлеDLL-библиотеки. Все эти абсолютные адреса с их RVA-адресами содержатся в PE-файле в таблице релокаций.

Создание сборок

Для построения сборки программы на C# можно выполнить в Visual Studio команду меню Project | Properties | Application| Output type (выбрать из Windows Application, Console Application, Class Library).

Для получения информации о сборках и управления сборками библиотека Framework предоставляет класс Assembly из пространства имен System.Reflection. Этот класс содержит ряд методов, включая следующие:

§  GetAssembly      – получает текущую загруженную сборку, в которой определен указываемый класс.
Например:

// Сборки в C#

Assembly Assembly1;

Int32 Int1 = new Int32();  // Создаем объект

Type Type1;

Type1 = Int1.GetType();  // Создаем объект типа Type для объекта Int1

Assembly1 = Assembly.GetAssembly(Int1.GetType());

// Используя свойство CodeBase запрашиваем размещение сборки

Console.WriteLine("CodeBase=" + Assembly1.CodeBase);

// Этот же код на C++ можно записать следующим образом:

Assembly^ Assembly1;

Int32 Int1(0);  Type^ Type1;

Type1 = Int1.GetType();

Assembly1 = Assembly::GetAssembly( Int1.GetType() );

Console::WriteLine( "CodeBase= {0}", Assembly1->CodeBase );

§  GetCallingAssembly – возвращает объект Assembly для метода, из которого был вызван текущий выполняемый метод.
Например:

Assembly Assembly1;

Int32 Int1 = new Int32();

Type Type1; Type1 = Int1.GetType();

Assembly1 = Assembly.GetAssembly(Int1.GetType());

// Отображаем имя сборки (свойство FullName), вызвавшей данный метод

Console.WriteLine("GetCallingAssembly=" +
                                Assembly.GetCallingAssembly().FullName);

§  GetExportedTypes – возвращает в объекте типа Type[] список экспортируемых типов, объявленных в сборке и доступных вне сборки. Например:

using System;

using System.Collections.Generic;

using System.Text;

using System.Reflection;

namespace Console3

{

using System;

using System.Reflection;

public class Program

{

    public static void Main()

    {        foreach (Type t in Assembly.GetExecutingAssembly().GetExportedTypes())

        {            Console.WriteLine(t);        }

    }}

public class PublicClass

{    public class PublicNestedClass {}

    protected class ProtectedNestedClass {}

    internal class FriendNestedClass {}

    private class PrivateNestedClass {}

}

internal class FriendClass

{    public class PublicNestedClass {}

    protected class ProtectedNestedClass {}

    internal class FriendNestedClass {}

    private class PrivateNestedClass {}

} }

// На следующем рисунке приведен результат выполнения этого примера

§  GetLoadedModules – возвращает список (Module[]) всех загруженных модулей, являющихся растью данной сборки;

§  GetModules – возвращает список (Module[]) всех модулей, являющихся растью данной сборки;

§  GetName – возвращает имя данной сборки

§  Load – загружает сборку с указанным именем сборки;

§  LoadFile – загружает сборку из указанного файла;

§  LoadFrom – загружает сборку по имени или из файла. Например:

Assembly Assembly1;

Assembly1 = Assembly.LoadFrom("c:\\Work.Assembly.dll");

// Получение ссылки на метод загруженной сборки

MethodInfo Method = Assembly1.GetTypes()[0].GetMethod("M1");

// Запрос информации о параметрах метода

ParameterInfo[] Params = Method.GetParameters();

// Отображение информации о параметрах метода

foreach (ParameterInfo Param in Params)

{

    Console.WriteLine("Param=" +

                        Param.Name.ToString());                  // Param = sMyParam1

    Console.WriteLine("  Type=" +

                       Param.ParameterType.ToString());//   Type = System.String

    Console.WriteLine("  Position=" +

                         Param.Position.ToString());             //   Position = 0

    Console.WriteLine("  Optional=" +

                       Param.IsOptional.ToString());           //   Optional=False

 

}

Класс Assembly позволяет получать не просто информацию о всех методах сборки, но и получать информацию о методах с указанными модификаторами доступа, с определением статичесого метода или метода экземпляра класса.

Например:

// При вызове программы параметр указывает имя файла сборки:
// ClassLoad MyAssembly

 

using System;

using System.Reflection;

using System.Security.Permissions;

 

public class ClassLoad

{

    [PermissionSetAttribute(SecurityAction.Demand, Name="FullTrust")]

    public static void Main(string[] args)

    {

        Assembly a = Assembly.Load(args[0]);   // Загрузка сборки

        Type[] mytypes = a.GetTypes();

        BindingFlags flags = (BindingFlags.NonPublic | BindingFlags.Public |

            BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly);

 

        foreach(Type t in mytypes)

        {

            MethodInfo[] mi = t.GetMethods(flags);

            Object obj = Activator.CreateInstance(t);

 

            foreach(MethodInfo m in mi)

            {

                Console.WriteLine(m);

            }

        }

    }

}

// Компиляция следующей сборки: csc /t:library MyAssembly.cs

// выполнит создание файла MyAssembly.dll.

using System;

public class MyAssembly

{

    public void Method1()    {        Console.WriteLine("Это Method1");    }

    public void Method2()    {        Console.WriteLine("Это Method2");    }

    public void Method3()    {        Console.WriteLine("Это Method3");    }

}

 

Следующий пример иллюстрирует создание и загрузку сборок.

Например:

// M1Server.cs

//          csc /t:library M1Server.cs     – компиляция

public class M1Server {   }

 

Ссылка из клиента на созданную DLL:

// M1Client.cs

//          csc M1Client.cs /r:M1Server.dll

using System;

using System.Diagnostics;

using System.Reflection;

class M1ClientApp

{          public static void Main()

            {          Assembly DLLAssembly =

                                Assembly.GetAssembly(typeof(M1Server));  // Запрос типа

                Console.WriteLine("M1Server.dll сборка");

                Console.WriteLine("\t" + DLLAssembly);

                Process p = Process.GetCurrentProcess();  // Запрос текущего

                                                                                          // процесса

                string AssemblyName = p.ProcessName + ".exe";

                Assembly NewAssembly = Assembly.LoadFrom(AssemblyName);

                Console.WriteLine("M1Client.exe сборка");

    Console.WriteLine("\t" + NewAssembly);

            }

}

// M1Client.cs

//          csc M1Client.cs /r:M1Server.dll /r:System.Diagnostics.dll

using System;

using System.Diagnostics;

using System.Reflection;

class M1ClientApp

{    public static void Main()

    {   Assembly DLLAssembly = Assembly.GetAssembly(typeof(M1Server));

        Console.WriteLine(DLLAssembly);

        Process p = Process.GetCurrentProcess();

        string AssemblyName = p.ProcessName + ".exe";

        Assembly NewAssembly = Assembly.LoadFrom(AssemblyName);

        Console.WriteLine(NewAssembly);

    }

}

 

Вы можете следить за любыми ответами на эту запись через RSS 2.0 ленту. Вы можете оставить ответ, или trackback с вашего собственного сайта.

Оставьте отзыв

XHTML: Вы можете использовать следующие теги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

 
Rambler's Top100