Драйверы устройств в системе Windows
Драйвер - системная программа, предназначенная для управления каким-либо физическим или виртуальным устройством компьютера. Система ввода и вывода в Windows. Объекты для управления оборудованием. Разработка dll-библиотеки для взаимодействия с драйвером.
Рубрика | Программирование, компьютеры и кибернетика |
Вид | дипломная работа |
Язык | русский |
Дата добавления | 07.12.2010 |
Размер файла | 3,8 M |
Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже
Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.
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; //Количество прочитанных слов
//Системный вызов чтения данных из
//файла. В данном случае - из нашего устройства
//len - количество запрашиваемых слов
//rd - количество прочитанных слов.
ReadFile(hDevice, data, len, &rd, NULL);
//Установить последний байт в 0 - признак конца строки.
data[len*4+1]=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 в нашем случае позволяет программисту не думать о сложных системных вызовах, необходимых для общения с драйвером, о формате передаваемых ему данных, а сосредоточится на решении прикладных задач.
6.1 Подключение 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, если такой функции нет в библиотеке.
Так как из библиотеки импортируется не само тело функции, а ее адрес, то вызов такой функции превращается в непростое дело. Прежде всего, в программе объявляется не сама функция, а переменная, содержащая указатель на нее. Естественно, работа с таким указателем сильно отличается от работы с указателем на число или строку. Ведь функция в отличие от просто переменной возвращает значение и принимает некоторые параметры, поэтому указатель на нее должен быть объявлен специальным образом.
Указатель на функцию, импортируемую из dll-библиотеки должен также быть скомпилирован со специальным объявлением типа - __declspec(dllimport). Эту строку также удобно представить в виде директивы #define.
#define XDSPINTER_API __declspec(dllimport).
Мы импортируем из библиотеки четыре функции, поэтому необходимо определить их типы: параметры, передаваемые в функцию, возвращаемое значение. Это можно сделать при помощи директивы typedef:
//Объявить тип - указательная функцию, возвращающую значение типа int
// и принимающую два параметра - массив типа char и число int.
// В библиотеке ей будет соответствовать функция
// EXPORT int ReadMem(char *data, int len)
typedef XDSPINTER_API int ( *MemReadFun)(char *data, int len);
// EXPORT int WriteMem(char *data, int len)
typedef XDSPINTER_API int ( *MemWrtFun)(char *data, int len);
// EXPORT int GetMemSize(void)
typedef XDSPINTER_API int ( *MemSizeFun)();
//EXPORT bool IsDriverPresent(void)
typedef XDSPINTER_API bool ( *IsDrivFun)();
Теперь пришло время создать сами указатели на функции:
MemReadFun ReadMem;
MemWrtFun WriteMem;
MemSizeFun GetMemSize;
IsDrivFun IsDriverPresent;
Теперь рассмотрим функцию, подключающую dll-библиотеку к приложению. Она будет подключать dll-библиотеку к приложению и пытаться установить связь с драйвером. Функция вернет true в случае успеха и false при неудаче. Т.к. VC++ - объектно-ориентированная среда, то эта функция будет методом одного из классов приложения (в нашем случае - класса представления).
bool CXDSPView::ConnectToDriver()
{
//Переменная, в которой будет храниться возвращаемое значение.
success=true;
//HMODULE InterDll -переменная экземпляра, где хранится хэндл библиотеки.
InterDll=::LoadLibrary("XDSPInter");
if (InterDll==NULL)
{
//Не удалось подключиться к библиотеке
AfxMessageBox("Couldn't load a library XDSPInter.dll",MB_ICONERROR | MB_OK);
//Вернем неудачу.
success=false;
}
else
{
//Библиотека подключена успешно. Импортируем функции.
ReadMem=(MemReadFun)::GetProcAddress(InterDll,"ReadMem");
if (ReadMem==NULL)
{
//Не удалось импортировать функцию
AfxMessageBox("Couldn't get adress for ReadMem function
from library XDSPInter.dll",MB_ICONERROR | MB_OK);
success=false;
}
WriteMem=(MemReadFun)::GetProcAddress(InterDll,"WriteMem");
if (WriteMem==NULL)
{
//Не удалось импортировать функцию
AfxMessageBox("Couldn't get an adress for WriteMem function
from library XDSPInter.dll",MB_ICONERROR | MB_OK);
success=false;
}
GetMemSize=(MemSizeFun)::GetProcAddress(InterDll,"GetMemSize");
if (GetMemSize==NULL)
{
//Не удалось импортировать функцию
AfxMessageBox("Couldn't get an adress for GetMemSize function from
library XDSPInter.dll",MB_ICONERROR | MB_OK);
success=false;
}
IsDriverPresent=(IsDrivFun)::GetProcAddress(InterDll,"IsDriverPresent");
if (IsDriverPresent ==NULL)
{
//Не удалось импортировать функцию
AfxMessageBox("Couldn't get an adress for IsDriverPresent function
from library XDSPInter.dll",MB_ICONERROR | MB_OK);
success=false;
}
}
return(success);
}
Вызов метода ConnectToDriver() целесообразно сделать в конструкторе класса. Там же надо реализовать и проверку, присутствует ли в системе драйвер. Тогда вся необходимая инициализация будет проведена еще при запуске приложения.
CXDSPView::CXDSPView()
: CFormView(CXDSPView::IDD)
{
//{{AFX_DATA_INIT(CXDSPView)
//}}AFX_DATA_INIT
//Здесь мы добавляем свой код. Success - переменная экземпляра. Если она
//равна true - то ошибок нет, иначе произошла какая-то ошибка.
success=true;
//Пробуем подключить dll:
if (ConnectToDriver())
{
//Удалось подключить библиотеку. Теперь пытаемся установить связь с
//драйвером - вызываем функцию в dll:
if (!IsDrvPresent())
{
//Неудача
success=false;
AfxMessageBox("Necessary driver isn't present in the system",
MB_ICONERROR | MB_OK);
}
}
else
//Не удалось подключиться к dll.
success=false;
}
Метод, производящий чтение памяти устройства может выглядеть следующим образом:
void CXDSPView::OnRead()
{
int res; //Количество слов, прочитанных из памяти
res=(*ReadMem)(dt,256); //Пытаемся читать 256 слов.
m_buff.SetWindowText(dt); //Выводим данные на экран
//Код, характерный для VC++.
CXDSPDoc *m_doc; //Подключаем документ, связанный с представлением
m_doc=GetDocument();
//копируем туда данные.
strcpy((char*)m_doc->m_buffer,dt);
//Примечание: оба буфера должны иметь достаточный объем - минимум
//256*4+1 байт.
}
Аналогично может выглядеть метод записи в память устройство:
void CXDSPView::OnWrite()
{
//Получили данные, введенный пользователем
m_buff.GetWindowText(dt,32767);
int res;
//Записываем его в память устройства. Заметим, что в качестве длины данных
//мы передаем не длину в байтах, а в 4-байтых словах.
res=(*WriteMem)(dt,strlen(dt)%4+1);
}
Метод, возвращающий длину памяти устройтсва, совсем прост и, думаю, в комментариях не нуждается.
int CXDSPView::GetTotalLen()
{
int res=(*GetMemSize)();
return(res);
}
Также введем еще один метод, который может быть полезным. Он будет очищать память устройства.
void CXDSPView::OnClear()
{
//Получили документ
CXDSPDoc *m_doc;
m_doc=GetDocument();
//Забиваем буфер нулями
for (int i=0;i<1025;i++)
dt[i]=0;
//Обнуляем буфер в классе документа
m_doc->m_buffer[0]=0;
int res;
//Записывем в память устройства нули
res=(*WriteMem)(dt,256);
//Обновляем данные в окне приложения.
m_buff.SetWindowText(dt);
}
Конечно, написанные нами приложение и dll-библиотека весьма несовершенны. Например, сбои будут происходить, если будут запущены несколько приложений. Тогда они будут одновременно обращаться к одной и той же dll и одновременно работать с устройством. Это может породить множество сбоев. В лучшем случае данные, получаемые каждым из них, будут неадекватными. В худшем - система зависнет. Впрочем, этот недостаток можно устранить, модифицировав драйвер способом, описанным выше. Также в нашем приложении производится работа только с первыми 1024 байтами памяти устройства.
Конечно, коммерческая ценность такой системы равна нулю. Но она может быть хорошим учебным примером для ознакомления с программированием WDM - драйверов в Windows и DriverStudio.
7. Отладка драйверов
Разговор о драйверах был бы неполным, если не упомянуть об отладке драйверов. Т.к. драйвера работают в нулевом кольце защиты процессора со всеми вытекающими последствиями, то обыкновенные отладчики пользовательских приложений не пригодны для отладки драйверов.
Если, например, разрабатывать драйвер под ОС Linux, то ситуация там может быть немного хуже: в этой ОС вообще нет какой-либо возможности отлаживать драйвера, кроме как воспользоваться отладчиком gdb. Но в таком случае надо перекомпилировать ядро системы специальным образом и станцевать еще несколько подобных танцев с бубном. Поэтому зачастую отладка сводится к вызову функций printk, которые в великом множестве раскиданы по всему ядру системы.
К счастью, хоть в этом Windows имеет преимущества. Для того, чтобы можно было отлаживать драйвера, отладчик должен сам работать в нулевом кольце защиты. Естественно, разработка такой программы является чрезвычайно сложной задачей, поэтому таких отладчиков на сегодняшний день известно всего два: WinDbg (поставляется с пакетом DDK) и SoftIce (входит в состав NuMega DriverStudio). SoftIce считается одним из лучших отладчиков для Windows всех типов. Это надежный, мощный и довольно удобный в использовании инструмент. SoftIce может применяться для различных целей: для отладки драйверов и приложений пользователя, для просмотра информации о системе и т.п. Мы рассмотрим, как применять SoftIce для отладки драйверов устройств.
Будучи установленным в Win98, SoftIce прописывает в Autoexec.bat строку вида: c:\Progra~1\numega\driver~1\softice\winice
Т.е. SoftIce загружается после загрузки DOS и сам грузит Windows. При работе Windows SoftIce активизируется лишь при каком-нибудь системном исключении или в точке останова, заданной программистом в драйвере. Также вызвать SoftIce можно, нажав Ctrl+D. На экране появляется окно отладчика.
Пока окно SoftIce активно, вся деятельность ОС замирает; именно сейчас можно безболезненно отлаживать драйвера.
Окно SoftIce разбито на несколько окон. Обычно в центре видно окно кода, над ним - окно регистров процессора и в самом низу - окно сообщений. Перемещаться в пределах окна можно, используя клавиши управления курсором или мышь.
В самом низу окна SoftIce расположена командная строка. SoftIce не имеет графического интерфейса, и все команды управления отладчиком вводятся в командной строке. SoftIce имеет довольно неплохую систему помощи. Перечень команд выдается по команде help. Наверное, самая важная команда - это команда выхода из SoftIce. Для этого нужно нажать клавишу F5 или дать команду Х (регистр не имеет значения).
Внимательно изучив окно сообщений, мы там увидим разнообразные системные сообщения и те сообщения, которые наш драйвер выводит через объект трассировки. Таким образом, можно просматривать все важные сведения, которые драйвер хочет сообщить нам. Если мы хочем, чтобы драйвер не выводил какие-то сообщения или выводил другие сообщения, нам надо отредактировать текст драйвера, добавив новые или удалив существующие трассировочные сообщения. После этого надо перекомпилировать драйвер и перезагрузить его.
Но, естественно, программисту мало простого чтения сообщений, посланных драйвером. Для эффективной отладки любой программы надо установить точку останова (breakpoint), просмотреть значения регистров. К счастью, SoftIce предоставляет такую возможность.
Универсальной точкой останова является использование прерывания INT 3. Как и в ОС MS-DOS, в Windows INT 3 также является прерыванием отладки. Для этого в тексте драйвера, где необходимо установить breakpoint, необходимо вставить следующий код:
_asm
{
int 3
}
При этом происходит вызов прерывания INT 3.
Но по умолчанию SoftIce не реагирует на INT 3. Для того чтобы по этому прерыванию активизировался отладчик, необходимо вызвать SoftIce и дать команду: SET I3HERE ON
Теперь при вызове INT 3 произойдет <всплывание> этого кода в отладчике. Для отключения режима отладки по INT 3 следует дать команду SET I3HERE OFF.
После того, как наш драйвер <всплыл> в SoftIce, мы можем контролировать выполнение программы при помощи команд:
HERE (F7)
шаг на следующую строку в окне кода;
T F8
выполнить одну инструкцию процессора (трассировка);
HBOOT
перезагрузка системы;
G
перейти на указанный адрес;
GENINT
сгенерировать прерывание;
X F5
продолжить выполнение программы (выход из SoftIce).
Если драйвер был скомпилирован в отладочной конфигурации, то на экране будет виден текст драйвера, написанный на С++.
SoftIce также может просматривать значения переменных пользователя. Для того, чтобы открыть/закрыть окно просмотра переменных (Watch), надо дать команду WW или нажать Alt+F4. Добавить/убрать переменную для просмотра можно при помощи команды WATCH.
Это основные команды, применяемые для отладки драйверов устройств в SoftIce. А в общем, этот отладчик имеет огромное количество функциональных возможностей и его полное описание поставляется с программой и занимает порядка двухсот страниц.
8. Список литературы
1. cs.stu.cn.ua
2. http://www.codenet.ru/progr/visualc
3.http://www.e-reading.org.ua/bookbyauthor.php?author=42058
Размещено на Allbest.ru
Подобные документы
Использование стандартных библиотек Windows. Установка и настройка дополнительных устройств ввода/вывода. Использование камеры, динамиков, сканера, дисков и портов ввода/вывода. Драйверы внешних устройств. Безопасность данных в операционных системах.
контрольная работа [1,8 M], добавлен 13.10.2022Разработка и практическая апробация действия драйвер-фильтра клавиатуры для операционной системы Windows, переопределяющего значение любых клавиш и отключающего клавиши клавиатуры. Составление программы для установки и управления данным драйвером.
курсовая работа [226,8 K], добавлен 18.06.2009Разработка драйверов ядра Windows. Драйвер виртуальных устройств Windows - компьютерная программа, с помощью которой другая программа получает доступ к аппаратному обеспечению стандартным образом. Доступ к драйверам из приложений пользовательского режима.
курсовая работа [436,1 K], добавлен 25.10.2012Архитектура ввода/вывода Windows NT. Внутренняя организация шины USB. Сущностная характеристика драйверной модели WDM. Точки входа разрабатываемого драйвера, размещение кода в памяти, установка драйвера в системе. Реализация кода драйвера на языке C.
курсовая работа [1,2 M], добавлен 27.09.2014Изучение подсистемы ввода-вывода и файловой системы ОС семейства Windows NT. Анализ особенностей работы приложения TotalCommander и его взаимодействия с файловой системой и подсистемой ввода-вывода. Взаимодействие TotalCommander с сетевыми адаптерами.
лабораторная работа [1,1 M], добавлен 12.06.2012Основные части персонального компьютера: системный блок, устройства ввода и вывода информации. Основные элементы системного блока: материнская плата, процессор, оперативная память, кэш-память, накопители. Операционная система, объекты Windows, окна.
реферат [135,0 K], добавлен 21.09.2009Архитектура Windows NT 5. Приоритеты выполнения программного кода. Описание формата MIDI-данных. Установка драйвера в системе. Выбор средств разработки программного обеспечения. Обработка запросов драйверной модели WDM. Использование библиотеки DirectKS.
курсовая работа [498,8 K], добавлен 24.06.2009Разработка и практическая апробация действия драйвер-фильтра USB-накопителя операционной системы Windows, предоставляющего возможности: установка на любой USB накопитель, перехват информации ввода/вывода, запись перехваченной информации в файл на диске.
курсовая работа [349,3 K], добавлен 18.06.2009Операционная система Windows - совокупность программных средств, обеспечивающих работу аппаратной части компьютера и прикладных программ, их взаимодействие между собой и пользователем. Основные объекты и приемы управления. Текстовый процессор Word.
методичка [1,4 M], добавлен 23.04.2012Операционная система как посредник в работе с устройствами компьютера: ДрайверЫ и 32-разрядность Windows 95 и программ. VFAT, DLL, DLE. Технические и программные средства для доступа и работы в Internet. TCP/IP, FTP, WWW. База данных в Excel.
реферат [55,0 K], добавлен 28.06.2008