Страница 1 из 5 Как уже отмечалось ранее, виртуальные драйверы служат прежде всего для виртуализации аппаратуры, то есть для предоставления одновременно выполняемым задачам возможности совместного использования устройств компьютера. Измерительная или управляющая аппаратура, подключаемая к компьютеру с целью создания автоматизированной установки, вряд ли будет эксплуатироваться в многозадачном режиме, однако использование для ее управления виртуального драйвера может заметно сократить программные издержки и уменьшить время отклика. Рассмотрим пример виртуального драйвера, обслуживающего прерывания от описанной в предыдущей статье интерфейсной платы таймера-счетчика.
Очевидно, что в состав такого драйвера должен входить обработчик прерываний от платы. Функции этого обработчика определяет программист; в простейшем случае обработчик может просто прочитать данные из выходного регистра счетчика и замаскировать прерывания. Однако приложение при этом не узнает о завершении измерений; более естественно организовать вызов из обработчика прерываний драйвера некоторой функции приложения, в которую можно передать прочитанные данные. Фактически эта функция будет играть роль обработчика прерывания приложения, однако вызываться она будет не самим прерыванием, а обработчиком драйвера. Предусмотрим следующую схему взаимодействия приложения и драйвера. Приложение по команде пользователя вызывает драйвер и передает ему константы настройки таймера. Драйвер инициализирует таймер и возвращает управление в приложение, которое продолжает свое выполнение. По сигналу прерывания от таймера приложение приостанавливается и активизируется обработчик прерываний, находящийся в драйвере. В этом обработчике выполняются чтение данных из выходного регистра счетчика, вызов (через 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; //Получим из драйвера аппаратные данные } |