- К.И. ЯКОВЛЕВ (jki_home@hotmail.com)
Таким образом, мы
описали структуру меню, потом связали
ее с классом окна - оно получает строчку
меню, которое при обращении к нему
будет формировать сообщения,
обрабатываемые функцией окна. С диалоговыми
окнами сложнее - приходится
подгонять размеры и положение
элементов управления. Но поскольку
такое описание делается отдельно от
кода, его также можно отдельно
отредактировать (в BC тем же ResourceWorkshop'ом)
в режиме WYSIWYG (What You See Is What You Get).
Рисуете себе окошки, кнопочки,
растягиваете, перемещаете, а потом
сохраняете в виде текстового (.rc)
или двоичного (.res) файла.
А вот в DOS'e в этом случае действительно
приходилось наугад выставлять размеры
окон, окантовку, компилировать,
запускать - увидев, что текст вылез за
рамку, вновь редактировать,
компилировать...
Перейдем к самой программе. Как
известно любому программирующему на
С, выполнение программы должно
начинаться с функции Main. Однако а
случае с приложением Windows, все
начинается с WinMain:
int PASCAL WinMain(HINSTANCE
hInst,HINSTANCE hPreInst,LPSTR lpszCmdLine,int nCmdShow)
{
HWND hWnd;MSG lpMsg;WNDCLASS wcApp;
if(!hPreInst){wcApp.lpszClassName=szProgName;
wcApp.hInstance=(hi=hInst);
wcApp.lpfnWndProc=WndProc;
wcApp.hCursor=LoadCursor(NULL,IDC_ARROW);
wcApp.hIcon=LoadIcon(hInst,"EnvIcon");wcApp.lpszMenuName=NULL;
wcApp.hbrBackground=(hBr=GetStockObject(BLACK_BRUSH));
wcApp.style=CS_HREDRAW|CS_VREDRAW;
wcApp.cbClsExtra=0;wcApp.cbWndExtra=0;
if(!RegisterClass(&wcApp))return 0;}else return 0;
hWnd=CreateWindow(szProgName,NULL,WS_POPUP,CW_USEDEFAULT,
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,(HWND)NULL, (HMENU)NULL,(HANDLE)hInst,(LPSTR)NULL);
ShowWindow(hWnd,nCmdShow);// UpdateWindow(hWnd);
SendMessage(hWnd,WM_SYSCOMMAND,SC_MAXIMIZE,0L);
while(GetMessage(&lpMsg,NULL,0,0)){
TranslateMessage(&lpMsg);DispatchMessage(&lpMsg);}return(lpMsg.wParam);
}
}
Как видно, в отличие от
DOS'овской Main, у Windows и другой набор
параметров, и иной порядок их передачи, о
чем свидетельствует модификатор PASCAL
перед именем функции.
Недоумение вызывают и нестандартные
типы данных, встречающиеся, начиная с
описания параметров - HINSTANCE, LPSTR, HWND, MSG,
WNDCLASS... Что это?
Эти типы определены в windows.h и других
включаемых файлах. Часть из них (MSG,
WNDCLASS) представляют собой
структуры данных Windows. Другие - просто
алиасы стандартных типов: LPSTR -
дальний указатель на строку, HWND, HINSTANCE,
UINT - 32-битные целые числа.
Это полезно и при переносимости (в Windows
3.х некоторым из них соответствовали 16-битные
данные), и при контроле правильности
кода.
Параметры WinMain имеют следующее
значение:
HINSTANCE hInst - идентификационный номер
запущенного процесса;
HINSTANCE hPreInst - идентификационный номер
предыдущей копии процесса - рудимент Windows
3.х - в Windows 95 всегда 0;
LPSTR lpszCmdLine - указатель на командную
строку;
int nCmdShow - "рекомендуемый"
начальный режим отображения главного
окна (нормальный, свернутый в иконку
или полный экран).
Далее большая часть кода посвящена
определению класса окон wcApp: ему
присваивается имя, идентификатор
процесса, указывается функция окна, с
ним связываются стандартные или
создаются (загружаются из ресурсов)
курсор, кисть, иконка, меню,
устанавливается ряд дополнительных
параметров. Затем класс регистрируется
функцией RegisterClass, и функцией CreateWindow
создается его конкретное окно, при этом
снова задается ряд параметров.
Голос поклонника ООП: "Знакомые все
лица! Я же говорил, что система обектно-ориентированная".
Голос мастдайщика: "Я понял, куда вся
память девается! Класс, экземпляр,
курсоры, кисти, иконки, меню - а программа
еще ничего не делает!"
Наконец, окно активируется функцией с
параметром отображения ShowWindow(hWnd,nCmdShow),
что считается хорошим тоном. Хотя в
нашем случае оно сразу же
максимизируется... нажатием
соответствующей кнопки в системном меню!
Точнее, его симуляцией функцией SendMessage(hWnd,WM_SYSCOMMAND,SC_MAXIMIZE,0L).
Заканчивается наша главная функция
циклом while, в котором пока не придет
сообщение завершения, происходит прием
(GetMessage), трансляция (TranslateMessage) и диспетчеризация
(DispatchMessage) сообщений.
Перейдем к функции окна, обрабатывающей
сообщения:
LRESULT CALLBACK WndProc(HWND
hWnd,UINT messg,WPARAM wParam,LPARAM lParam){
HDC hdc,hwdc;PAINTSTRUCT ps;char strbuf[256];int dx,dy;
switch(messg){
case WM_ACTIVATE:
case WM_ACTIVATEAPP:
if(!wParam)PostMessage(hWnd,WM_CLOSE,0,0L);break;
case WM_MOUSEMOVE:
NewPt.x=LOWORD(lParam);NewPt.y=HIWORD(lParam);ClientToScreen(hWnd,&NewPt);
if((NewPt.x!=OldPt.x)||(NewPt.y!=OldPt.y))PostMessage(hWnd,WM_CLOSE,0,0L);
break;
case WM_KEYDOWN:
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_SYSKEYDOWN:
PostMessage(hWnd,WM_CLOSE,0,0L);break;
case WM_PAINT:hdc=BeginPaint(hWnd,&ps);
GetClientRect(hWnd,&rect);
SelectObject(hdc,hbmp);GetObject(hbmp,sizeof(bm),(LPSTR)&bm);
r2.left=r1.left=rect.left+(rect.right-rect.left)/4;
r2.right=r1.right=rect.right-(rect.right-rect.left)/4;
r2.top=r1.top=rect.left+(rect.bottom-rect.top)/4;
r2.bottom=r1.bottom=rect.bottom-(rect.bottom-rect.top)/4;
ValidateRect(hWnd,NULL);EndPaint(hWnd,&ps);
break;
case WM_TIMER:if((++tc)==2160)tc=0;tcount=(tc/dtCadr)+1;
hdc=GetDC(hWnd);hwdc=CreateCompatibleDC(hdc);
if((tc==1)||!(tc%dtCadr)){FillRect(hdc,&r2,hBr);time=GetCurrentTime();
dx=((r1.right-r1.left)*LOBYTE(LOWORD(time)))/256;
dy=((r1.bottom-r1.top)*HIBYTE(LOWORD(time)))/256;
r2.left=r1.left+dx;r2.right=r1.right+dx;r2.top=r1.top+dy;r2.bottom=r1.bottom+dy;};
SelectObject(hwdc,hbmp);GetObject(hbmp,sizeof(bm),(LPSTR)&bm);
BitBlt(hdc,xc+=xs,yc+=ys,bm.bmWidth,bm.bmHeight,hwdc,0,0,SRCCOPY);
DeleteDC(hwdc);
if((xc==rect.left)||((xc+bm.bmWidth)==rect.right))xs=-xs;
if((yc==rect.top)||((yc+bm.bmHeight)==rect.bottom))ys=-ys;
if(((tc==1)||!(tc%dtCadr))||!((xc>r2.right)||
((xc+bm.bmWidth)<r2.left)||(yc>r2.bottom)
||((yc+bm.bmHeight)<r2.top))){
LoadString(hi,tcount,strbuf,255);DrawText(hdc,strbuf,-1,&r2,DT_CENTER);};
ReleaseDC(hWnd,hdc);
break;
case WM_DESTROY:
DeleteObject(hbmp);KillTimer(hWnd,idTimer);ShowCursor(TRUE);
PostQuitMessage(0);
break;
case WM_CREATE:
ShowCursor(FALSE);GetCursorPos(&OldPt);hbmp=LoadBitmap(hi,szBMName);
idTimer=SetTimer(hWnd,NULL,50,(TIMERPROC)NULL)
;break;
default:return(DefWindowProc(hWnd,messg,wParam,lParam));}
return(0);
}
}
Модификатор CALLBACK
означает "повторно вызываемый".
Ведь функция связывается с классом, у
которого одновременно может быть
открыто несколько окон. Одному окошку
сообщения тоже могут приходить, не
дожидаясь, пока предыдущее будет
обработано - что напоминает рекурсию.
Параметры функции окна - параметры
сообщения: хэндл окна, которому
адресовано сообщение, номер-тип
сообщения, wParam и lParam. В Windows 3.х wParam
действительно был 16-битным (w-word), а lParam 32-битным
(long), но в более поздних версиях ОС они
оба 32-битные. Впрочем, почему "был"?
Windows 3.х не только по сей день
используется, но и сопровождается Microsoft!
Например, IE 5.0 существует еще и в
версии для Windows 3.х!
Обработка сообщений организована
посредством оператора case по
параметру messg. Например, выбор пункта
меню порождает сообщение WM_COMMAND (системного
меню - WM_SYSCOMMAND), а параметр wParam
определяет, какой именно пункт выбран.
Рассмотрим кратко, без каких сообщений
категорически нельзя обойтись:
- WM_CREATE - инициация окна, "конструктор";
- WM_DESTROY - завершение работы, "деструктор"
окна;
- WM_PAINT - перерисовка окна;
- default:return(DefWindowProc(hWnd,messg,wParam,lParam)) - ни
одна функция окна не обрабатывает ВСЕ
передаваемые окну сообщения, и если не
передавать необработанные DefWindowProc,
программа будет работать некорректно
или вообще лишится жизни J.
Остальные сообщения реализуют логику
прикладной программы. Например, в нашем
случае, по сообщениям таймера,
перемещается картинка, сделанная с
черной окантовкой, так что она "заметает
собственный след", и при
необходимости перерисовывается текст. А
по сообщениям о нажатии клавиш и кнопок
мыши, завершается работа скринсэйвера.
Вот и все в нулевом приближении. Многое,
увы, осталось "за кадром", но всему
свое время.
(Продолжение следует)
Источник: http://www.mycomp.com.ua/
|