2.4 Разработка dll-библиотеки для взаимодействия с драйвером
2.4 Разработка dll-библиотеки для взаимодействия с драйвером
dll-библиотека (Dynamic Link Library) — программный модуль, который может быть динамически подключен к выполняющемуся процессу. Dll–библиотека может содержать функции и данные. При подключении dll к процессу она отображается на адресное пространство этого процесса.
Если говорить по русски, то это означает: в любой момент времени программа может загрузить dll-библиотеку, получить указатели на функции и данные этой библиотеки. Потом приложение как-то использует функции и данные библиотеки, и когда они больше не нужны — выгружает библиотеку.
Dll-библиотека содержит два вида функций: внешние (External) и внутренние (Internal). Внутренние функции могут вызываться только самой dll, а внешние может также вызывать приложение, подключившее библиотеку. В этом случае говорят, что dll-библиотека экспортирует функции и данные.
Как было упомянуть выше, в настоящее время для связи с драйвером используется схема Приложение?Библиотека dll?Драйвер. При использовании такой архитектуры запрос приложения на операцию ввода-вывода поступает в dll-библиотеку, проходит там предварительную обработку и передается драйверу. Результат, возвращенный драйвером библиотеке dll, также обрабатывается и передается приложению. Преимущества такого подхода очевидны:
• Выпускается огромное количество различных периферийных устройств, и, соответственно, для каждого устройства разрабатывается свой драйвер. Программисту будет тяжело разбираться во всех тонкостях работы драйвера устройства: формат данных для чтения/записи, запоминать непонятные IOCTL-коды. Гораздо лучше — предоставить для него понятный интерфейс API-функций для работы с устройством. Еще лучше, если такой интерфейс будет унифицированным для всех устройств данного типа. Задача dll-библиотеки, поставляемой с драйвером – связать стандартные интерфейсы, предоставляемые прикладной программе, со специфическими алгоритмами работы драйвера.
• если в будущем измениться алгоритм взаимодействия приложения с драйвером, то пользователю для работы с новым драйвером будет необходимо обновить только библиотеку dll. Все ранее разработанные программы остануться прежними.
Естественно, такой подход имеет свои минусы. В данном случае за счет большего числа вызовов, через которые проходит запрос на ввод-вывод, снижается быстродействие работы системы.
В нашем случае нам необходимо разработать dll-библиотеку, которая будет предоставлять приложению три функции: чтение памяти, запись в память и получение общего количества памяти устройства. Естественно, dll – библиотеку мы также будем проектировать в среде Visual C++.
Запустите среду VC++ и создайте новый проект с названием XDSPInter. В качестве типа проекта выберите Win32 Dynamic-Link Library. Далее в качестве типа проекта выберите A Simple DLL (простая dll-библиотека). Среда VC++ создаст для Вас пустой проект с одной– единственной функцией DllMain().
Функция DllMain() вызывается при подключении и отключении dll процессом. DllMain() имеет возвращаемое значение BOOL APIENTRY (фактически, она возвращает значение типа BOOL) и три параметра —HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved.
Параметры:
• HANDLE hModule — дескриптор (хэндл) нашей dll;
• DWORD ul_reason_for_call — флаг, показывающий, почему была вызвана функция. Может принимать значения:
• DLL_PROCESS_ATTACH или DLL_THREAD_ATTACH — библиотека подключается к процессу;
• DLL_PROCESS_DETACH или DLL_THREAD_DETACH — библиотека отключается от процесса.
• LPVOID lpReserved – зарезервировано.
Функция DllMain() — единственная функция, которая обязательно должна присутствовать в библиотеке. Остальные функции и переменные добавляет программист в соответствии с решаемой задачей.
В нашем случае dll–библиотека будет экспортировать следующие функции: bool IsDriverPresent(void). Функция будет определять, присутствует ли в системе необходимый драйвер и попытаться подключиться к нему. Если это удастся — функция вернет true, в противном случае — false.
int ReadMem(char data, int len) — чтение данных из памяти устройства. Char* data — буфер для данных, int len — число 32-битных слов для чтения. Функция вернет число прочитанных слов.
int WriteMem(char *data, int len) — аналогична предыдущей; запись данных в память.
int GetMemSize(void) — получить объем доступной памяти устройства. Для того, чтобы функция стала экспортируемой, она должна быть скомпилирована со специальным объявлением типа:
extern "C" __declspec (dllexport)
Для того, чтобы при каждом объявлении функции не писать эту длинную малопонятную строку, определим ее, как директиву препроцессора:
#define EXPORT extern "C" __declspec (dllexport)
Теперь перед каждым объявлением функции просто следует писать слово EXPORT. Создадим заголовочный файл нашей dll-библиотеки, в котором перечислим все экспортируемые функции и директивы препроцессора:
#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 в нашем случае позволяет программисту не думать о сложных системных вызовах, необходимых для общения с драйвером, о формате передаваемых ему данных, а сосредоточится на решении прикладных задач.
Более 800 000 книг и аудиокниг! 📚
Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОКЧитайте также
Построение взаимодействия
Построение взаимодействия Мы изучили довольно простой пример, добавляя различные свойства CSS3 к элементам нашей страницы, которые относятся исключительно к взаимодействию. Браузеры, поддерживающие эти свойства, будут показывать анимацию полупрозрачного фона
10.7. Сигналы для межпроцессного взаимодействия
10.7. Сигналы для межпроцессного взаимодействия «ЭТО УЖАСНАЯ МЫСЛЬ! СИГНАЛЫ НЕ ПРЕДНАЗНАЧЕНЫ ДЛЯ ЭТОГО! Просто скажите НЕТ». - Джефф Колье (Geoff Collyer) - Одним из главных механизмов межпроцессного взаимодействия (IPC) являются каналы, которые описаны в разделе 9.3 «Базовая
Модель сетевого взаимодействия OSI
Модель сетевого взаимодействия OSI В основу работы стека протоколов положена модель OSI (Open System Interconnection — взаимодействие открытых систем). Данная модель предусматривает семь уровней сетевого взаимодействия, на каждом из которых решаются конкретные задачи. Источником
Основные действия по организации X-взаимодействия
Основные действия по организации X-взаимодействия В данной главе были рассмотрены самые разнообразные средства установления X-соединения. В ваше распоряжение предоставляется настолько много возможностей, что разобраться с ними бывает достаточно трудно. Ниже описаны
6.1.2. Уровни взаимодействия OSI
6.1.2. Уровни взаимодействия OSI Физический уровень (Physical Layer)Физический уровень передает биты по физическим каналам связи, например, коаксиальному кабелю или витой паре. На этом уровне определяются характеристики электрических сигналов, которые передают дискретную
26.1. Способы взаимодействия
26.1. Способы взаимодействия Процессы, как и люди, могут «общаться» между собой, то есть обмениваться информацией. В главе 3 мы бегло рассмотрели два средства межпроцессного взаимодействия (IPC, Inter-Process Communication); полудуплексные каналы (конвейеры) и сигналы, но в UNIX-системах
5.5. Диаграммы взаимодействия
5.5. Диаграммы взаимодействия Существенное: объекты и их взаимодействия Диаграмма взаимодействии используется, чтобы проследить выполнение сценария в том же контексте, что и диаграмма объектов [Эти диаграммы обобщают диаграммы трассировки событий Румбаха и диаграммы
Понятие удаленного взаимодействия .NET
Понятие удаленного взаимодействия .NET Вы должны помнить из главы 13, что домен приложения [AppDomain] задает логические границы выполнения компоновочного блока .NET в рамках процесса Win32. Понимание этого очень важно для дальнейшего обсуждения распределенных приложений .NET,
Пространства имен удаленного взаимодействия .NET
Пространства имен удаленного взаимодействия .NET Перед тем как углубиться в детали процесса удаленного взаимодействия .NET. мы должны выяснить, какие функциональные возможности предлагают пространства имен, обеспечивающие удаленное взаимодействие. Библиотеки базовых
Каркас удаленного взаимодействия .NET
Каркас удаленного взаимодействия .NET Когда клиенты и серверы обмениваются информацией через границы приложений, среда CLR вынуждена использовать низкоуровневые примитивы, обеспечивающие настолько "прозрачное" взаимодействие сторон, насколько это возможно. Это значит,
Термины удаленного взаимодействия .NET
Термины удаленного взаимодействия .NET Подобно любой новой парадигме, удаленное взаимодействие .NET предлагает свой собственный набор трехбуквенных акронимов. Поэтому, перед тем как рассмотреть первый пример программного кода, нам с вами придется определить несколько
Файлы конфигурации удаленного взаимодействия
Файлы конфигурации удаленного взаимодействия Итак, вы успешно построили распределённое приложение, используя слой удаленного взаимодействия .NET. В связи c данными примерами следует обратить внимание на то что полученные приложения клиента и сервера содержат большой