Программирование arrow Visual C++ arrow Основы разработки прикладных виртуальных драйверов

Основы разработки прикладных виртуальных драйверов

Оглавление

Как уже отмечалось ранее, виртуальные драйверы служат прежде всего для виртуализации аппаратуры, то есть для предоставления одновременно выполняемым задачам возможности совместного использования устройств компьютера. Измерительная или управляющая аппаратура, подключаемая к компьютеру с целью создания автоматизированной установки, вряд ли будет эксплуатироваться в многозадачном режиме, однако использование для ее управления виртуального драйвера может заметно сократить программные издержки и уменьшить время отклика. Рассмотрим пример виртуального драйвера, обслуживающего прерывания от описанной в предыдущей статье интерфейсной платы таймера-счетчика. Очевидно, что в состав такого драйвера должен входить обработчик прерываний от платы. Функции этого обработчика определяет программист; в простейшем случае обработчик может просто прочитать данные из выходного регистра счетчика и замаскировать прерывания. Однако приложение при этом не узнает о завершении измерений; более естественно организовать вызов из обработчика прерываний драйвера некоторой функции приложения, в которую можно передать прочитанные данные. Фактически эта функция будет играть роль обработчика прерывания приложения, однако вызываться она будет не самим прерыванием, а обработчиком драйвера.

Предусмотрим следующую схему взаимодействия приложения и драйвера. Приложение по команде пользователя вызывает драйвер и передает ему константы настройки таймера. Драйвер инициализирует таймер и возвращает управление в приложение, которое продолжает свое выполнение. По сигналу прерывания от таймера приложение приостанавливается и активизируется обработчик прерываний, находящийся в драйвере. В этом обработчике выполняются чтение данных из выходного регистра счетчика, вызов (через VMM) обработчика прерывания приложения и передача в него считанных данных. Обработчик прерываний приложения принимает из драйвера данные и, завершаясь, возвращает управление в VMM, который наконец передает управление в приложение в точку его приостановки.

Поскольку обработчик прерываний приложения вызывается из VMM и после своего завершения опять передает управление в систему, его возможности ограниченны. В нем, в частности, недопустим вызов функции вывода информации - MessageBox() - в окно сообщения. Поэтому здесь, как и в предыдущем примере, возникает проблема оповещения главной функции WinMain() о приходе прерывания. Мы решим эту проблему следующим образом: обработчик прерывания приложения, получив управление, устанавливает флаг, который опрашивается главной функцией в каждом шаге цикла обработки сообщений. Главная функция, обнаружив установленное состояние флага, посылает в приложение сообщение пользователя, которое наравне с сообщениями Windows обслуживается циклом обработки сообщения. В функции обработки этого сообщения уже можно выполнять любые действия безо всяких ограничений.

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

Приложение Windows, взаимодействующее с виртуальным драйвером обработки аппаратных прерываний

#define STRICT  
#include <windows.h>
#include <windowsx.h>
#include <string.h>
//Определения констант
#define MI_START 100 //Константы, используемые
#define MI_EXIT 101 //для идентификации
#define MI_READ 102 //пунктов меню
/* void Cls_OnUser(HWND hwnd) */
#define HANDLE_WM_USER(hwnd, wParam, lParam, fn) \//Макрос для обработки
((fn)(hwnd), 0L) // сообщения WM_USER
//Прототипы функций
void Register(HINSTANCE); //Вспомогательные
void Create(HINSTANCE); //функции
LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);//Оконная функция
void OnCommand(HWND,int,HWND,UINT);//Функции
void OnDestroy(HWND); //обработки сообщений Windows
void OnUser(HWND); //и пользователя
void InitCard(); //Функция инициализации платы (через драйвер)
void GetAPIEntry(); //Функция получения точки входа в драйвер
void isr(int,int); //Прототип обработчика прерывания приложения
//Глобальные переменные
char szClassName[]="MainWindow";//Имя класса главного окна
char szTitle[]="Управление";//Заголовок окна
HWND hMainWnd; //Дескриптор главного окна
FARPROC VxDEntry; //Адрес точки входа в драйвер
int unsigned data; //Данное из драйвера
char request; //Флаг окончания измерений
//Главная функция WinMain
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE,LPSTR,int){
MSG msg; //Структура для приема сообщений
Register(hInstance); //Регистрация класса главного окна
Create(hInstance); //Создание и показ главного окна
/*Более сложный цикл обработки сообщений, в который включены
анализ флага request завершения измерений и посылка приложению
сообщения WM_USER при установке этого флага*/
do{
if(PeekMessage(&msg,NULL,0,0,PM_REMOVE)){
if (msg.message == WM_QUIT)return msg.wParam;
DispatchMessage(&msg);
}//Конец if(PeekMessage())
if(request){
request=0; //Сброс флага
PostMessage(hMainWnd,WM_USER,0,0);/*Поставить в очередь
сообщение WM_USER без параметров*/
}//Конец if(request)
}while(1);//Конец do
}//Конец WinMain
//Функция Register регистрации класса окна
void Register(HINSTANCE hInst){
WNDCLASS wc;
memset(&wc,0,sizeof(wc));
wc.lpszClassName=szClassName;
wc.hInstance=hInst;
wc.lpfnWndProc=WndProc;
wc.lpszMenuName="Main";
wc.hCursor=LoadCursor(NULL,IDC_ARROW);
wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);
wc.hbrBackground=GetStockBrush(WHITE_BRUSH);
RegisterClass(&wc);
}
//Функция Create создания и показа окна
void Create(HINSTANCE hInst){
hMainWnd=CreateWindow(szClassName,szTitle,WS_OVERLAPPEDWINDOW,
10,10,200,100,HWND_DESKTOP,NULL,hInst,NULL);
ShowWindow(hMainWnd,SW_SHOWNORMAL);
}
//Оконная функция WndProc главного окна
LRESULT CALLBACK WndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam){
switch(msg){
HANDLE_MSG(hwnd,WM_COMMAND,OnCommand);
HANDLE_MSG(hwnd,WM_DESTROY,OnDestroy);
HANDLE_MSG(hwnd,WM_USER,OnUser);
default:
return(DefWindowProc(hwnd,msg,wParam,lParam));
}
}
//Функция OnCommand обработки сообщений WM_COMMAND от пунктов меню
void OnCommand(HWND hwnd,int id,HWND,UINT){
switch(id){
case MI_START://Инициализация платы
InitCard();
break;
case MI_READ:
char txt [80];
wsprintf(txt,"Накоплено %d событий",data);
MessageBox(hMainWnd,txt,"Данные",MB_ICONINFORMATION);
break;
case MI_EXIT://Завершить приложение
DestroyWindow(hwnd);
}
}
//Функция OnUser обработки сообщения WM_USER
void OnUser(HWND){
MessageBox(hMainWnd,"Измерения окончены","Контроль",MB_ICONINFORMATION);
}
//Функция OnDestroy обработки сообщения WM_DESTROY
void OnDestroy(HWND){
PostQuitMessage(0);
}
//Функция InitCard инициализации (через драйвер) платы
void InitCard (){
GetAPIEntry(); //Получение адреса точки входа в драйвер
_AX=_DS; //Передача в драйвер DS
_BX=55; //Канал 0
_CX=20000; //Канал 1; BX*CX=Длительность интервала
_DX=1000; //Канал 2, внутренний генератор
_SI=OFFSETOF(isr); //Смещение обработчика прерываний
_DI=SELECTOROF(isr); //Селектор обработчика прерываний
VxDEntry(); //Вызов драйвера
}
//Функция GetAPIEntry получения адреса точки входа в драйвер
void GetAPIEntry(){
asm{
mov AX,0x1684
mov BX,0x8000
int 0x2F
mov word ptr VxDEntry,DI
mov word ptr VxDEntry+2,ES
}
}
//Обработчик прерываний приложения. Вызывается VMM и возвращает управление в VMM
void isr(int segment,int dt){
_DS=segment; //Инициализируем DS селектором, полученным из драйвера
request++; //Поставим запрос на сообщение
data=dt; //Получим из драйвера аппаратные данные
}

 
« Предыдущая статья   Следующая статья »


  • Visual C++, Работа с СУБД Oracle через интерфейс OCCI
    OCCI - расшифровывается как Oracle C++ Call Interface и представляет собой специализированное апи для работы с СУБД Oracle используя C++ что в общем то явствует из названия. Для использования необходимо подключить заголовочный файл "occi.h"....
  • Visual C++, Задача Майхилла для Microsoft Visual C++
    О синхронизации процессов в среде Windows. Задача Майхилла - еще один (наряду с задачей RS-триггера) пример решения нетривиальных проблем создания сложных систем. Справившись с ней, мы научимся организовывать взаимодействие параллельно работающих компонентов сложных программных комплексов в жестких условиях. ...
  • Visual C++, Использование ODBC в Visual C++
    Класс CDatabase представляет собой класс, который обеспечивает связь с источником данных. Под источником данных может пониматься как непосредсвенно файл, в котором находится таблица, например dBase, так и файл с многими таблицами, например Microsoft Access или сервер баз данных Oracle, MS SQL Server и т.д. Для связи с источником данных используется интерфейс ODBC. У данного класса есть папа в виде класса
  • Visual C++, Создание простого приложения с плагинами
    В этой статье описываются принципы и решения, применяемые при проектировании приложений, которые будут использовать внешние, динамически подключаемые, модули. Эта статья более ориентирована на тех, кто хочет использовать механизмы подключения/отключения функциональности приложения, наподобии механизма Aobe Photoshop или Far, а не просто многократного использования кода в разных приложениях....
  • Visual C++, Работа с 1C Предприятие из Visual C++
    В данной статье показано, как можно работать с 1С Предприятием из С++ с помощью OLE DB. Так же она будет интересна тем, кто не пользуется C++, но хочет узнать подробности "а как оно устроено внутри 1С". В данной статье речь пойдет об 1С Предприятии версии 7.7. Полагаю, что в версии 8 мало что изменилось. Предполагается, что читатель хотя бы чуть-чуть знаком с 1С Предприятием. Так же предполагается, что вы изучали официальное руководство 1С по вопросам OLE DB (часть вторая описани...
  • Visual C++, Как самому сделать plug-in к FAR на Visual C++
    Трудно найти человека, которые не знает или не использует Far - IMHO лучший клон NC для Windows. Кроме того, что это просто очень хороший файл менеджер, к нему есть огромное количество plug-in модулей. Plug-in модуль это DLL-файл, который вместо стандартных Windows функций по работе с монитором, клавиатурой и т.д. обращается к функциям Far-а. Far поддерживает весь набор функций для работы в текстовом режиме. Установка plug-in модуля происходит предельно просто - DLL файл и файлы данных коп...
  • Visual C++, Использование директивы #import в Visual C++
    В данной статье я попытаюсь объяснить то, как работает эта директива и привести несколько примеров её использования. Надеюсь, после этого вы тоже найдёте её полезной.  Директива #import введена в Visual C++, начиная с версии 5.0. Её основное назначение облегчить подключение и использование интерфейсов COM, описание которых реализовано в библиотеках типов....
  • Visual C++, Создание VxD на Visual C++ без ассемблерных модулей
    Виртуальные драйверы устройств (VxD) в Windows во многих случаях являются единственным «честным» способом обхода ограничений, установленных системой для приложений Win32: невозможности прямого доступа к портам ввода-вывода и служебной памяти, эффективной обработки аппаратных прерываний, использования сервисных функций существующих VxD и т.п. Кроме того, без VxD не обходится практически ни один полноценный драйвер физического или виртуального устройства....