При изменении размеров родительского окна в дочернее поступает сообщение WM_PAINT, по которому в переменной R формируются размеры родительского окна и при помощи функции MoveWindow(hWndC, R.left, R.bottom-iHeight, R.right, R.bottom-iHeight, 1) дочернему окну выделяется нижняя часть родительского окна.
BOOL PostMessage(
Функция PostMessage помещает сообщение в очередь сообщений, связанную с потоком, который создал заданное окно и возвращает значение без ожидания потока, который обрабатывает сообщение.
HWND hWnd,
UINT Msg,
WPARAM wParam,
LPARAM lParam
);
Если вводимый символ не является символьным кодом виртуальной клавиши Enter и количество введённых символов меньше 21, то оконная функция ChldWndProc дочернего окна записывает символы в буфер Password при помощи указателя Str.
После этого выполняется функция InvalidateRect() для порождения сообщения WM_PAINT, которое придет в дочернее окно и обновит отображаемую в нем строку.
10) Основное окно получает сообщение WM_CОMMAND и по значению идентификатора в LОWОRD(wParam) определяет, что оно пришло не от меню, а от дочернего окна, и вызывает функцию обработки пароля OnChild(), передавая ей в качестве второго параметра код извещения. Соответствующий фрагмент кода приведен в листинге:
|
|
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_EXIT:
DestroyWindow(hWnd);
break;
case NumC:
OnChld(hWnd,message,HIWORD(wParam),lParam); // Обработка пароля
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
11) Код функции обработки пароля приведён в листинге:
void OnChld(HWND hWnd,UINT message,int Length,LPARAM lParam)
{
if(strcmp(PassWord,TruePassWord)||Length)
MessageBox(hWnd, "Надо вспомнить правильный пароль","Скажи \"Пароль\"",0);
else MessageBox(hWnd, "Пароль принят","",0);
}
На основе сравнения строк и проверки длины введенного пароля функция принимает решение о приеме пароля и отображает соответствующее сообщение при помощи функции MessageBox().
15) Создание дочернего окна с дескриптором Chld оформлено отдельной функцией.
BOOL CreateChild(HWND hWnd)
{static WNDCLASS wc;
if(Chld) //Стандартный для WINDOWS прием - чтобы не думать, о
// действиях при попытке нового ввода до завершения предыдущего
DestroyWindow(Chld);
UnregisterClass("ReadStr",GetModuleHandle(NULL));
// Раз было окно, значит был и класс
// Если класс не убрать, получится ошибка при новой регистрации
}
wc.hInstance = GetModuleHandle(NULL);
wc.hbrBackground =(HBRUSH)(COLOR_BTNFACE+1);
wc.lpfnWndProc = ChldWndProc;
wc.lpszClassName = "ReadStr";
if (!RegisterClass(&wc)) return 0;
Chld=0;
Chld=CreateWindowEx (WS_EX_CLIENTEDGE, "ReadStr",
"Значения ", WS_CHILD|WS_VISIBLE, 30,30,60,60,
hWnd, HMENU(NumC),GetModuleHandle(NULL), NULL);
SetFocus(Chld);
return (int)Chld;
}
После первого щелчка в рабочей области родительского окна левой кнопкой мыши и до завершения ввода пароля дескриптор Chld не будет равен нулю. При повторном вызове функции CreateChild (то есть при повторной активизации левой кнопки мыши в клиентской области родительского окна) уничтожается дочернее окно и отменяется регистрация класса.
|
|
Пример №1: Создания главного окна с дочерним окном для ввода пароля.
#include "stdafx.h"
#include "resource.h"
#define MAX_LOADSTRING 100
// Global Variables:
HINSTANCE hInst; // current instance
TCHAR szTitle[MAX_LOADSTRING]; // The title bar text
TCHAR szWindowClass[MAX_LOADSTRING]; // The title bar text
static TCHAR PassWord[7]; //введенный пользователем пароль
static TCHAR TruePassWord[]=_T("Password"); //Правильный пароль
static HWND Chld;
static WORD i;
static TCHAR *Str=NULL;
static int sMax;
int wHeight=30; //Высота дочернего окна
static TCHAR Sym[80];
// Foward declarations of functions included in this code module:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
BOOL CreateChild(HWND hWnd);
void InitStr(HWND hWnd,TCHAR *s,int Max,TCHAR Sy);
void OnChld(HWND hWnd,UINT message,int Length,LPARAM lParam);
LRESULT APIENTRY ChldWndProc (HWND UkOkn,UINT message, WPARAM wParam,LPARAM lParam);
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: Place code here.
MSG msg;
HACCEL hAccelTable;
// Initialize global strings
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_CHILD_WINDOW, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// Perform application initialization:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_CHILD_WINDOW);
// Main message loop:
while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return msg.wParam;
}
LRESULT APIENTRY ChldWndProc(HWND hWndC,UINT message,WPARAM wParam,LPARAM lParam)
{ RECT R;
switch (message)
{
case WM_CHAR:
if (LOWORD(wParam)==VK_RETURN || i>=20)
{
Str[i]=0;
PostMessage(GetParent(hWndC),WM_COMMAND,MAKELONG(NumC, i-sMax),0);
i=0;
DestroyWindow(hWndC);
UnregisterClass(_T("ReadStr"),GetModuleHandle(NULL));
}
else
{
if(++i<=sMax)
Str[i-1]=(char)LOWORD(wParam);
InvalidateRect(hWndC,NULL,1);
}
break;
case WM_PAINT:
{PAINTSTRUCT ps;
GetClientRect(GetParent(hWndC),&R);
MoveWindow(hWndC,R.left,R.top,R.right,R.top+wHeight,1);
BeginPaint(hWndC,&ps);
SetBkMode(ps.hdc, TRANSPARENT);
TextOut(ps.hdc,0,0,_T("Введите пароль: "),14);
TextOut(ps.hdc,17*8,0,Sym,i);
EndPaint(hWndC,&ps);
}
return 0;
default:
return DefWindowProc(hWndC,message,wParam,lParam);
}
return 0;
}
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = (WNDPROC)WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, (LPCTSTR)IDI_CHILD_WINDOW);
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = (LPCWSTR)IDC_CHILD_WINDOW;
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL);
return RegisterClassEx(&wcex);
}
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;
hInst = hInstance; // Store instance handle in our global variable
hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
400, 300, 500, 500, NULL, NULL, hInstance, NULL);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
void OnChld(HWND hWnd,UINT message,int Length,LPARAM lParam)
{
if(_tcscmp(PassWord,TruePassWord)||Length)
MessageBox(hWnd, _T("Надо вспомнить правильный пароль"),_T("Скажи \"Пароль\""),0);
else MessageBox(hWnd, _T(" Пароль принят"),_T(""),0);
}
BOOL CreateChild(HWND hWnd)
{static WNDCLASS wc;
if(Chld) //Стандартный для WINDOWS прием - чтобы не думать, о действих при
{ //попытке нового ввода до завершения
DestroyWindow(Chld); // предыдущего.
UnregisterClass(_T("ReadStr"),GetModuleHandle(NULL));
// Раз было окно, значит,был и класс
// Если класс не убрать, получится ошибка при новой регистрации
}
wc.hInstance = GetModuleHandle(NULL);
wc.hbrBackground =(HBRUSH)(COLOR_BTNFACE+1);
wc.lpfnWndProc = ChldWndProc;
wc.lpszClassName = _T("ReadStr");
if (!RegisterClass(&wc)) return 0;
Chld=0;
Chld=CreateWindowEx
(WS_EX_CLIENTEDGE, _T("ReadStr"), _T(" Значения "),
WS_CHILD|WS_VISIBLE,
30,30,60,60,
hWnd, HMENU(NumC),GetModuleHandle(NULL), NULL);
SetFocus(Chld);
return (int)Chld;
}
void InitStr(HWND hWnd,TCHAR *s,int Max,TCHAR Sy)
{
Str=s;
sMax=Max;
int j;
for (j=0; j<21;j++)Sym[j]=Sy;
Sym[j]=0;
CreateChild(hWnd);
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
TCHAR szHello[MAX_LOADSTRING];
LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);
switch (message)
{
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_EXIT:
DestroyWindow(hWnd);
break;
case NumC:
OnChld(hWnd,message,HIWORD(wParam),lParam); // Обработка пароля
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_LBUTTONDOWN:
InitStr(hWnd,PassWord,8,_T('*'));
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
Пример 2: Окно приложения стиля временного окна с кнопкой минимизации и с фоном цвета рабочего стола Windows занимает весь экран. При попытке запустить второй экземпляр приложения на передний план перемещается окно первого экземпляра и завершается работа.
|
|
Шаг 1)
Для поиска приложения используется функция FindWindow:
HWND hwnd= FindWindow(szMainClass,szTitle);
Данная функция возвращает дескриптор окна класса szMainClass с заголовком szTitle. Если такого окна нет, то это первый экземпляр приложения и дескриптору hwnd присваивается NULL. Иначе возвращается дескриптор найденного окна.
Если работает экземпляр другого приложения с окном класса с таким же именем szMainClass и заголовком szTitle, то данная функция вернёт дескриптор окна другого приложения иначе переходим к шагу 5.
Шаг 2)
Если «предыдущий экземпляр приложения» обнаружен, то выводим сообщение:
MessageBox(hwnd,”Можно запускать только одну копию приложения!!!\n\n”
“Перемещаю на передний план первый экземпляр”,
szTitle,MB_OK);
Шаг 3)
До перемещения окна на передний план необходимо проверить состояние этого окна и, если оно свёрнуто, то восстанавливаем его предыдущее состояние:
If(IsIconic(hwnd)) ShowWindow(hwnd,SW_RESTORE);
SW_RESTORE - активизирует и отображает окно. Если окно минимизировано или развернуто, Windows восстанавливает его в первоначальном размере и позиции (то же самое, что и SW_SHOWNORMAL).
Шаг 4)
Перемещаем окно первого экземпляра на передний план:
SetForegroundWindow(hwnd);
Затем второй экземпляр приложения завершает работу.
Шаг 5)
В случае, если предыдущий экземпляр приложения не найден, то регистрируем класс окна:
if (!RegClass(WndProc,szMainClass,COLOR_DESKTOP))
return FALSE;
Шаг 6)
Для создания максимально распахнутого окна приложения необходимо определить ширину и высоту экрана в пикселях:
int w=GetSystemMetrics(SM_CXSCREEN);
int h=GetSystemMetrics(SM_CYSCREEN);
Шаг 7)
Создаём окно:
Hwnd=CreateWindow(szMainClass,szTitle,WS_POPUPWINDOW|WS_CAPTION|WS_MINIMIZEBOX|WS_VISIBLE,0,0,w,h,0,0,hInstance,NULL);
Стиль WS_POPUPWINDOW|WS_CAPTION|WS_MINIMIZEBOX|WS_VISIBLE указывает на временное окно с кнопкой минимизации и заголовком.
Шаг 8)
Далее создаём цикл обработки сообщений приложения:
while (GetMessage(&msg,0,0,0)) DispatchMessage(&msg);
Так как приложение не предусматривает обработки сообщений от клавиатуры, то в цикле отсутствует вызов функции трансляции клавиатурных сообщений.
|
|
Шаг 9)
Создаём оконную процедуру:
LRESULT CALLBACK WndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{switch(msg)
{
case WM_DESTROY:
{PostQuitMessage(0); return 0;}
}
return DefWindowProc(hwnd,msg,wParam,lParam);
}
______________________________________________________________
Листинг 1:
#include <windows.h>
#include<tchar.h> // for UNICODE charset
bool RegClass(WNDPROC,LPCTSTR,UINT);
LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
HINSTANCE hInstance;
TCHAR szMainClass[]=_T("MainClass");
TCHAR szTitle[]=_T("Пример 1.1");
int WINAPI WinMain(HINSTANCE hInst,HINSTANCE hPrevInst,
LPSTR lpszCmdLine, int nCmdShow)
{
MSG msg;
hInstance=hInst;
HWND hwnd=FindWindow(szMainClass,szTitle);
if (hwnd)
{
MessageBox(hwnd,_T("Можно запускать только один экземпляр приложения!\n")
_T("Перемещаю на передний план первый экземпляр"),
szTitle,MB_OK|MB_ICONSTOP);
if (IsIconic(hwnd))
ShowWindow(hwnd,SW_RESTORE);
SetForegroundWindow(hwnd);
return 0;
}
if (!RegClass(WndProc,szMainClass,COLOR_DESKTOP))
return FALSE;
int w=GetSystemMetrics(SM_CXSCREEN)-1;
int h=GetSystemMetrics(SM_CYSCREEN)-1;
hwnd=CreateWindow(szMainClass,szTitle,WS_POPUPWINDOW|WS_CAPTION|
WS_MINIMIZEBOX|WS_VISIBLE,
0,0,w,h,0,0,hInstance,NULL);
if (!hwnd) return FALSE;
while (GetMessage(&msg,0,0,0))
DispatchMessage(&msg);
return msg.wParam;
}
bool RegClass(WNDPROC Proc,LPCTSTR szName,UINT brBackground)
{
WNDCLASS wc;
wc.style=wc.cbClsExtra=wc.cbWndExtra=0;
wc.lpfnWndProc=Proc;
wc.hInstance=hInstance;
wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);
wc.hCursor=LoadCursor(NULL,IDC_ARROW);
wc.hbrBackground=(HBRUSH)(brBackground+1);
wc.lpszMenuName=(LPCTSTR)NULL;
wc.lpszClassName=szName;
return (RegisterClass(&wc)!=0);
}
LRESULT CALLBACK WndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{switch(msg)
{
case WM_DESTROY:
{PostQuitMessage(0); return 0;}
}
return DefWindowProc(hwnd,msg,wParam,lParam);
}
Самостоятельно на практике: Приведенный код перевести на ассемблер.