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