Выбрать главу

#define EXPORT extern "C" __declspec (dllexport)

EXPORT int ReadMem(char *data, int len);

EXPORT int WriteMem(char *data, int len);

EXPORT int GetMemSize(void);

EXPORT bool IsDriverPresent(void);

Теперь рассмотрим текст исходного срр–файла библиотеки.

//В начале идут включения заголовочных файлов:

#include "stdafx.h" // Основной заголовочный файл MFC

#include "XDSPInter.h" //Наш заголовочный файл

//Определим IOCTL-код для нашего драйвера:

#define XDSPDRV_IOCTL_GETMEMSIZE 0x800

//Введем переменную, которая будет содержать HANDLE драйвера, возвращаемый

//вызовом API CreateFile.

HANDLE hDevice = INVALID_HANDLE_VALUE;

//Также введем строку со значением символической ссылки на наше устройство:

char *sLinkName = \\\\.\\XDSPdrvDevice0;

//И зарезервируем переменную для хранения объема памяти карточки

UINT dwSize;

//Вспомогательная внутренняя функция OpenByName будет пытаться связаться с

//драйвером.

HANDLE OpenByName(void) {

 // вызов API.

 return CreateFile(sLinkName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL,  OPEN_EXISTING, 0, NULL);

 //Функция возвращает NULL, если не удалось подключится к драйверу и хэндл

 //на него в противном случае.

}

//Далее – функция DllMain:

BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {

 //Определяем, почему была вызвана функция:

 switch (ul_reason_for_call) {

 //Приложение подключает библиотеку. Ничего не делаем.

 case DLL_PROCESS_ATTACH: {

  break;

 }

 case DLL_THREAD_ATTACH: {

  break;

 }

 //Приложение отключает библиотеку.

 case DLL_THREAD_DETACH: {

  //Закрыть хэндл драйвера

  if (hDevice != INVALID_HANDLE_VALUE) CloseHandle(hDevice);

  hDevice = INVALID_HANDLE_VALUE;

  break;

 }

 case DLL_PROCESS_DETACH: {

  //Закрыть хэндл драйвера

  if (hDevice != INVALID_HANDLE_VALUE) CloseHandle(hDevice);

  hDevice = INVALID_HANDLE_VALUE;

  break;

 }

 } //Все операции завершились успешно. Вернем true.

 return TRUE;

}

//Эта внешняя функция будет вызываться приложением, которое захочет установить

//связь с драйвером. Функция вернет true в случае успеха и false при неудаче.

EXPORT bool IsDriverPresent(void) {

 //Попытаемся открыть хэндл драйвера

 hDevice=OpenByName();

 if (hDevice == INVALID_HANDLE_VALUE)

  //неудача

  return(false);

 else

  //Успех.

  return(true);

};

//Внешняя функция, производящая чтение памяти устройства. Char* data – буфер для

//данных, int len – число 32-битных слов для чтения. Функция вернет число

//прочитанных слов.

EXPORT int ReadMem(char *data, int len) {

 unsigned long rd=0; //Количество прочитанных слов

 ReadFile(hDevice, data, len, &rd, NULL); //Системный вызов чтения данных из

  //файла. В данном случае – из нашего устройства

  //len – количество запрашиваемых слов

  //rd – количество прочитанных слов.

 data[len*4+1]=0; //Установить последний байт в 0 – признак конца строки.

 return(rd); //Вернуть количество прочитанных слов.

}

//Внешняя функция, производящая запись в память. Практически аналогична

//предыдущей.

EXPORT int WriteMem(char *data, int len) {

 unsigned long nWritten=0;

 WriteFile(hDevice, data, len, &nWritten, NULL);

  //len – количество запрашиваемых слов

  //nWritten – количество прочитанных слов.

 return nWritten;

}

//Эта функция возвращает количество памяти устройства, байт.

EXPORT int GetMemSize(void) {

 CHAR bufInput[4]; // Это пустой входной буфер, который будет

  //передан устройсву

 unsigned long bufOutput; //Буфер, куда драйвер запишет результат.

 ULONG nOutput; //Длина возвращенных драйвером данных, байт

  // Вызвать функцию device IO Control с кодом XDSPDRV_IOCTL_GETMEMSIZE

 if (!DeviceIoControl(hDevice, XDSPDRV_IOCTL_GETMEMSIZE, bufInput, 4, &bufOutput, 4, &nOutput, NULL)) return(0); //Неудача

 else return(bufOutput); //Кол-во памяти

}

Таким образом, наша библиотека экпортирует всего четыре функции для работы с устройством. Все они имеют простой синтаксис и просты в использовании. Использование dll в нашем случае позволяет программисту не думать о сложных системных вызовах, необходимых для общения с драйвером, о формате передаваемых ему данных, а сосредоточится на решении прикладных задач.

2.5 Подключение dll-библиотеки к приложению.

После того, как написан драйвер и dll-библиотека для работы с ним, пришло время написать приложение пользоваеля, работающее с устройством. Оно будет взаимодействовать с драйвером через dll-библиотеку. Естественно, написано оно также будет в среде Visual C++. В принципе, его можно было бы реализовать в среде Visual Basic, Delphi или CВuilder, но это приведет к некоторым трудностям, прежде всего в использовании системных вызовов и структур данных. В данном разделе, в отличие от предыдущих, не рассматривается какое-либо конкретное приложение, а даются общие рекомендации по написанию такой программы.

Подключение библиотеки к приложению не требует особых усилий. Библиотека подключается при помощи системного вызова HMODULE LoadLibrary(char* LibraryName), где LibraryName — строка с именем файла dll-библиотеки. Возвращаемое значение — хендл (дескриптор) бибилиотеки. Если функция возвратила NULL, то произошла ошибка при подключении библиотеки.

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

FARPROC GetProcAdress(HMODULE hModule, char * ProcName)

• hModule — хэндл библиотеки, возвращенный LoadLibrary;

• ProcName — строка с именем импортируемой функции. Вызов GetProcAdress возвращает адрес функции с заданным именем и NULL, если такой функции нет в библиотеке.