IZONE - http://www.izcity.com/ - бесплатный софт, вэб-сервисы, ресурсы для раскрутки, свежие номера журнала "Internet Zone".

 IZONE 


Окна своими руками-2

К.И. ЯКОВЛЕВ (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/

 


Copyright © "Internet Zone"info@izcity.com
Копирование и использование данных материалов разрешается только в случае указания на журнал "Internet Zone", как на источник получения информации. При этом во всех ссылках обязательно явное указание адреса вэб-сайта http://www.izcity.com/. При наличии у копируемого материала авторов и источника информации - их также нужно указывать, наряду со ссылкой на нас.