Учебное пособие 1082
.pdf// идентификатор и имя каждого процесса foreach (var p in runmngProcs)
{
string info = string.Format("-> PID: {0}\tName: {1}", p.Id, p.ProcessName);
Console.WriteLine(info);
}
Console.WriteLine("************************************\n");
}
static void Main()
{
Console.WriteLine("*****Процессы*****");
ListAllRunningProcesses();
Console.ReadLine();
}
Статический метод Process.GetProcessById() получает информацию по конкретному объекту Process через идентификатор процесса (PID).
В случае запроса несуществующего PID генерируется исключение ArgumentException.
Пример получения информации об объекте Process с идентификатором 896 [1 – 3].
// если процесс 896 не существует, генерируется исключение static void GetSpeciflcProcess()
{
Process theProc = null; try
{
theProc = Process.GetProcessByld(896);
}
catch(ArgumentException ex)
{
Console.WriteLine(ex.Message);
}
}
29
Рассмотрим проход по загруженным модулям, которые обслуживаются в рамках конкретного процесса.
При получении доступа к ProcessModuleCollection через свойство Process.Modules можно извлечь список всех модулей, которые обслуживаются внутри процесса.
Пример функции перечисления модулей в процессе на основе PID.
static void EnumModsForPid (int pID)
{
Process theProc = null; try
{
theProc = Process.GetProcessByld(pID);
}
catch(ArgumentException ex)
{
Console.WriteLine(ex.Message);
return;
}
Console.WriteLine(“Модули для процесса: {0}", theProc.ProcessName);
try
{
ProcessModuleCollection theMods = theProc.Modules; foreach(ProcessModule pm in theMods)
{
string info = string.Format("-> Имя модуля: {0}“, pm.ModuleName);
Console.WriteLine(info);
}
}
catch (ArgumentException ex)
{Console.WriteLine(ex.Message);
return;
}
}
30
.NET позволяет программно смотреть домены приложений, создавать и выгружать домены приложений во время выполнения, загружать в домены приложений сборки с применением класса AppDomain (табл. 4) из пространства имен
System, из сборки mscorlib. dll.
|
Таблица 4 |
|
|
|
|
Метод |
Описание |
|
|
|
|
CreateDomain() |
Статический метод, создает новый до- |
|
|
мен приложения в текущем процессе |
|
|
|
|
Createlnstance() |
Создает экземпляр типа из внешней |
|
|
сборки после загрузки соответствующей |
|
|
сборки в вызывающий домен приложе- |
|
|
ния |
|
ExecuteAssembly() |
Запускает сборку *.ехе внутри домена |
|
|
приложения за счет предоставления |
|
|
имени её файла |
|
GetAssemblies() |
Какие сборки .NET были загружены в |
|
|
домен приложения (двоичные файлы |
|
|
СОМ и С игнорируются) |
|
GetCurrentThreadld() |
Статический метод, возвращает иден- |
|
|
тификатор активного потока в текущем |
|
|
домене приложения |
|
Load() |
Динамическая загрузка сборки в теку- |
|
|
щий домен приложения |
|
Unload() |
Статический метод выгрузки опреде- |
|
|
лённого домена приложения из кон- |
|
|
кретного процесса |
|
К используемому по умолчанию домену приложения можно получить доступ в своём приложении по статическому свойству AppDomain.CurrentDomain (табл. 5).
Затем можно привязываться к любым интересующим событиям и использовать методы и свойства AppDomain во время выполнения.
31
|
|
|
|
Таблица 5 |
|
|
|
|
|
|
|
Свойство |
|
Описание |
|
|
|
|
|
|
|||
BaseDirectory |
Извлекает путь к каталогу поиска сборок |
|
|||
|
|
|
|
|
|
CurrentDomain |
Статическое |
свойство |
идентификации |
|
|
|
домена приложения для выполняющего- |
|
|||
|
ся в текущий момент потока |
|
|
||
FriendlyName |
Получает дружественное имя текущего |
|
|||
|
домена приложения |
|
|
|
|
MonitoringIsEnabled |
Получает/устанавливает значение для |
|
|||
|
указания работы функции отслеживания |
|
|||
|
использования ресурсов ЦП и памяти |
|
|||
|
для текущего процесса. После включе- |
|
|||
|
ния функции для процесса отключить ее |
|
|||
|
нельзя |
|
|
|
|
SetupInformation |
Извлекает детали конфигурации домена |
|
|||
|
приложения |
в |
виде |
объекта |
|
|
AppDomainSetup |
|
|
|
Рассмотрим вывод сведений об используемом по умолчанию домене приложения с помощью класса AppDomain.
static void Main()
{
Console.WriteLine ("***** Домен по умолчанию *****\n");
DisplayDADStats();
Console.ReadLine();
}
private static void DisplayDADStats()
{
//доступ к домену приложения текущего потока по умолчанию
AppDomain defaultAD = AppDomain.CurrentDomain;
//дружественное имя
Console.WriteLine(“Имя домена: {0}", defaultAD.FriendlyName);
32
// идентификатор
Console.WriteLine("ID домена: {0}", defaultAD.Id); // используется ли по умолчанию
Console.WriteLine(“Домен по умолчанию? : {0}", defaultAD.IsDefaultAppDomain());
// базовый каталог
Console.WriteLine(“Директория домена: {0}", defaultAD.BaseDirectory);
}
Для получения уведомлений от CLR при загрузке новой сборки в домен приложения надо обработать событие
AssemblyLoad().
Событие имеет тип делегата
AssemblyLoadEventHandler(), который указывает на любой метод, принимающий System.Object в первом параметре, а AssemblyLoadEventArgs во втором.
Метод InitDAD() инициализирует домен по умолчанию при обработке события AssemblyLoad с помощью лямбдавыражения
private static void InitDAD()
{
//вывод имени любой сборки, загружаемой в домен приложения
//после его создания
AppDomain defaultAD = AppDomain.CurrentDomain; defaultAD.AssemblyLoad += (o, s) =>
{
Console.WriteLine("{0} загружена", s.LoadedAssembly.GetName().Name);
};
}
33
Рассмотрим пример создания специальных доменов приложений в процессе выполнения (и загрузки в них новых сборок).
Методу AppDomain.CreateDomain() передается дружест-
венное имя создаваемого домена приложения [1 – 3].
static void Main()
{
Console.WriteLine ("***Загруженные в домен сборки *****\n");
//отображение всех сборок, загруженных в домен по умолчанию
AppDomain defaultAD = AppDomain.CurrentDomain; ListAllAssembliesInAppDomain(defaultAD);
//создание нового домена приложения
MakeNewAppDomain();
Console.ReadLine();
}
private static void MakeNewAppDomain()
{
//создание нового домена приложения в текущем процессе и
//перечисление всех загруженных в него сборок
AppDomain newAD = AppDomain.CreateDomain("SecondAppDomain"); ListAllAssembliesInAppDomain(newAD);
}
static void ListAllAssembliesInAppDomain(AppDomain ad)
{
// список всех загруженных сборок, в домен по умолчанию var loadedAssemblies = from a in ad.GetAssemblies()
orderby a.GetName().Name select a;
Console.WriteLine("*** Сборка загружена в {0} *****\n",
ad.FriendlyName); foreach (var a in loadedAssemblies)
{
Console. WriteLine (“-> Имя: {0}",
34
a.GetName().Name);
Console. WriteLine (“-> Версия: {0}\n", a.GetName().Version);
}
}
CLR загружает сборки в используемый по умолчанию домен приложения по мере необходимости.
При создании вручную специальных доменов приложений сборки можно загружать в эти домены с помощью метода
AppDomain.Load().
Существует метод AppDomain.ExecuteAssembly(), кото-
рый позволяет загрузить сборку *.ехе и выполнить метод
Main().
Пример, загружающий сборку Shapes.dll в новый вторичный домен приложения [3]. Библиотеку надо скопировать в папку bin\Debug текущего приложения.
private static void MakeNewAppDomain()
{
//создание нового домена приложения в текущем процессе
AppDomain newAD = AppDomain.CreateDomain("SecondAppDomain"); try
{
//загрузка в новый домен сборки Shapes.dll
newAD.Load("Shapes");
}
catch (FileNotFoundException ex)
{
Console.WnteLine(ex.Message);
}
// вывод списка всех сборок
ListAllAssembliesInAppDomain(newAD);
}
Выгружать отдельные сборки .NET в CLR не разрешено.
35
Но с помощью метода AppDomain.Unload() можно производить избирательную выгрузку определенного домена приложения из обслуживающего процесса.
Вместе с доменом приложения будут выгружаться и все содержащиеся в нем сборки.
Тип AppDomain имеет событие DomainUnload, которое срабатывает при выгрузке специального домена приложения из содержащего его процесса. Событие ProcessExit срабатывает при выгрузке из процесса используемого по умолчанию домена, поэтому и завершение самого процесса.
Модифицируем метод MakeNewAppDomain().
private static void MakeNewAppDomain()
{
//создание нового домена приложения в текущем процессе
AppDomain newAD = AppDomain.CreateDomain("SecondAppDomain"); newAD.DomainUnload += (o, s) =>
{
Console.WriteLine(“Второй домен выгружен!");
}; try
{
//загрузка в новый домен сборки Shapes.dll
newAD.Load(“Shapes");
}
catch (FileNotFoundException ex)
{
Console.WriteLine(ex.Message);
}
//вывод списка всех сборок
ListAllAssembliesInAppDomain(newAD);
//уничтожение домена приложения
АррDomain.Unload(newAD) ;
}
36
В .NET не существует прямого соответствия между доменами приложений и потоками [1].
Фактически определенный AppDomain может иметь несколько потоков, выполняющихся в каждый конкретный момент времени.
Конкретный поток не привязан к одному домену приложений на протяжении своего времени существования.
Потоки могут пересекать границы доменов приложений, когда это вздумается планировщику Windows и CLR.
Но один поток не работает в более, чем одном домене приложений сразу.
Чтобы программно получить доступ к AppDomain, в котором работает текущий поток, вызывается статический метод
Thread.GetDomain().
static void ExtractAppDomainHostingThread()
{
// получить AppDomain, в котором работает текущий поток
AppDomain ad = Thread.GetDomain();
}
Особенности работы с делегатами
Простой способ создания потока – использование делегата асинхронным образом [1 - 3].
Делегат – аналог безопасных типов ссылок на методы. Класс Delegate поддерживает асинхронный вызов мето-
дов, т.е. делегат создает отдельный поток, невидимый нами. Это главное преимущество платформы .NET, если поток
создается для вызова методов в асинхронном режиме. Рассмотрим делегат BinaryOp.
public delegate int BinaryOp(int x, int y);
указывающий на метод, принимающий 2 целых числа и возвращающий целое число.
37
После компиляции сборка с определением этого делегата будет содержать определение класса, сгенерированного динамически при построении проекта на основе объявления делегата.
Пример [3].
public sealed class BinaryOp : System.MulticastDelegate
{
public BinaryOp(object target, uint functionAddress); public void Invoke(int x, int y);
public IAsyncResult Beginlnvoke(int x, int y, AsyncCallback cb, object state);
public int Endlnvoke(IAsyncResult result);
}
При объявлении делегата .NET компилятор С# строит запечатанный класс – наследник System.MulticastDelegate (на-
следник System.Delegate).
Сгенерированный Invoke() используется для вызова ме-
тода в синхронном режиме.
Поэтому вызывающий поток должен ждать, пока не завершится вызов делегата.
Метод Invoke() не нужно вызывать в коде напрямую – он вызывается неявно, при применении обычного синтаксиса вызова метода.
Пример вызова статического метода Add() в синхронном (блокирующем) режиме [3].
namespace SyncDelegateReview
{
public delegate int BinaryOp(int x, int y); class Program
{
static void Main()
{
Console.WriteLine ("***** Синхронный вызов делегата *****");
// вывести идентификатор выполняющегося потока
38