Виртуальный драйвер для обслуживания аппаратных прерываний - Разработка приложения

ОГЛАВЛЕНИЕ

Разработка приложения 

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

Приложение 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 в случае его прихода) приходится выполнять вручную. Конструкция:

do{ ...   }while(1)

позволяет организовать бесконечный цикл, поскольку условие продолжения цикла, анализируемое оператором 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() - результата измерений.