Виртуальный драйвер для обслуживания аппаратных прерываний - Разработка приложения
ОГЛАВЛЕНИЕ
Разработка приложения
При разработке приложения, реализующего описанный алгоритм, за основу взят программный комплекс из предыдущей части статьи. В нем используется тот же файл ресурсов, однако программный файл модифицирован.
Приложение 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; //Получим из драйвера аппаратные данные
}
Главная функция WinMain() выполняет три характерные для нее процедуры: регистрацию класса главного окна, создание и показ главного окна, цикл обработки сообщений. В цикле обработки сообщений имеется два принципиальных отличия от примера предыдущей части статьи: в каждом шаге цикла проверяется состояние флага завершения измерений request, и если флаг оказывается установленным, то вызывается функция Windows PostMessage(), которая ставит в очередь сообщений приведенного выше приложения наше сообщение с кодом WM_USER. Для того чтобы в цикл обработки сообщений включить проверку флага, пришлось заменить в нем функцию GetMessage() на функцию PeekMessage(), которая, в отличие от GetMessage(), при отсутствии сообщений в очереди возвращает управление в программу, что и дает возможность включить в цикл дополнительные действия. Однако PeekMessage() не анализирует сообщение WM_QUIT о завершении программы, поэтому <вылавливание> этого сообщения (и завершение программы оператором return 0 в случае его прихода) приходится выполнять вручную. Конструкция:
позволяет организовать бесконечный цикл, поскольку условие продолжения цикла, анализируемое оператором while, безусловно удовлетворяется (константа 1 никогда не может стать равной 0).
В оконной функции WndProc() фиксируется приход трех сообщений: WM_COMMAND от пунктов меню, WM_DESTROY от команд завершения приложения и WM_USER, свидетельствующего об окончании измерений. Поскольку для сообщения WM_USER в файле windowsx.h отсутствует макрос HANDLE_WM_USER, его пришлось определить в начале программы с помощью оператора #define, построив макрорасширение по аналогии с каким-либо из макросов вида HANDLE_сообщение из файла windowsx.h, хотя бы с макросом HANDLE_WM_DESTROY.
Фрагмент программы, выполняемый при выборе пользователем пункта меню <Пуск>, содержит лишь вызов функции InitCard(). В ней вызовом вложенной функции GetAPIEntry определяется адрес API-процедуры драйвера, а затем, после заполнения ряда регистров параметрами, передаваемыми в драйвер, вызывается эта процедура. В драйвер передаются следующие параметры: селектор сегмента данных приложения, три константы для инициализации платы, а также селектор и смещение обработчика прерываний приложения isr(). Передача в драйвер содержимого сегментного регистра DS (селектора сегмента данных) необходима потому, что при вызове драйвером (точнее, VMM) нашей функции isr() не восстанавливается операционная среда приложения, в частности регистр DS не указывает на поля данных приложения, которые в результате оказываются недоступными. Передав в драйвер содержимое DS, мы сможем вернуть его назад вместе с другими данными, передаваемыми из драйвера в приложение, восстановив тем самым адресуемость данных.
При выборе пользователем пунктов меню <Чтение> или <Выход> выполняются те же действия, что и в предыдущем примере.
По сравнению с предыдущим примером упростилась функция OnDestroy(). Поскольку восстановление маски в контроллере прерываний возложено теперь на драйвер, а исходный вектор мы в этом варианте программы не восстанавливаем, то в функции OnDestroy() лишь вызывается функция Windows PostQuitMessage(), приводящая к завершению программы.
В обработчике прерываний приложения isr() после засылки в регистр DS нашего же селектора сегмента данных, переданного ранее в драйвер и полученного из него в качестве первого параметра функции isr(), выполняется инкремент флага request и пересылка в переменную data второго параметра функции isr() - результата измерений.