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


 

Вернемся ненадолго к тексту приложения. Функция isr(), которую мы вызываем из драйвера, имеет следующий вид:

void isr(int segment,int dt){ 
...
}

Поскольку мы в драйвере протолкнули в стек сначала данные Data, а затем селектор DSseg, они расположились в стеке приложения в правильном с точки зрения этой функции порядке, поэтому она может обращаться к своим локальным переменным segment и dt, как если бы была вызвана обычным образом оператором:

isr(DSseg,Data);

После завершения функции isr() управление возвращается в VMM, а из него в драйвер на команду, следующую за вызовом Resume_Exec. Этот переход может потребовать пары сотен команд и нескольких десятков микросекунд.

Отложенная процедура драйвера завершается очевидными вызовами End_Nest_Exec - окончания вложенного блока выполнения и Pop_Client_State - восстановления структуры клиента.

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

Заметного упрощения программы можно добиться, организовав посылку в приложение сообщения WM_USER непосредственно из обработчика прерываний драйвера, точнее с уровня отложенных прерываний. В этом случае отпадает необходимость в передаче драйверу каких-либо данных, кроме дескриптора того окна приложения, в которое посылается сообщение WM_USER, в данном случае - дескриптора главного окна. Сокращается и текст процедуры отложенных прерываний Reflect_Int. Приложение Windows также упрощается: отпадает необходимость в разделении функций обработки прерываний между функцией Isr(), работающей, по существу, на уровне отложенных прерываний, и функцией OnUser(), выполняемой уже на обычном уровне задачи. Поскольку результат измерений легко передать из драйвера в приложение в качестве параметра сообщения WM_USER, исчезает необходимость в пункте <Чтение> в меню приложения.

Рассмотрим изменения, вносимые при использовании такого метода.

Приложение Windows, обрабатывающее аппаратные прерывания

//Операторы препроцессора #define и #include 
...
#define HANDLE_WM_USER(hwnd, wParam, lParam, fn) \//Макрос для обработки
((fn)(hwnd,wParam), 0L) // сообщения WM_USER
//Прототипы функций
...
void OnUser(HWND,WPARAM); //Сигнатура функции изменилась
//Глобальные переменные
HWND hMainWnd; //Дескриптор главного окна
...
//Главная функция WinMain
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE,LPSTR,int){
...
while(GetMessage(&msg,NULL,0,0))//Обычный цикл
DispatchMessage(&msg);//обработки сообщений
return 0;
}//Конец WinMain
//Функция Register регистрации класса окна
...
//Функция 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_EXIT://Завершить приложение
DestroyWindow(hwnd);
}
}
//Функция OnUser обработки сообщения WM_USER
void OnUser(HWND,WPARAM wParam){
char txt [80];
wsprintf(txt,"Измерения окончены\nНакоплено %d событий",(UINT)wParam);
MessageBox(hMainWnd,txt,"Контроль",MB_ICONINFORMATION);
}
//Функция OnDestroy обработки сообщения WM_DESTROY
...
//Функция InitCard инициализации (через драйвер) платы
void InitCard (){
GetAPIEntry(); //Получение адреса точки входа в драйвер
_BX=55; //Канал 0
_CX=20000; //Канал 1; BX*CX=Длительность интервала
_DX=1000; //Канал 2, внутренний генератор
_DI=(UINT)hMainWnd; //Дескриптор главного окна
VxDEntry(); //Вызов драйвера
}
//Функция GetAPIEntry получения адреса точки входа в драйвер
void GetAPIEntry()
...

В приведенном выше тексте файла .CPP детально показаны только измененные участки программы.

Изменилось определение макроса HANDLE_WM_USER для обработки сообщения WM_USER, которое мы пошлем в приложение из драйвера: функция обработки этого сообщения fn (в приложении она носит название OnUser()) принимает два параметра - hwnd и wParam. Через параметр сообщения wParam в приложение будет передан результат измерений. При необходимости передавать в приложение больший объем данных можно было расширить состав параметров функции третьим параметром lParam.

Цикл обработки сообщений существенно упростился и принял форму, обычную для простых приложений Windows.

В функции OnCommand() удален фрагмент, связанный с пунктом меню <Чтение> (идентификатор MI_READ), поскольку в этом пункте уже нет необходимости.

В функции OnUser() параметр wParam приводится к типу целого без знака, преобразуется в символьную форму и выводится на экран в окно сообщения с соответствующим текстом.

Как будет видно из программы драйвера (и как, впрочем, должно быть очевидно для читателя), при посылке из драйвера сообщения WM_USER необходимо указать системе, какому окну адресовано это сообщение. Однако драйверу, естественно, ничего не известно об окнах приложений; дескриптор нашего главного окна следует передать драйверу на этапе инициализации платы. Эта операция выполняется в функции InitCard(), где перед вызовом драйвера в регистры BX, CX и DX засылаются константы настройки таймера, а регистр DI является приведенным к целому типу дескриптором главного окна hMainWnd.

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