При создании приложений, использующих платформу Microsoft .NET, возникает задача применения в собственных проектах уже готовых библиотек кода, написанных на других языках. Сжатые сроки разработки и уже имеющиеся программные блоки не позволяют отказаться от готовых решений, поэтому их приходится использовать, встраивая в структуру собственных проектов.
Код, выполняющийся под управлением среды выполнения (в случае платформы .NET — среды Common Language Runtime), называется управляемым. Напротив, код, запускаемый не под управлением среды, называется неуправляемым. Примером неуправляемого кода могут служить COM-компоненты, Microsoft ActiveX интерфейсы и функции API Win32.
Microsoft .NET Framework позволяет взаимодействовать с COM-компонентами, COM+-службами, внешними типами библиотек и разными службами операционной системы. Перехват исключений различается в управляемом и неуправляемом коде. Платформа .NET Framework предлагает две службы взаимодействия управляемого кода с неуправляемым — Platform Invoke и COM interoperability, которые используют реальные возможности проекта.
Службы Platform Invoke позволяют управляемому коду запускать функции неуправляемого кода, которые находятся в файлах библиотек динамической компоновки (DLL). Эти службы предоставляют механизмы обнаружения и запуска неуправляемых функций и преобразование типов данных входящих и исходящих аргументов функции. Когда управляемый код запускает функцию неуправляемого кода, локализованную в DLL-файле, службы Platform Invoke находят этот DLL файл, загружают его в оперативную память и находят адрес функции в памяти. После этого службы передают входящие аргументы функции в стек, преобразовывают данные, которые необходимо перевести, эмулируют сборку мусора и передают управление по адресу неуправляемой функции в памяти.
Объектная модель COM (
Для преодоления этих различий CLR предлагает классы-оболочки, которые позволяют управляемым и неуправляемым клиентам думать, что они работают с объектами той же среды, что и они сами. Когда управляемый клиент вызывает неуправляемый метод, CLR создает временную оболочку RCW (Runtime Callable Wrapper). RCW абстрагирует различия между управляемым и неуправляемым кодом. CLR также создает оболочку
Механизм преобразования определяет, какие типы данных должны быть переданы в метод и возвращены методом из управляемого в неуправляемый код и наоборот. Большинство типов данных имеют свои аналоги как в управляемом, так и в неуправляемом коде. Они не нуждаются в конвертировании при передаче из одной среды в другую. В пространстве имен System такими типами данных являются следующие:
Но есть и такие типы данных, которые имеют неоднозначное значение в управляемом и неуправляемом языках программирования. Эти типы нуждаются в конвертировании при переходе из управляемого в неуправляемый код и наоборот. Например, управляемая строка (string) имеет несколько значений в неуправляемом коде. В таблице 5.1 представлен список типов данных, которые нуждаются в конвертировании при переходе из одной среды в другую.
| Тип данных | Тип при конвертировании |
|---|---|
| System.Array | C-style или SAFEARRAY |
| System.Boolean | 1-, 2- или 4-битовое значение со значением true как 1 или –1 |
| System.Char | Символ кодировки ANSI или Unicode |
| System.Class | Интерфейс class |
| System.Object | Различные интерфейсы |
| System.Mdarray | C-style или SAFEARRAY |
| System.String | Строка с завершающим нулем ( |
| System.Valuetype | Структура |
| System.Szarray | C-style или SAFEARRAY |
В большинстве случаев стандартные RCW и
Наиболее часто служба Platform Invoke применяется для запуска функций WinAPI, находящихся в файлах dll. Когда служба вызывает функцию, совершаются следующие операции:
Для подключения функции необходимо представить адрес библиотеки и данные о функции: название, входящие и исходящие аргументы.
Windows API — это набор функций, входящий в состав семейства операционной системы Microsoft Windows. Преимуществом использования этих функций является то, что они уже полностью готовы и не приходится тратить время на реализацию подобной функциональности. Однако с ними тяжело работать — в частности, довольно сложно отлавливать исключения, возникающие в работе приложения.
Первым шагом в запуске неуправляемой функции является объявление функции. Функция должна быть статической (static) и внешней (extern). Далее следует импорт библиотеки, содержащей эту функцию. Импортировать библиотеку нужно, используя атрибут DllImport, который находится в пространстве имен System.Runtime.InteropServices. Атрибут DllImport нуждается в названии библиотеки и может принимать один из параметров, указанных в таблице 5.2:
| Аргумент | Описание |
|---|---|
| EntryPoint | Указывает название функции. Если название метода совпадает с названием неуправляемой функции, то этот аргумент ставить необязательно |
| CharSet | Указывает кодировку строковых значений. Значением по умолчанию является ANSI |
| ExactSpelling | Предотвращает изменение точки входа ( |
| CallingConvention | Указывает тип конвертирования аргументов, передаваемых в функцию. Значением по умолчанию является WinAPI |
| PreserveSig | Указывает, что сигнатура управляемого метода не должна трансформироваться в сигнатуру неуправляемого метода, который возвращает значение типа HRESULT и может иметь дополнительные аргументы в качестве возвращаемых значений Значением по умолчанию является true (сигнатура не должна трансформироваться). |
| SetLastError | Указывает возможность использования метода GetLastError для обработки ошибок при выполнении неуправляемого метода. Значением по умолчанию является false |
Аргументы, которые принадлежат к простым типам данных, такие как byte или int, Platform Invoke автоматически преобразовывает в соответствующие типы в неуправляемой платформе. В таблице 5.3 приводится соответствие типов данных в функциях WinAPI и библиотеки .NET Framework.
| Тип данных в неуправляемом коде | Тип данных в .NET Framework (C#) |
|---|---|
| HANDLE | Int |
| BYTE | Byte |
| SHORT | Short |
| WORD | Ushort |
| INT | Int |
| UINT | Uint |
| LONG | Int |
| ULONG | Uint |
| BOOLEAN | Int |
| CHAR | Char |
| LPSTR (и большинство строковых типов данных) | String для входящих аргументов, StringBuilder для двусторонних аргументов |
| FLOAT | Float |
| DOUBLE | double |
При указании библиотеки не нужно указывать путь до нее. CLR ищет библиотеку в каталоге приложения, затем в подкаталогах, в папке Windows и папке Window\System32. При указании адреса библиотеки могут возникнуть исключения — CLR проверяет только его. Например, если был указан адрес "C:\Windos\system32\someDll.dl", а у пользователя операционная система располагается на диске D — приложение работать не будет. При использовании функций WinAPI сами файлы библиотек не нужно включать в проект.
Итак, приступим к практике. Создайте новое Windows-приложение и назовите его GetUserName. Перетаскиваем на форму из окна ToolBox кнопку и надпись. Устанавливаем следующие свойства формы и элементов управления:
| Form1, форма, свойство | Значение |
|---|---|
| Size | 560; 96 |
| Text | Получение имени пользователя |
| label1, свойство | Значение |
|---|---|
| Name | lblGetUserName |
| Location | 168; 20 |
| Size | 368; 23 |
| Text |
| Button1, свойство | Значение |
|---|---|
| Name | btnGetUserName |
| Location | 16; 20 |
| Text | Получить! |
В окне Solution Explorer щелкаем правой кнопкой на названии проекта и выбираем Add\Add New Item…. В появившемся окне шаблонов выбираем класс и называем его WinAPIClass.cs. Переходим в его код и добавляем пространство имен для работы с библиотеками dll и объектом StringBuilder:
using System; using System.Runtime.InteropServices; using System.Text;
Обращаемся к библиотеке secur32.dll и создаем метод GetUserNameEx (привожу пространство имен класса полностью
namespace GetUserName
{
public class WinAPIClass
{
#region Получение имени текущего пользователя
/// <summary>
/// Получение имени текущего пользователя.
/// </summary>
/// <param name="nameFormat">Формат имени из перечисления NameFormat.</param>
/// <param name="userName">Выходной параметр — имя.пользователя</param>
/// <param name="userNameSize">Количество символов в имени.</param>
/// <returns></returns>
//Подключение библиотеки secur32.dll
[DllImport("secur32.dll", CharSet=CharSet.Auto)]
public static extern int GetUserNameEx (int nameFormat, StringBuilder userName, ref uint userNameSize);
/// <summary>
/// Формат имени.
/// </summary>
public enum NameFormat:int
{
NameUnknown = 0,
NameFullyQualifiedDN = 1,
NameSamCompatible = 2,
NameDisplay = 3,
NameUniqueId = 6,
NameCanonical = 7,
NameUserPrincipal = 8,
NameCanonicalEx = 9,
NameServicePrincipal = 10,
NameDnsDomain = 12
};
#endregion
}
}
Все! Класс, использующий функцию WinAPI, готов. В обработчике кнопки btnGetUserName обращаемся к созданному классу:
private void btnGetUserName_Click(object sender, System.EventArgs e)
{
StringBuilder builder = new StringBuilder(100);
uint size = 100;
WinAPIClass.GetUserNameEx((int)WinAPIClass.NameFormat.NameSamCompatible, builder, ref size);
lblGetUserName.Text = String.Format("Имя пользователя: {0}", builder.ToString());
}
Обратите внимание на то, что в качестве значения перечисления идет тип данных int, объект StringBuilder передается в качестве строки и к последнему параметру добавляется ключевое слово ref, т.к. параметр передается по ссылке. Результатом запуска приложения будет определение имени текущего пользователя (рис. 5.1).
(рис 5.1) Результат запуска приложения GetUserNameКласс WinAPIClass, в котором происходит обращение к библиотеке secur32.dll, мог быть написан на другом, C-подобном языке, в оригинале он был реализован именно на C. Интересно сравнить типы данных двух листингов объявления методов:
GetUserNameEx на языке СBOOLEAN GetUserNameEx( EXTENDED_NAME_FORMAT // формат имени NameFormat, // буфер для названия LPTSTR lpNameBuffer, // размер буфера для названия PULONG nSize );
GetUserNameEx на языке С#[DllImport("secur32.dll", CharSet=CharSet.Auto)]
public static extern int GetUserNameEx
(int nameFormat, StringBuilder userName,
ref uint userNameSize);
Выбор типа данных может повлиять на выполнение преобразования входящих и исходящих параметров. Например, в библиотеках WinAPI тип данных long — 32-битный, в то время как в C# тип данных long — 64-битный.
На диске, прилагаемом к книге, вы найдете приложение GetUserName (Code\Glava5\GetUserName).
Всякий раз, когда мы начинаем изучать новый язык программирования, в число первых примеров входит вывод текстовой строки на экран. Операционная система Windows содержит определенное число типов диалоговых окон, с помощью которых пользователь получает уведомления о различных событиях. Создайте новое Windows-приложение и назовите его MessageBox. Располагаем на форме четыре кнопки и устанавливаем следующие свойства формы и кнопок:
| Form1, форма, свойство | Значение |
|---|---|
| Size | 232; 144 |
| Text |
| Button1, свойство | Значение |
|---|---|
| Name | btnABORTRETRYIGNORE |
| Location | 8; 8 |
| Size | 200; 23 |
| Text | Прервать Повторить Пропустить |
| Button2, свойство | Значение |
|---|---|
| Name | btnYESNOCANCEL |
| Location | 8; 32 |
| Size | 200; 23 |
| Text | Да Нет Отмена |
| Button3, свойство | Значение |
|---|---|
| Name | btnOK |
| Location | 8; 56 |
| Size | 200; 23 |
| Text | OK |
| Button4, свойство | Значение |
|---|---|
| Name | btnYESNO |
| Location | 8; 80 |
| Size | 200; 23 |
| Text | Да Нет |
Снова добавляем к проекту класс и называем его WinAPIClass.cs.
Листинг этого класса:
using System;
using System.Runtime.InteropServices;
namespace MessageBox
{
public class WinAPIClass
{
#region Вызов диалогового окна MessageBox
/// <summary>
/// Вызов диалогового окна MessageBox.
/// </summary>
/// <param name="handle">Родительская форма окна</param>
/// <param name="text">Текст окна.</param>
/// <param name="title">Заголовок окна.</param>
/// <param name="type">Тип окна.</param>
/// <returns></returns>
[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern int MessageBox(IntPtr handle, string text, string title, uint type);
/// <summary>
/// Тип диалогового окна.
/// </summary>
public enum MessageBoxType:int
{
/// <summary>
/// Три кнопки — Abort, Retry, Ignore
/// </summary>
MB_ABORTRETRYIGNORE = 2,
/// <summary>
/// Три кнопки — Cancel, Try, Continue
/// </summary>
MB_CANCELTRYCONTINUE = 6,
/// <summary>
/// Одна кнопка — Ok.
/// </summary>
MB_OK = 0,
/// <summary>
/// Две кнопки — Ok, Cancel.
/// </summary>
MB_OKCANCEL = 1,
/// <summary>
/// Две кнопки — Retry, Cancel
/// </summary>
MB_RETRYCANCEL = 5,
/// <summary>
/// Две кнопки — Yes, No
/// </summary>
MB_YESNO = 4,
/// <summary>
/// Три кнопки — Yes, No, Cancel
/// </summary>
MB_YESNOCANCEL = 3,
/// <summary>
/// Иконка-восклицание
/// </summary>
MB_ICONEXCLAMATION = 0x30,
/// <summary>
/// Иконка-предупреждение
/// </summary>
MB_ICONWARNING = 0x30,
/// <summary>
/// Иконка-информация
/// </summary>
MB_ICONINFORMATION = 0x40,
/// <summary>
/// Иконка-вопрос
/// </summary>
MB_ICONQUESTION = 0x20,
/// <summary>
/// Иконка-стоп
/// </summary>
MB_ICONSTOP = 0x10,
/// <summary>
/// Иконка-ошибка
/// </summary>
MB_ICONERROR = 0x10,
}
/// <summary>
/// Тип возвращаемого значения.
/// </summary>
public enum MessageBoxReturnType:int
{
IDABORT = 3,
IDCANCEL = 2,
IDCONTINUE = 11,
IDIGNORE = 5,
IDNO = 7,
IDOK = 1,
IDRETRY = 4,
IDTRYAGAIN = 10,
IDYES = 6
}
#endregion
}
}
В перечислении MessageBoxType приводятся различные типы стандартных окон и иконок. В обработчиках кнопок выбираем несколько вариантов окон и иконок:
private void btnABORTRETRYIGNORE_Click(object sender, System.EventArgs e)
{
//Текст сообщения
string text = "Hello World";
//Заголовок окна
string title = "From WinApi";
// Тип данных IntPtr — указатель на объект.
WinAPIClass.MessageBox(IntPtr.Zero, text, title, (uint)(WinAPIClass.MessageBoxType.MB_ABORTRETRYIGNORE | WinAPIClass.MessageBoxType.MB_ICONQUESTION));
}
private void btnYESNOCANCEL_Click(object sender, System.EventArgs e)
{
string text = "Hello World";
string title = "From WinApi";
WinAPIClass.MessageBox(IntPtr.Zero, text, title, (uint)(WinAPIClass.MessageBoxType.MB_YESNOCANCEL | WinAPIClass.MessageBoxType.MB_ICONINFORMATION));
}
private void btnOK_Click(object sender, System.EventArgs e)
{
string text = "Hello World";
string title = "From WinApi";
WinAPIClass.MessageBox(IntPtr.Zero, text, title, (uint)(WinAPIClass.MessageBoxType.MB_OK | WinAPIClass.MessageBoxType.MB_ICONSTOP));
}
private void btnYESNO_Click(object sender, System.EventArgs e)
{
string text = "Hello World";
string title = "From WinApi";
WinAPIClass.MessageBox(IntPtr.Zero, text, title, (uint)(WinAPIClass.MessageBoxType.MB_YESNO | WinAPIClass.MessageBoxType.MB_ICONEXCLAMATION));
}
Запускаем приложение. При нажатии на кнопки появляются соответствующие диалоговые окна (рис. 5.2).
(рис 5.2) Результат работы приложения MessageBoxНа языке С метод в обработчике будет выглядеть следующим образом (сравните преобразование типов данных!):
INT MessageBox(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType);
На диске, прилагаемом к книге, вы найдете приложение MessageBox. (Code\Glava5\MessageBox).
При включении компьютера и загрузке операционной системы раздается сигнал из динамика на системном блоке. Режим залипания клавиш, включаемый при пятикратном нажатии клавиши Shift, также сопровождается звуковым сигналом. Этот простейший динамик не может воспроизводить сложные звуки, но его иногда можно использовать, когда необходимо обратить внимание пользователя на какое-либо событие, не будучи уверенным в том, что на его компьютере имеются звуковые колонки.
Создайте новое Windows-приложение и назовите его EasySound. Располагаем на форме семь кнопок и устанавливаем следующие свойства формы и кнопок:
| Form1, форма, свойство | Значение |
|---|---|
| Size | 344; 264 |
| Text | Easy Sound |
| Button1, свойство | Значение |
|---|---|
| Name | btnDo |
| Location | 0; 0 |
| Size | 48; 232 |
| Text | До |
| Button2, свойство | Значение |
|---|---|
| Name | btnRe |
| Location | 48; 0 |
| Size | 48; 232 |
| Text | Ре |
| Button3, свойство | Значение |
|---|---|
| Name | btnMi |
| Location | 96; 0 |
| Size | 48; 232 |
| Text | Ми |
| Button4, свойство | Значение |
|---|---|
| Name | btnFa |
| Location | 144; 0 |
| Size | 48; 232 |
| Text | Фа |
| Button5, свойство | Значение |
|---|---|
| Name | btnSol |
| Location | 192; 0 |
| Size | 48; 232 |
| Text | Соль |
| Button6, свойство | Значение |
|---|---|
| Name | btnLya |
| Location | 240; 0 |
| Size | 48; 232 |
| Text | Ля |
| Button7, свойство | Значение |
|---|---|
| Name | btnС |
| Location | 288; 0 |
| Size | 48; 232 |
| Text | Си |
Добавляем класс WinAPIClass:
using System;
using System.Runtime.InteropServices;
namespace EasySound
{
public class WinAPIClass
{
#region Вызов сигнала
/// <summary>
/// Вызов сигнала.
/// </summary>
/// <param name="frequency">Частота сигнала в герцах. Диапазон от 37 до 32767 Гц.</param>
/// <param name="duration">Продолжительность сигнала в миллисекундах.</param>
/// <returns></returns>
[DllImport("Kernel32.dll")]
public static extern bool Beep (int frequency, int duration);
#endregion
}
}
Создаем обработчики для кнопок:
private void btnDo_Click(object sender, System.EventArgs e)
{
//В скобках: первый параметр — частота, второй — продолжительность сигнала
WinAPIClass.Beep(1000, 100);
}
private void btnRe_Click(object sender, System.EventArgs e)
{
WinAPIClass.Beep(3000, 100);
}
private void btnMi_Click(object sender, System.EventArgs e)
{
WinAPIClass.Beep(5000, 100);
}
private void btnFa_Click(object sender, System.EventArgs e)
{
WinAPIClass.Beep(7000, 100);
}
private void btnSol_Click(object sender, System.EventArgs e)
{
WinAPIClass.Beep(9000, 100);
}
private void btnLya_Click(object sender, System.EventArgs e)
{
WinAPIClass.Beep(11000, 100);
}
private void btnC_Click(object sender, System.EventArgs e)
{
WinAPIClass.Beep(13000, 100);
}
Запускаем приложение. Я не уверен, что в музыкальном смысле создаваемые сигналы в точности идентичны своим названиям, но тем не менее определенное сходство наверняка есть (рис. 5.3).
(рис 5.3) Приложение Easy SoundНа языке С метод в обработчике будет выглядеть следующим образом:
BOOL Beep(DWORD dwFreq, DWORD dwDuration);
На диске, прилагаемом к книге, вы найдете приложение EasySound (Code\Glava5\EasySound).
Обычная Windows-форма тоже может быть анимирована, что иногда используется для создания заставки автозагрузочных дисков (рис. 5.4). К сожалению, возможное отсутствие библиотеки .NET Framework на компьютере пользователя делает ненадежным использование такого рода приложений, написанных на C#. Остается надеяться, что выход новой версии – Microsoft Windows Longhorn — изменит ситуацию в лучшую сторону (подробнее о распространении приложений — в лекции 9).
(рис 5.4) Анимированная форма — заставка автозагрузочного дискаСоздайте новое приложение и назовите его AnimatedWindow. Расположите на форме три кнопки и установите следующие свойства формы и кнопок:
| Form1, форма, свойство | Значение |
|---|---|
| Text | Анимация формы |
| Button1, свойство | Значение |
|---|---|
| Name | btnAW_BLEND |
| Location | 30; 62 |
| Size | 232; 23 |
| Text | Проявление |
| Button2, свойство | Значение |
|---|---|
| Name | btnHOR_AW_SLIDE |
| Location | 30; 118 |
| Size | 232; 23 |
| Text | Горизонтальное появление |
| Button3, свойство | Значение |
|---|---|
| Name | btnCenter_AW_SLIDE |
| Location | 30; 182 |
| Size | 232; 23 |
| Text | Появление из центра |
Добавляем класс WinAPIClass:
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace AnimatedWindow
{
public class WinAPIClass
{
#region Анимация окна
/// <summary>
/// Тип анимации окна. Перечисление возвращает тип данных int
/// после приведения. Каждому элементу перечисления присвоено
/// свое значение типа int.
/// </summary>
[Flags]
public enum AnimateWindowFlags:int
{
AW_HOR_POSITIVE = 1,
AW_HOR_NEGATIVE = 2,
AW_VER_POSITIVE = 4,
AW_VER_NEGATIVE = 8,
AW_CENTER = 16,
AW_HIDE = 65536,
AW_ACTIVATE = 131072,
AW_SLIDE = 262144,
AW_BLEND = 524288
};
/// <summary>
/// Анимация окна.
/// </summary>
/// <param name="hwnd">Окно.</param>
/// <param name="dwTime">Время.</param>
/// <param name="dwFlags">Тип анимации. Если в неуправляемом
/// коде используется перечисление, то его нужно конвертировать
/// в тип данных int. </param>
/// <returns></returns>
[DllImportAttribute("user32.dll",EntryPoint="AnimateWindow",SetLastError=true)]
public static extern bool AnimateWindow(IntPtr hwnd,int dwTime,int dwFlags);
/// <summary>
/// Анимация окна.
/// </summary>
/// <param name="ctrl">Окно.</param>
/// <param name="dwTime">Время.</param>
/// <param name="Flags">Флаги.</param>
/// <returns></returns>
public static bool AnimateWindow(Control ctrl,int dwTime, AnimateWindowFlags Flags)
{
return AnimateWindow(ctrl.Handle,dwTime,(int)Flags);
}
#endregion
}
}
Создаем обработчики кнопок:
private void btnAW_BLEND_Click(object sender, System.EventArgs e)
{
// Скрываем окно
this.Hide();
// Запускаем анимацию. Второй параметр в скобках — время анимации в миллисекундах.
WinAPIClass.AnimateWindow(this, 3000, WinAPIClass.AnimateWindowFlags.AW_ACTIVATE|WinAPIClass.AnimateWindowFlags.AW_BLEND);
// Отображаем кнопки после анимации
this.btnAW_BLEND.Invalidate();
this.btnHOR_AW_SLIDE.Invalidate();
this.btnCenter_AW_SLIDE.Invalidate();
}
private void btnHOR_AW_SLIDE_Click(object sender, System.EventArgs e)
{
this.Hide();
WinAPIClass.AnimateWindow(this, 3000, WinAPIClass.AnimateWindowFlags.AW_HOR_POSITIVE|WinAPIClass.AnimateWindowFlags.AW_SLIDE);
this.btnAW_BLEND.Invalidate();
this.btnHOR_AW_SLIDE.Invalidate();
this.btnCenter_AW_SLIDE.Invalidate();
}
private void btnCenter_AW_SLIDE_Click(object sender, System.EventArgs e)
{
this.Hide();
WinAPIClass.AnimateWindow(this, 3000, WinAPIClass.AnimateWindowFlags.AW_CENTER|WinAPIClass.AnimateWindowFlags.AW_SLIDE);
this.btnAW_BLEND.Invalidate();
this.btnHOR_AW_SLIDE.Invalidate();
this.btnCenter_AW_SLIDE.Invalidate();
}
При запуске приложения доступны три вида анимации (рис. 5.5). Добавьте кнопок на форму и попробуйте вызвать другие эффекты перечисления AnimateWindowFlags.
(рис 5.5) Анимация формы. А — готовое приложение, Б — проявление (изменение прозрачности), В — горизонтальное появление справа, Г — появление из центра (расширение)На языке С метод в обработчике будет выглядеть следующим образом:
BOOL AnimateWindow(HWND hwnd, DWORD dwTime, DWORD dwFlags);
На диске, прилагаемом к книге, вы найдете приложение AnimatedWindow (Code\Glava5\ AnimatedWindow).
Использование функций WinAPI позволяет добавлять в свои приложения даже такую функциональность, как завершение работы системы и ее перезагрузка. Создайте новое Windows- приложение и назовите его StartTurnOff. Добавляем на форму пять кнопок и устанавливаем следующие значения формы и кнопок:
| Form1, форма, свойство | Значение |
|---|---|
| BackgroundImage | Code\Glava5\StartTurnOff\Image\Window.bmp |
| FormBorderStyle | None |
| Size | 314; 400 |
| StartPosition | CenterScreen |
| Button1, свойство | Значение |
|---|---|
| Name | BtnCancel |
| Location | 243; 169 |
| Size | 61; 19 |
| Text | Отмена |
| Button2, свойство | Значение |
|---|---|
| Name | btnCancel2 |
| Location | 245; 369 |
| Size | 61; 19 |
| Text | Отмена |
| Button3, свойство | Значение |
|---|---|
| Name | BtnTurnOff |
| Image | Code\Glava5\StartTurnOff\Image\TurnOff.bmp |
| Location | 140; 80 |
| Size | 34; 34 |
| Text |
| Button4, свойство | Значение |
|---|---|
| Name | BtnRestart |
| Image | Code\Glava5\StartTurnOff\Image\Restart.bmp |
| Location | 238; 81 |
| Size | 34; 34 |
| Text |
| Button5, свойство | Значение |
|---|---|
| Name | BtnUserOut |
| Image | Code\Glava5\StartTurnOff\Image\UserOut.bmp |
| Location | 198; 280 |
| Size | 34; 34 |
| Text |
Добавляем класс WinAPIClass:
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Text;
namespace StartTurnOff
{
public class WinAPIClass
{
#region Завершение работы Windows.
/// <summary>
/// Завершение работы.
/// </summary>
/// <param name="shutDownType">Тип завершения работы.</param>
/// <param name="reason">Причина</param>
/// <returns></returns>
[DllImport("user32.dll")]
public static extern bool ExitWindowsEx(uint shutDownType, string reason);
//To shut down or restart the system, the calling process must use the
//AdjustTokenPrivileges function to enable the SE_SHUTDOWN_NAME privilege.
// Для выключения или перезагрузки системы вызываемый процесс
//должен использовать функцию AdjustTokenPrivileges для получения
//определенного уровня доступа (привилегии)
/// <summary>
/// Тип завершения работы.
/// </summary>
public enum ShutdownType:int
{
LogOf = 0,
Shutdown = 0x00000001,
Reboot = 0x00000002,
PowerOf = 0x00000008
}
#endregion
// Дополнительные переменные
public const int SE_PRIVILEGE_ENABLED = 2;
public const string SE_SHUTDOWN_NAME = "SeShutdownPrivilege";
public const int ANYSIZE_ARRAY = 1;
public const int TOKEN_QUERY = 8;
public const int TOKEN_ADJUST_PRIVILEGES = 32;//0x00000020;
// Структура, используемая для передачи в метод AdjustTokenPrivileges
// в качестве необходимой привилегии.
[StructLayout(LayoutKind.Sequential, Pack=1)]
internal struct TokPriv1Luid
{
// Количество привилегий в наборе.
public int Count;
public long Luid;
// Тип привилегии.
public int Attr;
}
/// <summary>
/// Возвращение указателя на текущий процесс.
/// </summary>
/// <returns></returns>
[DllImport("kernel32.dll", ExactSpelling=true) ]
internal static extern IntPtr GetCurrentProcess();
/// <summary>
/// Открывание процесса access token, который содержит информацию о
///безопасности текущего пользователя,
/// привязанного к заданному процессу.
/// </summary>
/// <param name="h">Указатель на процесс.</param>
/// <param name="acc">Тип access token.</param>
/// <param name="phtok">Указатель на access token.</param>
/// <returns></returns>
[DllImport("advapi32.dll", ExactSpelling=true) ]
internal static extern bool OpenProcessToken( IntPtr h, int acc, ref IntPtr phtok );
/// <summary>
/// Возвращение значения переменной в текущей сессии ОС.
/// </summary>
/// <param name="host">Название системы. Если значение null,
///поиск осуществляется
/// в локальной системе.</param>
/// <param name="name">Название переменной.</param>
/// <param name="pluid">Указатель на значение переменной.</param>
/// <returns></returns>
[DllImport("advapi32.dll") ]
internal static extern bool LookupPrivilegeValue( string host, string name, ref long pluid );
/// <summary>
/// Включение указанной привилегии.
/// </summary>
/// <param name="htok">Указатель на access token</param>
/// <param name="disall">Необходимость выключения всех привилегий.</param>
/// <param name="newst">Новое значение привилегии. Если предыдущее
/// значение установлено в true,
/// то это значение не учитывается.</param>
/// <param name="len">Размер буфера для следующего значения.</param>
/// <param name="prev">Предыдущая привилегия.</param>
/// <param name="relen">Указатель на размер полученных привилегий.</param>
/// <returns></returns>
[DllImport("advapi32.dll", ExactSpelling=true) ]
internal static extern bool AdjustTokenPrivileges( IntPtr htok, bool disall,
ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen );
}
}
В коде формы добавляем обработчики кнопок:
private void btnCancel_Click(object sender, System.EventArgs e)
{
this.Close();
}
private void btnUserOut_Click(object sender, System.EventArgs e)
{
WinAPIClass.ExitWindowsEx((uint)WinAPIClass.ShutdownType.LogOf, "Win Api test");
}
private void btnTurnOff_Click(object sender, System.EventArgs e)
{
RestartOrShotDown(WinAPIClass.ShutdownType.Shutdown);
}
private void btnRestart_Click(object sender, System.EventArgs e)
{
RestartOrShotDown(WinAPIClass.ShutdownType.Reboot);
}
void RestartOrShotDown(WinAPIClass.ShutdownType type)
{
WinAPIClass.TokPriv1Luid tp;
IntPtr hproc = WinAPIClass.GetCurrentProcess();
IntPtr hToken = IntPtr.Zero;
WinAPIClass.OpenProcessToken( hproc, WinAPIClass.TOKEN_ADJUST_PRIVILEGES | WinAPIClass.TOKEN_QUERY, ref hToken );
tp.Count = 1;
tp.Luid = 0;
tp.Attr = WinAPIClass.SE_PRIVILEGE_ENABLED;
WinAPIClass.LookupPrivilegeValue(null, WinAPIClass.SE_SHUTDOWN_NAME, ref tp.Luid );
WinAPIClass.AdjustTokenPrivileges(hToken, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero );
WinAPIClass.ExitWindowsEx((uint)type, "Win Api test");
}
private void btnCancel2_Click(object sender, System.EventArgs e)
{
this.Close();
}
При запуске приложения появляется совмещенное окно завершения работы и смены пользователя (рис. 5.6). Кнопки "Выключение", "Перезагрузка" и "Выход" лучше всего проверять, завершив работу со средой Visual Studio .NET.
(рис 5.6) Приложение StartTurnOffНа диске, прилагаемом к книге, вы найдете приложение StartTurnOff (Code\Glava5\StartTurnOff).
Рассмотренные примеры позволяют сделать вывод: использование функций WinAPI относится к одному из самых быстрых способов разработки приложений. При этом основная трудность заключается не в добавлении функции к приложению, а в ее поиске. В самом деле, операционная система Windows представляет чрезвычайно большой набор функций. Библиотека MSDN содержит подробное описание практически всех функций — для просмотра их списка в алфавитном порядке выбираем Пуск\Все программы\Microsoft Visual Studio .NET 2003\Microsoft Visual Studio .NET 2003 Documentation, переходим на вкладку Поиск (Search) и в строке поиска набираем WinAPI functions. В списке статей появится заголовок Functions in
(рис 5.7) Список функций WinAPI в алфавитном порядке в библиотеке MSDNЯ привел методику поиска, а не расположение в содержании, — в зависимости от версии структура библиотеки может меняться, но она неизменно будет содержать список функций.
При изучении синтаксиса языка C# вы проходили класс String (пространство имен System.String) и, возможно, класс StringBuilder (пространство имен System.Text). Если эти понятия не совсем вам ясны, освежите материал — класс String описывается в любом учебнике по C#. Здесь же мы кратко рассмотрим класс StringBuilder — в следующем примере этот класс в качестве примера при вызове очередной функции WinAPI.
Одним из основных преимуществ использования этого класса является скорость работы. Упрощенно говоря, при изменении строки, созданной как экземпляр класса String, у нас создается каждый раз новый экземпляр класса, а старый уничтожается; при использовании же класса StringBuilder мы всегда работаем с одним экземпляром.
Экземпляр String Builder создается так же, как и другие экземпляры классов:
StringBuilder sb1=new StringBuilder("Hello");
Класс StringBuilder содержит только SBuilder
У класса StringBuilder нет статических методов. Все его методы — динамические. Ниже перечислены основные свойства и методы класса StringBuilder.
Свойство Length. Возвращает длину строки. Пример (здесь и далее см. рис. 5.8):
StringBuilder sb1=new StringBuilder("Hello");
int k=sb1.Length;
Console.WriteLine("Длина строки sb1 "+k);
Свойство только для чтения MaxCapacity. Возвращает максимальное количество символов, которые можно записать в экземпляр класса StringBuilder. Пример:
StringBuilder sb1=new StringBuilder("Hello");
System.Console.WriteLine("Максимальное количество символов "+sb1.MaxCapacity);
Метод Append. Добавляет значение новой строки к существующему значению. Пример:
StringBuilder sb1=new StringBuilder("Hello");
StringBuilder sb2=new StringBuilder(" World ");
sb1.Append(sb2);
sb1.Append("!!!");
System.Console.WriteLine(sb1);
Метод Equals. Служит для сравнения двух строк. Возвращает true или false. Пример использования:
StringBuilder sb1=new StringBuilder("Hello");
StringBuilder sb2=new StringBuilder(" World ");
if(sb1.Equals(sb2))
System.Console.WriteLine("Строки равны");
else
System.Console.WriteLine("Строки не равны");
Метод Insert. Вставляет символы в заданную позицию (нумерация начинается с нуля). Пример использования:
StringBuilder sb3=new StringBuilder("Пртствие!");
sb3.Insert(2, "иве");
System.Console.WriteLine(sb3);
Метод Remove. Удаляет символы из строки. Первый параметр метода Remove — номер позиции, с которой начинается удаление, второй — количество удаляемых символов. Пример:
StringBuilder sb4=new StringBuilder("Казнить нельзя, помиловать");
sb4.Insert(7, ",");
sb4.Remove(15, 1);
System.Console.WriteLine(sb4);
Метод Replace. Заменяет символы. Пример:
StringBuilder sb5=new StringBuilder("Internet");
sb5.Replace("er", "ra");
System.Console.WriteLine(sb5);
(рис 5.8) Результаты приложения SBuilderНа диске, прилагаемом к книге, вы найдете приложение SBuilder (Code\Glava5\SBuilder).
Сравним теперь работу классов StringBuilder и String, используя точный счетчик времени выполнения задачи. Фактически вычисления производятся в тактах процессора, а потом переводятся в миллисекунды. Создайте новое консольное приложение и назовите его PrecisionCounter. Добавляем новый класс к проекту и называем его MechanismCounter. Полный листинг этого класса с комментариями:
using System;
using System.Runtime.InteropServices;
using System.Security;
namespace PrecisionCounter
{
public class MechanismCounter
{
static Int64 _start;
static Int64 finish;
static Int64 freq;
/// <summary>
/// Начало подсчета времени выполнения.
/// </summary>
public static void Start()
{
MechanismCounter._start = 0;
MechanismCounter.QueryPerformanceCounter(ref _start);
}
/// <summary>
/// Завершение подсчета времени выполнения и возвращение времени в секундах.
/// </summary>
/// <returns>Время в секундах, потраченное на выполнение участка
/// кода. Десятичная часть отражает доли секунды.</returns>
public static float Finish()
{
MechanismCounter.finish = 0;
MechanismCounter.QueryPerformanceCounter(ref finish);
MechanismCounter.freq = 0;
MechanismCounter.QueryPerformanceFrequency(ref freq);
return (((float)(MechanismCounter.finish - MechanismCounter._start))/((float)MechanismCounter.freq));
}
/// <summary>
/// Получение текущего значения тактов процессора.
/// </summary>
/// <param name="performanceCount">Переменная, в которую запишется значение.</param>
/// <returns></returns>
[SuppressUnmanagedCodeSecurity]
[DllImport("Kernel32.dll")]
static extern bool QueryPerformanceCounter(ref Int64 performanceCount);
/// <summary>
/// Получение текущей частоты (количество тактов / секунда) работы процессора.
/// </summary>
/// <param name="frequency">Переменная, в которую запишется значение.</param>
/// <returns></returns>
[SuppressUnmanagedCodeSecurity]
[DllImport("Kernel32.dll")]
static extern bool QueryPerformanceFrequency(ref Int64 frequency);
}
}
Итак, механизм счетчика готов. Запустим теперь две задачи: будем слагать строки, используя один раз String, а второй — StringBuilder:
using System;
using System.Text;
namespace PrecisionCounter
{
class TestCounter
{
[STAThread]
static void Main(string[] args)
{
Output("Запуск процесса подсчета времени выполнения сложения строк с использованием типа данных string");
MechanismCounter.Start();
string s = String.Empty;
for(int i = 0; i < 10000; i++)
{
s+="TestPerfCounter";
}
float timeString = MechanismCounter.Finish();
Output("Завершение процесса подсчета времени. Время выполнения (сек): " + timeString);
Output("Запуск процесса подсчета времени выполнения сложения строк с использованием типа данных StringBuilder");
MechanismCounter.Start();
StringBuilder sb = new StringBuilder();
for(int i = 0; i < 10000; i++)
{
sb.Append("TestCounter");
}
float timeStringBuilder = MechanismCounter.Finish();
Output("Завершение процесса подсчета времени. Время выполнения (сек): " + timeStringBuilder);
}
/// <summary>
/// Вывод на экран заданного значения.
/// </summary>
/// <param name="s">Значение.</param>
static void Output(string s)
{
Console.WriteLine(s);
}
}
}
Как и следовало ожидать, использование класса StringBuilder значительно ускоряет работу (рис. 5.9).
(рис 5.9) Результат работы приложения PrecisionCounterНа диске, прилагаемом к книге, вы найдете приложение PrecisionCounter (Code\Glava5\ PrecisionCounter).
Операционная система Windows предоставляет возможность снятия снимков с экрана (скриншотов) — при нажатии клавиши Print Screen текущее изображение помещается в буфер обмена. Затем можно вставить изображение непосредственно в документ или графический редактор — для последующего сохранения в нужном формате. Сделаем что-то подобное — простое приложение для фотографирования экрана и последующего сохранения изображений. Создайте новое Windows-приложение и назовите его
| Form1, форма, свойство | Значение |
|---|---|
| FormBorderStyle | FixedSingle |
| Size | 174; 94 |
| Text |
| Button1, свойство | Значение |
|---|---|
| Name | btnCreate |
| BackgroundImage | Code\Glava5\ |
| Location | 0; 0 |
| Size | 176; 64 |
| Text |
Из окна Toolbox перетаскиваем на форму элемент управления SaveFileDialog — его свойства установим программно. Добавляем класс к проекту и называем его ScreenShotDll.cs. Далее привожу полный листинг этого класса с комментариями:
using System;
using System.Runtime.InteropServices;
using System.Drawing.Imaging;
using System.Drawing;
using System.IO;
namespace ScreenShot
{
/// <summary>
/// Класс для создания снимков экрана
/// </summary>
public class ScreenShotDll
{
/// <summary>
///Создание снимка
/// </summary>
/// <param name="fileName">Название файла.</param>
/// <param name="imageFormat">Формат изображения.</param>
public void CaptureScreen(string fileName,ImageFormat imageFormat)
{
int hdcSrc = User32.GetWindowDC(User32.GetDesktopWindow()),
hdcDest = GDI32.CreateCompatibleDC(hdcSrc),
hBitmap = GDI32.CreateCompatibleBitmap(hdcSrc,
GDI32.GetDeviceCaps(hdcSrc,8),GDI32.GetDeviceCaps(hdcSrc,10));
GDI32.SelectObject(hdcDest,hBitmap);
GDI32.BitBlt(hdcDest,0,0,GDI32.GetDeviceCaps(hdcSrc,8),
GDI32.GetDeviceCaps(hdcSrc,10),
hdcSrc,0,0,0x00CC0020);
SaveImageAs(hBitmap,fileName,imageFormat);
Cleanup(hBitmap,hdcSrc,hdcDest);
}
/// <summary>
/// Получение снимка в формате Windows Bitmap
/// </summary>
/// <returns></returns>
public Bitmap CaptureScreen()
{
int hdcSrc = User32.GetWindowDC(User32.GetDesktopWindow()),
hdcDest = GDI32.CreateCompatibleDC(hdcSrc),
hBitmap = GDI32.CreateCompatibleBitmap(hdcSrc,
GDI32.GetDeviceCaps(hdcSrc,8),GDI32.GetDeviceCaps(hdcSrc,10));
GDI32.SelectObject(hdcDest,hBitmap);
GDI32.BitBlt(hdcDest,0,0,GDI32.GetDeviceCaps(hdcSrc,8),
GDI32.GetDeviceCaps(hdcSrc,10),
hdcSrc,0,0,0x00CC0020);
Bitmap btm = GetImage(hBitmap);
Cleanup(hBitmap,hdcSrc,hdcDest);
return btm;
}
/// <summary>
/// Освобождение ресурсов системы.
/// </summary>
/// <param name="hBitmap">Указатель на структуру данных.</param>
/// <param name="hdcSrc">Указатель на структуру данных источника.</param>
/// <param name="hdcDest">Указатель на структуру данных назначения.</param>
private void Cleanup(int hBitmap, int hdcSrc, int hdcDest)
{
User32.ReleaseDC(User32.GetDesktopWindow(),hdcSrc);
GDI32.DeleteDC(hdcDest);
GDI32.DeleteObject(hBitmap);
}
/// <summary>
/// Сохранение снимка
/// </summary>
/// <param name="hBitmap">Указатель на изображение.</param>
/// <param name="fileName">Название файла.</param>
/// <param name="imageFormat">Формат изображения.</param>
private void SaveImageAs(int hBitmap, string fileName, ImageFormat imageFormat)
{
Bitmap image = new Bitmap(Image.FromHbitmap(new IntPtr(hBitmap)),
Image.FromHbitmap(new IntPtr(hBitmap)).Width,
Image.FromHbitmap(new IntPtr(hBitmap)).Height);
image.Save(fileName,imageFormat);
}
/// <summary>
/// Возвращение изображения по указателю.
/// </summary>
/// <param name="hBitmap">Указатель на изображение.</param>
/// <returns></returns>
private Bitmap GetImage(int hBitmap)
{
Bitmap image =
new Bitmap(Image.FromHbitmap(new IntPtr(hBitmap)),
Image.FromHbitmap(new IntPtr(hBitmap)).Width,
Image.FromHbitmap(new IntPtr(hBitmap)).Height);
return image;
}
}
/// <summary>
/// Реализация методов библиотеки User32
/// </summary>
class User32
{
/// <summary>
/// Возвращение указателя на рабочий стол.
/// </summary>
/// <returns></returns>
[DllImport("User32.dll")]
public static extern int GetDesktopWindow();
/// <summary>
/// Получение структуры данных.
/// </summary>
/// <param name="hWnd">Указатель на окно.</param>
/// <returns></returns>
[DllImport("User32.dll")]
public static extern int GetWindowDC(int hWnd);
/// <summary>
/// Освобождение структуры данных.
/// </summary>
/// <param name="hWnd">Указатель на окно.</param>
/// <param name="hDC">Указатель на структуру данных.</param>
/// <returns></returns>
[DllImport("User32.dll")]
public static extern int ReleaseDC(int hWnd,int hDC);
}
/// <summary>
/// Реализация методов библиотеки GDI32
/// </summary>
class GDI32
{
/// <summary>
/// Передача изображения.
/// </summary>
/// <param name="hdcDest">Указатель на назначение передачи.</param>
/// <param name="nXDest">Х координата верхнего левого угла назначения.</param>
/// <param name="nYDest">У координата верхнего левого угла назначения.</param>
/// <param name="nWidth">Ширина прямоугольника.</param>
/// <param name="nHeight">Высота прямоугольника.</param>
/// <param name="hdcSrc">Указатель на источник.</param>
/// <param name="nXSrc">Х координата верхнего левого угла источника.</param>
/// <param name="nYSrc">У координата верхнего левого угла источника.</param>
/// <param name="dwRop">Код растровой операции.</param>
/// <returns></returns>
[DllImport("GDI32.dll")]
public static extern bool BitBlt(int hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, int hdcSrc,
int nXSrc, int nYSrc,int dwRop);
/// <summary>
/// Создание и запись в структуру данных изображения.
/// </summary>
/// <param name="hdc">Указатель на структуру данных.</param>
/// <param name="nWidth">Ширина изображения.</param>
/// <param name="nHeight">Высота изображения.</param>
/// <returns></returns>
[DllImport("GDI32.dll")]
public static extern int CreateCompatibleBitmap(int hdc,int nWidth, int nHeight);
/// <summary>
/// Создание и сохранение в памяти структуры данных, совместимой с указанным устройством вывода.
/// Для помещения изображения в структуру данных
/// необходимо указать высоту, ширину и цветовой режим изображения.
/// Это можно сделать с помощью функции CreateCompatibleBitmap, поддерживаемой устройствами с растровым выводом.
/// Для получения информации об этих устройствах используется функция GetDeviceCaps.
/// После использования структуры данных ее нужно удалить с помощью функции DeleteDC.
/// </summary>
/// <param name="hdc">Указатель на существующую структуру данных. Если указатель равен null,
/// то функция создает структуру для экрана текущего приложения.</param>
/// <returns></returns>
[DllImport("GDI32.dll")]
public static extern int CreateCompatibleDC(int hdc);
/// <summary>
/// Удаление указанной структуры данных.
/// </summary>
/// <param name="hdc">Структура данных.</param>
/// <returns></returns>
[DllImport("GDI32.dll")]
public static extern bool DeleteDC(int hdc);
/// <summary>
/// Удаление графических объектов с освобождением системных ресурсов.
/// </summary>
/// <param name="hObject">Указатель на графический объект.</param>
/// <returns></returns>
[DllImport("GDI32.dll")]
public static extern bool DeleteObject(int hObject);
/// <summary>
/// Возвращение информации об указанной структуре.
/// </summary>
/// <param name="hdc">Указатель на структуру данных.</param>
/// <param name="nIndex">Индекс совместимости. .</param>
/// <returns></returns>
[DllImport("GDI32.dll")]
public static extern int GetDeviceCaps(int hdc, int nIndex);
/// <summary>
/// Выбор объекта, находящегося в указанной структуре данных.
/// </summary>
/// <param name="hdc">Указатель на структуру данных.</param>
/// <param name="hgdiobj">Указатель на объект.</param>
/// <returns></returns>
[DllImport("GDI32.dll")]
public static extern int SelectObject(int hdc, int hgdiobj);
}
}
Вся функциональность практически готова. Осталось только добавить обработчик кнопки btnCreate:
private void btnCreate_Click(object sender, System.EventArgs e)
{
SaveFileDialog diag = new SaveFileDialog();
diag.Filter = "Файлы bmp|*.bmp";
if(diag.ShowDialog() != DialogResult.OK)
return;
// Получаем адрес файла.
string filename = diag.FileName;
// Создаем экземпляр класса ScreenShotDll
ScreenShotDll shoter = new ScreenShotDll();
// Создаем и сохраняем изображение.
shoter.CaptureScreen(filename, System.Drawing.Imaging.ImageFormat.Bmp);
}
Запускаем приложение (рис. 5.10). После нажатия на кнопку задаем в появившемся окне SaveFileDialog имя файла и сохраняем его.
(рис 5.10) Приложение ScreenShot! и сделанный им снимокНа диске, прилагаемом к книге, вы найдете приложение (Code\Glava5\
При вызове объекта COM-клиентом .NET-среда Common Language Runtime создает временную оболочку RCW (Runtime Callable Wrapper), причем всего одну, независимо от количества ссылок на объект. Это дает гарантию, что все обращения к объекту будут происходит только через одного проводника. Далее CLR, используя полученные из библиотеки метаданные, создает вызываемый объект и оболочку для возврата данных. Также CLR берет на себя функцию контроля сборки мусора в оболочке — разработчику не приходится задумываться об этом.
Главной задачей оболочки RCW является скрытие различий между управляемым и неуправляемым кодом. Она командует жизненным циклом COM-объекта, передает вызовы методов между управляемым и неуправляемым кодами, конвертирует параметры методов. В результате RCW позволяет разработчикам писать код, который использует COM-объекты как обычные .NET-объекты.
В качестве примера вызова COM-компонент создадим приложение, позволяющее просматривать документы в формате PDF. Создайте новое Windows-приложение и назовите его PDF Reader. Добавляем на форму элементы OpenFileDialog и MainMenu. Устанавливаем следующие свойства формы:
| Form1, форма, свойство | Значение |
|---|---|
| Text | Обозреватель документов в формате PDF |
| Maximized |
Добавляем пункты меню:
| Name | Text | Shortcut |
|---|---|---|
| mnuFile | Файл | |
| mnuOpen | Открыть | CtrlО |
В окне Toolbox щелкаем правой кнопкой на вкладке Windows Forms, и в появившемся контекстном меню выбираем Add/Remove Items… . В окне Customize Toolbox переключаемся на вкладку COM Components и ставим галочку в чекбоксе компонента Adobe Acrobat 7.0 Browser Document (рис. 5.11).
(рис 5.11) Добавление компонента в окно ToolboxЭтот компонент появляется после установки программы Adobe Acrobat Reader 7.0. Нажимаем ОК — в окне Toolbox появился новый элемент (рис. 5.12), перетаскиваем его на форму и устанавливаем свойству Dock значение Fill.
(рис 5.12) Компонент Adobe Acrobat 7.0 Browser DocumentДобавляем обработчик пункта меню "Открыть":
private void mnuOpen_Click(object sender, System.EventArgs e)
{
openFileDialog1.Filter = "Файлы pdf|*.pdf";
openFileDialog1.ShowDialog();
axAcroPDF1.LoadFile(openFileDialog1.FileName);
}
Запускаем приложения. При открытии документа в формате pdf происходит, по сути, встраивание в форму интерфейса программы Adobe Acrobat Reader (рис. 5.13):
(рис 5.13) Результат работы приложения PDF ReaderНа диске, прилагаемом к книге, вы найдете приложение PDF Reader. (Code\Glava5\PDF – Reader).
.NET-приложения могут использовать COM-классы с ранним или поздним связыванием. При использовании модели раннего связывания информация о типах должна быть известна на этапе компиляции. Для использования COM-типов в управляемом коде необходимо сгенерировать метаданные из библиотеки типов COM-компонента.
Библиотека описания типов COM-компонента может быть независимой от компонента и располагаться в файлах с расширением .tlb, .olb или .
После обнаружения библиотеки описания типов COM-компонента можно приступить к генерации сборки, содержащей метаданные о типе. Это можно сделать с помощью утилиты tlbimp.exe (Type Library Importer). Для ее запуска выбираем Пуск\Все программы\ Microsoft Visual Studio .NET 2003 \ Visual Studio .NET Tools \ Visual Studio .NET 2003 Command Prompt и вводим название утилиты — tlbimp.exe. После генерации метаданных они сохраняются в виде обычной .NET-сборки. Такие сборки называются сборками взаимодействия, поскольку они включают в себя описание COM-компонента и не содержат кода. Утилита tlbimp.exe имеет следующий синтаксис:
Tlbimp TlbFile [/out: name] [/reference: file] [/silent] [/verbose] [/strictref] [/unsafe][ [/primary]][/publickey: file] [/keyfile: file][/keycontainer: name]
В таблице 5.4 представлено описание некоторых параметров утилиты:
| Параметр | Описание |
|---|---|
| /out:filename | Указывает адрес, название сборки и пространство имен, содержащее метаданные |
| /keyfile:filename | Параметры для генерирования сборки, подписанной строгим именем |
| /reference:filename | Если компонент ссылается на внешние сборки, то с помощью этого аргумента можно указать адрес файла, в котором необходимо искать внешние типы |
| /primary | Главная сборка взаимодействия для данного типа |
Например, для генерирования .NET-сборки myInteropassembly.dll на основании библиотеки activeds.tlb запускаем следующую строку в окне Visual Studio .NET 2003 Command Prompt:
tlbimp c:\Windows\system32\activeds.tlb /out: c:\myInteropassembly.dll
В результате на диске С появится сборка взаимодействия, которую можно будет использовать как частную или публичную (подробнее о сборке в лекции 9).
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.