Средства разработки приложений

         

Отображение библиотеки DDEML в .NET


Библиотека DDEML представляет собой 32-разрядную библиотеку платформы Win32. Ее функции не могут быть вызваны непосредственно из приложения .NET. Для того, чтобы иметь возможность работать с этой библиотекой, нужно создать ее «отображение» в среде .NET, используя специальные средства. Таким образом, типы и структуры, с которыми работает библиотека будут автоматически преобразовываться средствами .NET в типы .NET и наоборот. Для доступа к функциям используется класс DLLImportAttribute, который описан в пространстве имен System.Runtime.InteropServices. Что касается типов параметров функций, как уже было сказано, среда .NET в большинстве случаев автоматически осуществляет все необходимые преобразования. В таблице 1 показаны подобные преобразования:

Win32 .NET
DWORD uint
HCONV IntPtr
HSZ IntPtr
UINT uint
DWORD * ref uint
void* far IntPtr или можно указать [Out] byte[], где [Out] – класс System.Runtime.InteropServices, который организовывает передачу данных от вызываемого объекта к вызывающему.

Для функции обратного вызова необходимо создать делегат, который имеет соответствующую сигнатуру, и указать его в качестве параметра в функции DdeInitialize:

/// <summary>

/// Делегат функции обратного вызова DDE

/// </summary>

internal delegate IntPtr DDECallBackDelegate(

uint wType,            // Код транзакции

uint wFmt,             // Формат данных

IntPtr hConv,          // Идентификатор канала

IntPtr hsz1,           // Идентификатор строки (в нашем случае, строки раздела)

IntPtr hsz2,           // Идентификатор строки (в нашем случае, элемента данных)

IntPtr hData,          // Идентификатор глобальной области данных, где находятся данные


uint dwData1,          // Дополнительный параметр (В нашей работе не рассматривается)

uint dwData2           // Дополнительный параметр (В нашей работе не рассматривается)

      );

При этом отображение функции DdeInitialize в среде .NET будет выглядеть так:

internal class DDEML

{

[DllImport("user32.dll", EntryPoint="DdeInitialize", CharSet=CharSet.Ansi)]

internal static extern uint DdeInitialize(

      ref uint pidInst, DDECallBackDelegate pfnCallback, uint afCmd, uint ulRes);

...

}

Ниже, я привожу пример вызова функции DdeInitialize в среде .NET:

public class ExcelDDEHotConnection

{

     // Ссылка на делегат-переходник для функции обратного вызова DDE

     private DDECallBackDelegate _DDECallBack = null;

     // Обработчик функции обратного вызова

private IntPtr DDECallBack(

           uint uType,

           uint uFmt,

           IntPtr hConv,

           IntPtr hsz1,

           IntPtr hsz2,

           IntPtr hData,

           uint dwData1,

           uint dwData2)

     {

          switch(uType)

           {

           // Мы обрабатываем только транзакции с данными

                case DDEML.XTYP_ADVDATA:



                 // Выполняем обработку транзакции

                                    ...

                 // Возвращаем управление

                 return new IntPtr(DDEML.DDE_FACK);

}

           // Все остальные транзакции мы не обрабатываем

           return IntPtr.Zero;

     }

     // Идентификатор приложения

     private uint idInst = 0;

     public ExcelDDEHotConnection()

     {

           // Создаем делегат-переходник для функции обратного вызова

            _DDECallBack = new DDECallBackDelegate(DDECallBack);

           // Регистрация в библиотеке DDEML

           DDEML.DdeInitialize(ref idInst, _DDECallBack, 0, 0);

           // выполняем остальные инициализирующие действия

           ...

     }

...

}

Прошу обратить внимание на то, что ссылку на делегат функции обратного вызова мы храним все время работы с DDEML. Если этого не сделать, то сборщик мусора .NET уничтожит этот делегат при очередной сборке мусора, что приведет к тому, что во внутренних структурах библиотеки DDEML ссылка на функцию обратного вызова будет указывать на уничтоженный объект.


Это, естественно, вызовет NullPointerException при попытке библиотеки DDEML вызвать функцию обратного вызова. Поэтому вызов функции DdeInitialize следующего вида нежелателен:

// Регистрация в библиотеке DDEML

DDEML.DdeInitialize(ref idInst, new DDECallBackDelegate(DDECallBack), 0, 0);

Забегая вперед, отмечу, что для отображения необходимых функций и констант библиотеки DDEML в компоненте ExcelDDEHotConnection служит класс DDEML.

Ниже приведен список остальных функций для работы с DDE:

Делегат функции обратного вызова:

     internal delegate IntPtr DDECallBackDelegate(

     uint wType, //Код транзакции

     uint wFmt, // Формат данных

     IntPtr hConv, // Идентификатор канала

      IntPtr hsz1, // Идентификатор строки (в нашем случае, строки раздела)

      IntPtr hsz2,  // Идентификатор строки (в нашем случае, элемента данных)

      IntPtr hData, // Идентификатор глобальной области данных, где находятся данные

     uint dwData1, // Дополнительный параметр (В нашей работе не рассматривается)

     uint dwData2 // Дополнительный параметр (В нашей работе не рассматривается)

     );         

Отображение функции DdeInitialize:

[DllImport("user32.dll", EntryPoint="DdeInitialize", CharSet=CharSet.Ansi)]

internal static extern uint DdeInitialize(

     ref uint pidInst, DDECallBackDelegate pfnCallback, uint afCmd,uint ulRes);

Отображение функции DdeUninitialize:

[DllImport("user32.dll", EntryPoint="DdeUninitialize", CharSet=CharSet.Ansi)]

internal static extern bool DdeUninitialize(uint idInst);

Отображение функции DdeCreateStringHandle:

[DllImport("user32.dll", EntryPoint="DdeCreateStringHandle", CharSet=CharSet.Ansi)]



internal static extern IntPtr DdeCreateStringHandle(

uint idInst, string psz,int iCodePage);

Отображение функции DdeFreeStringHandle:

[DllImport("user32.dll", EntryPoint="DdeFreeStringHandle", CharSet=CharSet.Ansi)]

internal static extern bool DdeFreeStringHandle(uint idInst, IntPtr hsz);

Отображение функции DdeConnect:

[DllImport("user32.dll", EntryPoint="DdeConnect", CharSet=CharSet.Ansi)]

internal static extern IntPtr DdeConnect(

uint idInst, IntPtr hszService, IntPtr hszTopic, IntPtr pCC);

Отображение функции DdeDisconnect:

[DllImport("user32.dll", EntryPoint="DdeDisconnect", CharSet=CharSet.Ansi)]

internal static extern bool DdeDisconnect(IntPtr hConv);

Отображение функции DdeClientTransaction:

[DllImport("user32.dll", EntryPoint="DdeClientTransaction", CharSet=CharSet.Ansi)]

internal static extern IntPtr DdeClientTransaction(

IntPtr pData, uint cbData, IntPtr hConv, IntPtr hszItem, uint uFmt,uint uType,

uint dwTimeout, ref uint pdwResult);

Отображение функции DdeGetData:

[DllImport("user32.dll", EntryPoint="DdeGetData", CharSet=CharSet.Ansi)]

internal static extern uint DdeGetData(

IntPtr hData, [Out] byte[] pDst, uint cbMax, uint cbOff);

Отображение функции DdeQueryString:

[DllImport("user32.dll", EntryPoint="DdeQueryString", CharSet=CharSet.Ansi)]

internal static extern uint DdeQueryString(

uint idInst, IntPtr hsz, StringBuilder psz, uint cchMax, int iCodePage);


Содержание раздела