Поиск

Использование функций обратного вызова на С#

Не только функции DLL могут быть вызваны приложениями на С#, но и сами функции DLL могут также вызывать определенные методы С# из вашего приложения в сценариях с обратными вызовами. Сценарии обратного вызова включают в себя использование любой из функций Win32 ЕпитХХХ. При вызове этой функции для перечисления элементов ей передается указатель на функцию, которая будет активизироваться Windows каждый раз, когда искомый элемент будет найден. Это делается комбинированием PInvoke (для вызова функции DLL) и делегатов (для определения обратного вызова). О делегатах см. главу 14.

Этот код выполняет перечисление и вывод заголовков всех окон в системе:

using System;
using System.Runtime.InteropServices;
using System.Text;
class CallbackApp {
[Dlllmport("user32.dll")]
static extern int GetWindowText(int hWnd, StringBuilder text, int
count);
delegate bool CallbackDef(int hWnd, int IParam);
[Dlllmport("user32.dll")]
static extern int EnumWindows (CallbackDef callback, int IParam);
static bool PrintWindow(int hWnd, int IParam)
{
StringBuilder text = new StringBuilder(255);
GetWindowText(hWnd, text, 255);
"
Console.WriteLine("Window caption: {0}", text);
return true; }
static void Main() {
CallbackDef callback = new CallbackDef(PrintWindow);
EnumWindows(callback, 0); } }

Сначала я определяю \Мп32-функции EnumWindows и GetWindowText с помощью атрибута Dlllmport. Затем я определяю делегат CallbackDef 'и метод PrintWindows. После этого мне остается только в методе Main создать экземпляр делегата CallbackDef (передавая ему метод PrintWindows) и вызвать метод Епит Windows. Для каждого окна, найденного в системе, Windows вызовет метод PrintWindows.

Метод PrintWindows интересен, так как использует класс StringBuilder для создания строки фиксированной длины, которую он передает функции GetWindowText. Поэтому функция GetWindowText определяется так:

static extern int GetWindowText(int hWnd, StringBuilder text, int count);

Причина в том, что функции DLL не разрешается изменять строку, так что вы не можете использовать этот тип. И если даже попытаться осуществить передачу по ссылке, вызывающий код не может инициализировать строку с правильным размером. Тут в дело вступает класс String-Builder. Коль скоро длина текста не превышает максимального значения, переданного конструктору StringBuilder, объект StringBuilder может быть разыменован и модифицирован вызванной функцией.