Window Application
За основу мы возьмем приложение WINDOW из 11 тома "Библиотеки системного программиста".
Внешний вид главного окна 32-разрядного варианта приложения WINDOW, запущенного в среде Microsoft Windows95, показан на рис. 1.16.
Рис. 1.16. Внешний вид приложения Window Application
Если сделать щелчок левой клавишей мыши по внутренней области окна, на экране появится сообщение (рис. 1.17).
Рис. 1.17. Сообщение, которое появляется после щелчка левой клавишей мыши внутри окна приложения Window Application
Если вы обратили внимание на внешний вид системного меню окон Microsoft Windows 95, то заметили, что вместо ни о чем не говорящей горизонтальной черточки в левой части заголовка окна отображается маленькая пиктограмма. По внешнему виду этой пиктограммы пользователь сможет легко узнать окно вашего приложения.
Надпись на заголовке окна выровнена влево, а не отцентрирована, как это было раньше в Microsoft Windows версии 3.1.
В правой части заголовка появились пиктограммы, с помощью которых можно выполнить следующие действия:
Пиктограмма | Назначение |
Минимизация окна приложения | |
Восстановление нормального размера окна | |
Удаление окна, которое приведет к завершению работы приложения (если удаляется главное окно) |
Исходные тексты приложения Window Application
Теперь мы перейдем к исходным текстам приложения Window Application. Основной файл исходного текста приложения приведен в листинге 1.1.
Листинг 1.1. Файл window\window.c
#include <windows.h> #include <windowsx.h> #include "afxres.h" #include "resource.h"
// ----------------------------------------------------- // Описание функций // -----------------------------------------------------
// Функция главного окна LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
// Функция для обработки сообщения WM_CREATE BOOL WndProc_OnCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct);
// Функция для обработки сообщения WM_LBUTTONDOWN void WndProc_OnLButtonDown(HWND hWnd, BOOL fDoubleClick, int x, int y, UINT keyFlags);
// Функция для обработки сообщения WM_DESTROY void WndProc_OnDestroy(HWND hWnd);
// ----------------------------------------------------- // Глобальные переменные // -----------------------------------------------------
// Идентификатор приложения HINSTANCE hInst;
// Название приложения char szAppName[] = "Window";
// Заголовок главного окна приложения char szAppTitle[] = "Window Application";
// ----------------------------------------------------- // Функция WinMain // ----------------------------------------------------- int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX wc; HWND hWnd; MSG msg;
hInst = hInstance;
// Преверяем, не было ли это приложение запущено ранее hWnd = FindWindow(szAppName, NULL); if(hWnd) { // Если окно приложения было свернуто в пиктограмму, // восстанавливаем его if(IsIconic(hWnd)) ShowWindow(hWnd, SW_RESTORE);
// Выдвигаем окно приложения на передний план SetForegroundWindow(hWnd); return FALSE; }
// Регистрируем класс окна memset(&wc, 0, sizeof(wc));
// Поля wc.cbSize и wc.hIconSm определены в структуре // WNDCLASSEX, которой можно пользоваться для // регистрации класса окна в Windows 95 wc.cbSize = sizeof(WNDCLASSEX);
// Поле wc.hIconSm задает идентификатор маленькой // пиктограммы, которая будет отображаться в левой // части заголовка окна (в области системного меню). // Загружаем пиктограмму из ресурсов приложения при // помощи функции LoadImage, так как функция // LoadIcon может загрузить только обычную пиктограмму wc.hIconSm = LoadImage(hInst, MAKEINTRESOURCE(IDI_APPICON_SM), IMAGE_ICON, 16, 16, 0);
// Завершаем заполнение структуры WNDCLASSEX wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = (WNDPROC)WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst;
// Для загрузки обычной пиктограммы вы можете // использовать функцию LoadImage wc.hIcon = LoadImage(hInst, MAKEINTRESOURCE(IDI_APPICON), IMAGE_ICON, 32, 32, 0);
wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1); wc.lpszMenuName = NULL; wc.lpszClassName = szAppName;
// Вызываем функцию RegisterClassEx, которая выполняет // регистрацию окна if(!RegisterClassEx(&wc))
// В случае ошибки пытаемся зарегистрировать окно // функцией RegisterClass if(!RegisterClass((LPWNDCLASS)&wc.style)) return FALSE;
// Создаем главное окно приложения hWnd = CreateWindow(szAppName, szAppTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInst, NULL); if(!hWnd) return(FALSE);
// Отображаем окно ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd);
// Запускаем цикл обработки сообщений while(GetMessage (&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; }
// ----------------------------------------------------- // Функция WndProc // ----------------------------------------------------- LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { // Для сообщения WM_CREATE назначаем обработчик, // расположенный в функции WndProc_OnCreate HANDLE_MSG(hWnd, WM_CREATE, WndProc_OnCreate);
// Для сообщения WM_LBUTTONDOWN назначаем обработчик, // расположенный в функции WndProc_OnLButtonDown HANDLE_MSG(hWnd, WM_LBUTTONDOWN, WndProc_OnLButtonDown);
// Для сообщения WM_DESTROY назначаем обработчик, // расположенный в функции WndProc_OnDestroy HANDLE_MSG(hWnd, WM_DESTROY, WndProc_OnDestroy);
default: return(DefWindowProc(hWnd, msg, wParam, lParam)); } }
// ----------------------------------------------------- // Функция WndProc_OnCreate // ----------------------------------------------------- BOOL WndProc_OnCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct) { return TRUE; }
// ----------------------------------------------------- // Функция WndProc_OnDestroy // ----------------------------------------------------- // Отключаем предупреждающее сообщение о том, что // функция типа void возвращает управление при помощи // оператора return. Этот оператор нужен для // использования макрокоманды FORWARD_WM_LBUTTONDOWN #pragma warning(disable: 4098) void WndProc_OnDestroy(HWND hWnd) { PostQuitMessage(0); return FORWARD_WM_DESTROY(hWnd, DefWindowProc); }
// ----------------------------------------------------- // Функция WndProc_OnLButtonDown // -----------------------------------------------------
#pragma warning(disable: 4098) void WndProc_OnLButtonDown( HWND hWnd, BOOL fDoubleClick, int x, int y, UINT keyFlags) { MessageBox(NULL, "Hello, 32-bit world!", "Window", MB_OK);
return FORWARD_WM_LBUTTONDOWN(hWnd, fDoubleClick, x, y, keyFlags, DefWindowProc); }
Файл resource.h (листинг 1.2) был создан автоматически системой Microsoft Visual C++ и содержит символические определения идентификаторов ресурсов приложения.
Листинг 1.2. Файл window\resource.h
//{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. // Used by window.rc // #define IDI_APPICON 101 #define IDI_APPICON_SM 102
// Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NO_MFC 1 #define _APS_NEXT_RESOURCE_VALUE 105 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1000 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif
В файле ресурсов определены две пиктограммы - стандартная (с идентификатором IDI_APPICON) и маленькая (с идентификатором IDI_APPICON_SM).
Файл resource.h автоматически включается в файл определения ресурсов window.rc (листинг 1.3). Последний был создан также автоматически системой Microsoft Visual C++.
Листинг 1.3. Файл window\window.rc
//Microsoft Visual C++ generated resource script. // #include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS ////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include "afxres.h"
////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS
#ifdef APSTUDIO_INVOKED ////////////////////////////////////////////////////////////// // // TEXTINCLUDE //
1 TEXTINCLUDE DISCARDABLE BEGIN "resource.h\0" END
2 TEXTINCLUDE DISCARDABLE BEGIN "#include ""afxres.h""\r\n" "\0" END
3 TEXTINCLUDE DISCARDABLE BEGIN "\r\n" "\0" END
////////////////////////////////////////////////////////////// #endif // APSTUDIO_INVOKED
////////////////////////////////////////////////////////////// // // Icon // IDI_APPICON ICON DISCARDABLE "window.ico" IDI_APPICON_SM ICON DISCARDABLE "windowsm.ico"
#ifndef APSTUDIO_INVOKED ////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // ////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED
Функция WinMain
Прежде всего приложение сохраняет свой идентификатор в глобальной переменной hInst. Затем оно выполняет проверку, не было ли это приложение запущено ранее.
Напомним, что для выполнения проверки мы не можем воспользоваться параметром hPrevInstance, так как для приложений Win32 этот параметр всегда равен NULL.
Воспользуемся функцией FindWindow , которая позволяет найти окно верхнего уровня по имени класса окна или по заголовку окна:
HWND FindWindow( LPCTSTR lpClassName, // указатель на имя класса LPCTSTR lpWindowName // указатель на имя окна );
Через параметр lpClassName передается указатель на текстовую строку, закрытую двоичным нулем, в которую необходимо записать имя класса искомого окна. Вместо имени можно также передать через этот параметр атом, созданный функцией GlobalAddAtom , соответствующей строке имени класса. С атомами вы уже встречались в 11 томе "Библиотеки системного программиста".
Напомним, что атом является 16-разряной величиной, поэтому вы должны записать его значение в младшую половину 32-разрядного параметра lpClassName. Старшая половина при этом должна содержать нулевое значение.
На базе одного и того же класса можно создать несколько окон. Если вам нужно найти окно с заданным заголовком, то имя заголовка следует передать через параметр lpWindowName. Если же подойдет любое окно, параметр lpWindowName может иметь значение NULL.
Если окно будет найдено, функция FindWindow вернет его идентификатор. В противном случае она возвратит значение NULL.
В нашем случае мы можем ограничится указанием только первого параметра:
hWnd = FindWindow(szAppName, NULL); if(hWnd) { if(IsIconic(hWnd)) ShowWindow(hWnd, SW_RESTORE); SetForegroundWindow(hWnd); return FALSE; }
Пользователь может не помнить, какие приложения уже запущены, а какие нет. Когда он запускает приложение, делая двойной щелчок по соответствующей пиктограмме, то ожидает что на экране появится его главное окно. И в этом он абсолютно прав. Если приложение Window Application было запущено ранее, целесообразно активизировать и выдвинуть на передний план его главное окно. Это именно то, к чему приготовился пользователь.
Прежде всего мы проверяем, не было ли окно приложения минимизировано. Для этого мы вызываем известную вам из предыдущих томов "Библиотеки системного программиста" функцию IsIconic . Если окно минимизировано, восстанавливаем его нормальный размер с помощью функции ShowWindow , передавая ей в качестве первого параметра идентификатор найденного окна, а в качестве второго - константу SW_RESTORE .
Затем вне зависимости от того, было окно минимизировано или нет, выдвигаем его на передний план и активизируем с помощью функции SetForegroundWindow :
BOOL SetForegroundWindow( HWND hwnd // идентификатор активизируемого окна );
В случае успеха эта функция возвращает значение TRUE, при ошибке - FALSE.
Если была найдена работающая копия приложения Window Application, мы завершаем работу текущей копии приложения. Если же копия работающего приложения не найдена, функция WinMain приступает к инициализации приложения.
Прежде всего выполняется регистрация класса окна. Приложения Win32 должны выполнять такую регистрацию немного не так, как это делают приложения Win16.
Напомним, что для регистрации класса окна приложения Win16 должны заполнить структуру типа WNDCLASS и затем передать ее адрес функции RegisterClass . В операционной системе Microsoft Windows 95 эта структура была расширена и теперь имеет тип WNDCLASSEX :
typedef struct _WNDCLASSEX { UINT cbSize; UINT style; // -------------------------------- WNDPROC lpfnWndProc; // Эта часть структуры WNDCLASSEX int cbClsExtra; // идентична структуре WNDCLASS int cbWndExtra; // HANDLE hInstance; // HICON hIcon; // HCURSOR hCursor; // HBRUSH hbrBackground; // LPCTSTR lpszMenuName; // LPCTSTR lpszClassName; // -------------------------------- HICON hIconSm; } WNDCLASSEX;
В начало структуры было добавлено поле cbSize (размер структуры WNDCLASSEX), в конец - поле hIconSm (идентификатор маленькой пиктограммы, которая отображается в левом верхнем углу заголовка главного окна приложения). В остальном структура WNDCLASSEX в точности соответствует структуре WNDCLASS.
Для того чтобы зарегистрировать класс окна в операционной системе Microsoft Windows 95, вы должны заполнить структуру WNDCLASSEX и передать ее адрес функции RegisterClassEx . Заполнение структуры выполняется следующим образом:
memset(&wc, 0, sizeof(wc)); wc.cbSize = sizeof(WNDCLASSEX); wc.hIconSm = LoadImage(hInst, MAKEINTRESOURCE(IDI_APPICON_SM), IMAGE_ICON, 16, 16, 0); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = (WNDPROC)WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst; wc.hIcon = LoadImage(hInst, MAKEINTRESOURCE(IDI_APPICON), IMAGE_ICON, 32, 32, 0); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1); wc.lpszMenuName = NULL; wc.lpszClassName = szAppName;
Для получения идентификатора маленькой пиктограммы мы воспользовались новой функцией LoadImage , так как функция LoadIcon позволяет загрузить только стандартную пиктограмму.
Функция LoadImage - это возможно именно та функция, которой вам не хватало для работы с изображениями в программном интерфейсе Win16. С ее помощью вы можете загрузить пиктограмму, курсор, а также битовое изображение, причем не только из ресурсов приложения, но и из файла:
HANDLE LoadImage( HINSTANCE hInst, // идентификатор модуля, // содержащего изображение LPCTSTR lpszName, // имя или идентификатор изображения UINT uType, // тип изображения int cxDesired, // желаемая ширина изображения int cyDesired, // желаемая высота изображения UINT fuLoad // флаги загрузки );
Прежде всего мы перечислим возможные значения параметра fuLoad, определяющие выполняемые этой функцией действия.
Параметр | Описание |
LR_LOADFROMFILE | Функция загружает изображение из файла, заданного параметром lpszName |
LR_DEFAULTSIZE | Если значение параметров cxDesired и cyDesired равны нулю, при загрузке изображения используются размеры по умолчанию |
LR_LOADREALSIZE | Если значение параметров cxDesired и cyDesired равны нулю, при загрузке пиктограммы или курсора используются размеры по умолчанию, взятые из системных метрик |
LR_DEFAULTCOLOR | Для отображения используется текущий цветовой формат |
LR_MONOCROME | Изображение загружается как черно-белое (монохромное) |
LR_LOADMAP3DCOLORS | При загрузке функция ищет в изображении таблицу цветов и заменяет темно-серый, серый и светло-серый цвета на системные, которые используются для того чтобы придать изображению трехмерный вид. Конкретно, выполняется такая замена:Исходный цвет RGB На что меняется128, 128, 128 COLOR_3DSHADOW 192, 192, 192 COLOR_3DFACE 223, 223, 223 COLOR_3DLIGHT |
LR_LOADTRANSPARENT | Загрузка в "прозрачном" режиме. В процессе загрузки выбирается значение цвета первого пиксела изображения и заменяется соответствующим значением из таблицы цветов, содержащим цвет окна по умолчанию COLOR_WINDOW . Все пикселы изображения, которые используют это значение таблицы, приобретают цвет COLOR_WINDOW |
LR_SHARED | Если изображение загружается несколько раз, его идентификатор будет использован повторно. Этот флаг не рекомендуется использовать для изображений с нестандартными размерами, которые могут изменяться, а также для изображений, загружаемых из файла |
Загружая пиктограмму из ресурсов приложения при заполнении структуры WNDCLASSEX мы установили значение параметра fuLoad равным 0, что соответствует LR_DEFAULTCOLOR.
Через параметр hInst передается идентификатор модуля, в ресурсах которого находится изображение. В нашем случае это идентификатор приложения, полученный от функции WinMain.
С помощью функции LoadImage вы можете загрузить системные ресурсы (битовые изображения, пиктограммы и курсоры, которые используются операционной системой). Соответствующие изображения называются OEM-изображениями. Их идентификаторы определены в файле winuser.h. Идентификаторы пиктограмм имеют префикс OIC_, курсоров - OCR_, битовых изображений - OBM_.
Если вы загружаете системные ресурсы, через параметр hInst необходимо передать нулевое значение. Идентификатор ресурса при этом должен передаваться через младшее слово параметра lpszName.
Параметр uType определяет тип загружаемого изображения и может принимать следующие значения:
Значение | Описание |
IMAGE_BITMAP | Битовое изображение |
IMAGE_CURSOR | Курсор |
IMAGE_ICON | Пиктограмма |
При заполнении структуры WNDCLASSEX мы вызываем функцию LoadImage два раза. В первый раз мы загружаем маленькую пиктограмму с размерами 16 х 16 пикселов, во второй раз - стандартную пиктограмму с размерами 32 х 32 пиксела.
И наконец, через параметр lpszName необходимо передать идентификатор ресурса (если изображение загружается из ресурсов модуля) или адрес текстовой строки, содержащий имя файла (если изображение загружается из файла).
Заполнив поля структуры WNDCLASSEX, мы вызываем функцию RegisterClassEx :
if(!RegisterClassEx(&wc)) if(!RegisterClass((LPWNDCLASS)&wc.style)) return FALSE;
В среде Microsoft Windows 95 функция RegisterClassEx должна выполнить все необходимые для регистрации действия и вернуть значение атома, который идентифицирует зарегистрированный класс.
Однако если вы запустите наше приложение в среде Microsoft Windows NT версии 3.5, функция RegisterClassEx вернет значение NULL. В программном интерфейсе операционной системы этой версии регистрация класса окна должны выполняться функцией RegisterClass.
В следующих версиях Microsoft Windows NT, когда в эту операционную систему будет встроена объектно-ориентированная оболочка, аналогичная оболочке Microsoft Windows 95, вы сможете выполнить расширенную регистрацию с помощью функции RegisterClassEx. Поэтому, для того чтобы наша программа смогла работать и в среде Microsoft Windows 95, и в среде Microsoft Windows NT версии 3.5 (а также более новых версий), мы вначале пытаемся использовать для регистрации функцию RegisterClassEx, а в случае неудачи - функцию RegisterClass, передавая последней адрес обычной структуры WNDCLASS.
После регистрации приложение Window Application создает главное окно, отображает его и запускает цикл обработки сообщений. Все эти действия были подробно описаны в 11 томе "Библиотеки системного программиста".
Функция WndProc
Функция WndProc предназначена для обработки сообщений, поступающих в очередь нашего приложения:
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { HANDLE_MSG(hWnd, WM_CREATE, WndProc_OnCreate); HANDLE_MSG(hWnd, WM_LBUTTONDOWN, WndProc_OnLButtonDown); HANDLE_MSG(hWnd, WM_DESTROY, WndProc_OnDestroy); default: return(DefWindowProc(hWnd, msg, wParam, lParam)); } }
Ее особенностью является использование макрокоманды HANDLE_MSG, сокращающей объем листинга функции.
Макрокоманда HANDLE_MSG определена в файле windowsx.h следующим образом:
#define HANDLE_MSG(hwnd, msg, fn) \ case (msg): return HANDLE_##msg((hwnd), \ (wParam), (lParam), (fn))
Согласно этому определению, для обработки сообщения WM_CREATE вызывается макрокоманда HANDLE_WM_CREATE. Аналогично, для обработки сообщения WM_LBUTTONDOWN вызывается макрокоманда HANDLE_WM_LBUTTONDOWN, а для обработки сообщения WM_DESTROY - макрокоманда HANDLE_.WM_DESTROY.
Эти макрокоманды, наряду с аналогичными для других сообщений, определены в файле windowsx.h:
#define HANDLE_WM_CREATE(hwnd, wParam, lParam, fn) \ ((fn)((hwnd), (LPCREATESTRUCT)(lParam)) ? 0L : (LRESULT)-1L) #define HANDLE_WM_DESTROY(hwnd, wParam, lParam, fn) \ ((fn)(hwnd), 0L) #define HANDLE_WM_LBUTTONDOWN(hwnd, wParam, lParam, fn) \ ((fn)((hwnd), FALSE, (int)(short)LOWORD(lParam), \ (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
Макрокоманда HANDLE_WM_CREATE вызывает функцию обарботки сообщения WM_CREATE, адрес которой передается ей через последний параметр. Прототип этой функции вы сможете найти все в том же файле windowsx.h рядом с определением макрокоманды HANDLE_WM_CREATE:
BOOL Cls_OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct)
Аналогичные прототипы определены и для других функций обработки сообщений.
Функции обработки сообщений
Функции обработки сообщений обычно выполняют всю работу, для которой создается приложение. Приложение Window Application практически ничего не делает, поэтому и функции обработки сообщений несложны:
BOOL WndProc_OnCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct) { return TRUE; }
void WndProc_OnDestroy(HWND hWnd) { PostQuitMessage(0); return FORWARD_WM_DESTROY(hWnd, DefWindowProc); }
void WndProc_OnLButtonDown(HWND hWnd, BOOL fDoubleClick, int x, int y, UINT keyFlags) { MessageBox(NULL, "Hello, 32-bit world!", "Window", MB_OK); return FORWARD_WM_LBUTTONDOWN(hWnd, fDoubleClick, x, y, keyFlags, DefWindowProc); }
Функция WndProc_OnCreate вызывается при создании окна и предназначена для инициализации данных, связанных с этим окном. В нашем случае она просто возвращает значение TRUE, сообщая тем самым, что инициализация завершилась успешно.
Функция WndProc_OnLButtonDown в ответ на щелчок мышью внутри окна выводит диалоговую панель с сообщением. Она завершается вызовом макрокоманды FORWARD_WM_LBUTTONDOWN, определенной в файле windowsx.h следующим образом:
#define FORWARD_WM_LBUTTONDOWN \ (hwnd, fDoubleClick, x, y, keyFlags, fn) \ (void)(fn)((hwnd), (fDoubleClick) ? WM_LBUTTONDBLCLK : \ WM_LBUTTONDOWN, (WPARAM)(UINT)(keyFlags), \ MAKELPARAM((x), (y)))
В качестве последнего параметра этой макрокоманде передается адрес функции, которая используется для обработки сообщений по умолчанию. В нашем случае это адрес функции DefWindowProc.
Макрокоманда FORWARD_WM_LBUTTONDOWN вызывает функцию DefWindowProc , передавая ей все необходимые параметры.
При уничтожении главного окна приложения вызывается функция WndProc_OnDestroy, которая помещает в очередь сообщение WM_QUIT. Это приводит к завершению цикла обработки сообщений, и, соответственно, к завершению работы приложения.