Вы находитесь на странице: 1из 97

Введение в MFC

Макаревич Л. Г.
Литература
 Нортон П., Макгрегор Р. WINDOWS 95/NT 4. Программирование с
помощью MFC. в 2-х книгах: Пер. с англ. "СК Пресс", 1998.
 Тосс В. Visual C++ 5. Энциклопедия пользователя: Пер. с англ.
К.:"Диасофт", 1998.-688 с. , ил.
 Грегори К. Использование Visual C++ 5. Специальное издание.: Пер. с
англ. - К.:Диалектика, 1997.-816 с. , ил.
 Тихомиров Ю. Visual C++6. - СПб.:БХВ - Санкт-Петербург, 1998. - 496
с., ил.
 Мешков А., Тихомиров Ю. Visual C++ и MFC. программирование для
Windows NT и Windows 95. В 3-х томах. Срб.:-BHV-Санкт-Петербург,
1997.
 Фролов А. В., Фролов Г. В. Microsoft Visual C++ и MFC.
Программирование для Windows 95 и Windows NT. - М.:ДИАЛОГ-
МИФИ, 1996. - 288 с. - (Библиотека системного программиста; Т. 24)
 Фролов А. В., Фролов Г. В. Microsoft Visual C++ и MFC.
Программирование для Windows 95 и Windows NT. - М.:ДИАЛОГ-
МИФИ, 1997. - 272 с. - (Библиотека системного программиста; Т. 28)
 Круглински Д., Уингоу С., Шеферд Дж. Программирование на Microsoft
Visual C++ 6.0 для профессионалов. Пер. с англ. –
СПб:Питер;М:Русская редакция, 2001. – 864 с.: ил.
Информация в Интернет
 ftp://ermak.cs.nstu.ru/metodic/makar/MFC
 www.codenet.ru
 www.codeguru.com
 www.firststeps.ru
 www.realcoding.net
 www.codegu.ru
 www.rsdn.ru
 Технология программирования 2004.doc
 Секунов Н. Самоучитель Visual С++
Программирование для Windows
 Windows 3.0 – 1990
 Независимость программ от аппаратуры
 Стандартный графический интерфейс
 Совместимость с предыдущими версиями
 Многозадачность
 Встраиваемые объекты ( Clipboard, OLE, COM, ActiveX)
 Windows 95
 Вытесняющая многозадачность
 Потоки ( Thread )
 32-разрядная поддержка. Улучшенное управление виртуальной памятью
 Windows NT
 Способность выполнять приложения, написанные для других платформ (MS DOS, POSIX,
WINDOWS 3.1, OS/2…)
 Переносимость на другие платформы
 Многопроцессорность
 Улучшенная защищенность
 Организация распределенности по правилам «клиент-сервер»
 Улучшенная защищенность
 DCOM – технологии
 Новая файловая система
 Windows XP
 Улучшенная защищенность
 Управление рабочим столом
 64-разрядная поддержка
 Многоязычная поддержка
 Удаленное управление рабочим столом
Пакеты разработки программ
 Windows SDK (Software Development Kit)
 Набор книг с описанием функций, макросов, сообщений
 Редакторы ресурсов
 Оперативная справка
 Библиотеки
 Примеры программ
 Windows API (Application Program Interface)
 Более 2000 функций, макросов, констант
 user32.dll – управление окнами, сообщениями, курсорами,
таймерами
 gdi32.dll – интерфейс графических устройств, рисование
элементов пользовательского инерфейса
 kernel32.dll – управление памятью, распределение ресурсов
 Дополнительные модули – стандартные диалоги,
дополнительные элементы управления, …
Ресурсы приложения
 Шаблоны диалогов
 Шаблоны панелей инструментов
 Курсоры
 Иконки
 Шаблоны меню
 Строковые таблицы
 Картинки ( bitmap )
 Акселераторы
Доступ к ресурсам осуществляется через численные идентификаторы или
строковое имя ресурса
Событийное программирование

Инициализация

Цикл сообщений
Получение сообщения из очереди

WM_QUIT?
Есть обработчик?

Окончание
Стандартная обработка Вызов обработчика
Цикл обработки сообщений
while ( GetMessage(&msg, 0, 0, 0))
{
TranslateMessage(&msg);// трансляция ввода с клавиатуры в WM_CHAR
DispatchMessage(&msg);// в оконную процедуру
}
GetMessage Function
GetMessage получает сообщения из очереди сообщений для потока. Функция
отфильтровывает сообщения для обработки.
BOOL GetMessage(      
    LPMSG lpMsg,    HWND hWnd,    UINT wMsgFilterMin,    UINT wMsgFilterMax);
Параметры
lpMsg
[out] Указатель на структуру MSG с информацией о сообщении из очереди.
hWnd
[in] От какого окна обрабатываются сообщения. Окно принадлежит данному потоку. 0 – от
любых окон данного потока.
wMsgFilterMin
[in] Задает фильтр сообщений. WM_KEYFIRST – от клавиатуры, WM_MOUSEFIRST – от
мыши.
Windows XP: WM_INPUT- только сообщения WM_INPUT.
Если wMsgFilterMin и wMsgFilterMax - 0, GetMessage возвращает все сообщения, то есть
нет фильтрации.
wMsgFilterMax
[in] Задает фильтр сообщений. WM_KEYLAST – от клавиатуры, WM_MOUSELAST - от мыши.
Return Value
Если сообщение – не WM_QUIT, то возвращается не ноль.
Если сообщение - WM_QUIT, возвращается ноль.
Если есть ошибка, то возвращается -1. Например, окно неверное или lpMsg неверный
указатель. Для расшифровки ошибки вызывается функция GetLastError.

BOOL PeekMessage( LPMSG lpMsg,     HWND hWnd,     UINT wMsgFilterMin,


    UINT wMsgFilterMax,     UINT wRemoveMsg );
Что такое сообщение
MSG Structure
Структура MSG содержит информацию о сообщении.

typedef struct {
HWND hwnd;
UINT message;
WPARAM wParam
LPARAM lParam;
DWORD time;
POINT pt;} MSG, *PMSG;

hwnd
Окно, от которого пришло сообщение.
message
Идентификатор сообщения.
wParam
Дополнительная информация, зависит от типа сообщения.
lParam
Дополнительная информация, зависит от типа сообщения.
time
Время сообщения.
pt
Координаты курсора для сообщения.
Минимальное приложение для
Windows /*
первая функция приложения - инициализирует приложение и
выполняет цикл обработки сообщений
/* имя приложения */ */
#define APPNAME "MINAPIWIN" int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
#include <windows.h> /* описание функций Win32® API */ int nCmdShow)
#include "resource.h" /* определение идентификаторов */ {
MSG msg;
/* глобальные переменные */ HANDLE hAccelTable;
HINSTANCE hInst; /* идентификатор приложения */
if (!hPrevInstance)
HWND hwndEdit; /* идентификатор окна */ {
char szAppName[] = APPNAME; /* имя приложения */ /* инициализируем данные окна и регистрируем его */
/* заголовок главного окна */ if (!InitApplication(hInstance))
char szTitle[] = APPNAME": Минимальное приложение-пример"; return (FALSE);
}
CHAR szFileName[_MAX_PATH]; /* инициализируем приложение */
CHAR szFileTitle[_MAX_PATH]; if (!InitInstance(hInstance, nCmdShow))
return (FALSE);
/* определение используемых функций */
ATOM APIWinRegisterClass(CONST WNDCLASS *); hAccelTable = LoadAccelerators (hInstance, szAppName);
BOOL InitApplication(HINSTANCE); /* Цикл обработки сообщений */
BOOL InitInstance(HINSTANCE, int); while (GetMessage(&msg, NULL, 0, 0))
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
{
if (!TranslateAccelerator (msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}

return (msg.wParam);
}
/* функция инициализации данных окна и его регистрируции*/ /* функция создания главного окна*/
BOOL InitApplication(HINSTANCE hInstance) BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{ {
WNDCLASS wc; HWND hWnd;
HWND hwnd; hInst = hInstance;
/* в Win32 API параметр hPrevInstance всегда равен NULL, hWnd = CreateWindow(szAppName, szTitle,
поэтому для нахождения ранее запущенного приложения WS_OVERLAPPEDWINDOW,
используем функция поиска окна (если не хотим CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
повторного запуска приложения */ NULL, NULL, hInstance, NULL);
hwnd = FindWindow (szAppName, NULL); if (!hWnd)
if (hwnd) return (FALSE);
{ ShowWindow(hWnd, nCmdShow);
/* нашли копию приложения - восстановим его на экране и UpdateWindow(hWnd);
активизируем его */ return (TRUE);
if (IsIconic(hwnd)) }
ShowWindow(hwnd, SW_RESTORE); /* функция главного окна
SetForegroundWindow (hwnd); обрабатывает следующие сообщения:
/* данная копия должна быть завершена */ WM_DESTROY - команда на завершение приложения */
return (FALSE); LRESULT CALLBACK WndProc(
} HWND hWnd,
wc.style = CS_HREDRAW | CS_VREDRAW; UINT message,
wc.lpfnWndProc = (WNDPROC)WndProc; WPARAM wParam,
wc.cbClsExtra = 0; LPARAM lParam)
wc.cbWndExtra = 0; {
wc.hInstance = hInstance; switch (message)
wc.hIcon = LoadIcon (hInstance, szAppName); {
wc.hCursor = LoadCursor(NULL, IDC_ARROW); case WM_DESTROY:
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); PostQuitMessage(0);
wc.lpszMenuName = szAppName; break;
wc.lpszClassName = szAppName; default:
return (DefWindowProc(hWnd, message, wParam, lParam));
return RegisterClass(&wc); }
} return (0);
}
Простейшее приложение для Windows

#include "windows.h"
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
MessageBox(0,"Привет, Мир!","Первая программа для Windows", MB_OK|MB_ICONEXCLAMATION);
MessageBeep(MB_ICONEXCLAMATION);
}
Приложение с диалогом
 Создали ресурс
 Добавили код
#include "windows.h"
#include "resource.h"
BOOL CALLBACK DlgProc(HWND hwnddlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_COMMAND:
{
WPARAM wId = wParam;
switch(wId)
{
case IDOK:
case IDCANCEL:
EndDialog(hwnddlg,0);
return TRUE;
}
}
}
return FALSE;
}
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
MessageBox(0,"Привет, Мир!","Первая программа для Windows",MB_OK|MB_ICONEXCLAMATION);
MessageBeep(MB_MB_ICONEXCLAMATION);
DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG1),0,(DLGPROC)DlgProc);
}
Модальные и немодальные диалоги
#include "windows.h"
#include "resource.h"
BOOL CALLBACK DlgProc(HWND hwnddlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_COMMAND:
{
WPARAM wId = wParam;
switch(wId)
{
case IDOK:
case IDCANCEL:
EndDialog(hwnddlg,0);
return TRUE;
}
}
}
return FALSE;
}
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
MessageBox(0,"Привет, Мир!","Первая программа для Windows",MB_OK|MB_ICONEXCLAMATION);
MessageBeep(MB_ICONEXCLAMATION);
DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG1),0,(DLGPROC)DlgProc);
HWND hhh = CreateDialog(hInstance,MAKEINTRESOURCE(IDD_DIALOG1),0,(DLGPROC)DlgProc);
ShowWindow(hhh, SW_SHOW);
hhh = CreateDialog(hInstance,MAKEINTRESOURCE(IDD_DIALOG1),0,(DLGPROC)DlgProc);
ShowWindow(hhh, SW_SHOW);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0 ))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
Графический интерфейс
пользователя
 Раньше программы писали
непосредственно в видеопамять или
принтер
 Windows добавляет прмежуточный слой –
GDI (Graphics Device Interface)
 Программа использует функции GDI со
ссылками на device context, а Windows
отображает все на физические устройства
через свои драйверы с разделением
дисплея между разными программами
Ресурсно-базируемое
программирование
 При программировании под Windows часть
информации сохраняется в специальных
форматах в ресурсных файлах.
 Эти файлы добавляются в exe-файл при
компоновке
 Ресурсные файлы включают иконки,
битмапы, меню, панели инструментов,
курсоры, диалоги и т. д.
 Для создания подобных элементов
используются редакторы ресурсов ( обычно
wysiwyg )
DLL – динамически-загружаемые
библиотеки
 Эти библиотеки используются всеми
приложениями, что экономит память и
дисковое пространство
 Можно использовать статичести
прикомпонованные библиотеки
 Можно создавать свои DLL
Что такое MFC
 K MFC следует подходить как к инструменту, который,
принимая на себя большую часть черновой работы,
требует от нас не просто знакомства с ним, но и
глубокого изучения. Только знание всех нюансов
построения и возможностей этой библиотеки позволит
нам быстро и легко создавать программы любой
степени сложности. В противном случае никогда не
будет уверенности, что созданное приложение при
любых условиях работает правильно — слишком много
скрыто от программиста. Функции вызываются не
только из недр Windows, но и из недр библиотеки. Ни о
каком последовательном вызове функций речь не идет.
Данные готовятся неизвестно где и неизвестно кем и
когда обрабатываются. Так что какая уж тут уверенность.
MFC - это набор классов, охватывающих большую часть
функциональных возможностей операционных систем Microsoft
Windows, а также предоставляющих разработчику значительное
количество не только очень мощных дополнительных классов, но
и целые механизмы, которые, не нарушая идеологию
операционной системы, существенно ее расширяют и ... упрощают.
 Перед создателями библиотеки стояла задача создания
объектно-ориентированного интерфейса для Windows,
удовлетворяющего следующим основным целям
проектирования программных продуктов:
  сокращение усилий по программированию приложений для
Windows;
  скорость выполнения программ, написанных с
использованием библиотеки, должна быть сопоставима с
программами, написанными на языке С с использованием
Win32 API;
  минимальный размер вспомогательного кода; 
  способность напрямую вызывать любую С-функцию Win32
API; 
  легкость использования Win32 API в C++ должна быть
такая же, как и при использовании традиционного С.
Создание MFC-приложения
Visual Studio для С++
 Текстовый редактор
 Редакторы ресурсов
 Компилятор (cpp -> obj )
 Компилятор ресурсов (rc ->res)
 Компоновщик (obj + res -> exe)
 Отладчик
 AppWizard
 ClassWizard
 Просмотрщик кода
 Помощь
 Диагностика (Spy, PVIEW)
 Контроль кода (Visual SourceSafe )
 Галерея (ActiveX компоненты)
 MFC
 ATL (Microsoft Active Template Library)
Как программировать под
Windows
 Программировать на С с
использованием Win32
 Разработать свою библиотеку классов
на С++ с использованием Win32
 Использовать MFC
 Использовать другие библиотеки
классов ( OWL )
Что необходимо знать для
изучения MFC
 MFC - это достаточно сложная библиотека, интенсивно использующая
возможности языка С++, а также в некоторых случаях и расширяющая язык.
Поэтому Вы должны хорошо разбираться в языке С++, в частности, не
должны вызывать затруднений наследование и создание производных
классов, полиморфизм (нужно также понимать его ограничения в С++),
виртуальные методы, перегрузка операторов, обработка исключений и
другие характерные для С++ понятия.
 Если Вы не так свободно ориентируетесь в С++, то настоятельно
рекомендуется перед изучением MFC укрепить свои знания по С++,
например, с помощью книги Стенли Б. Липпмана "С++ для начинающих", или
одной из множества других книг, подобных этой.
 Необходимо иметь опыт работы с одной из современных версий Windows, а
также представлять себе ее внутреннее устройство и принципы работы.
 Необходим хотя бы минимальный опыт работы со средой Microsoft Visual C+
+. Предполагается, что Вы, по крайней мере, умеете создавать проекты,
добавлять к ним файлы, менять настройки и собирать проекты. Однако,
библиотека MFC построена так, что она повторяет структуру подсистем и
объектов Windows API. Это сделано ради эффективности. Поэтому, если Вы
уже программировали на С/С++ с использованием только "чистого" Windows
API, то это существенно облегчит процесс изучения MFC.
 Опыт работы с системами RAD тоже будет полезен.
Иерархия классов MFC
CObject – базовый класс
 Информация о типе времени выполнения
Если указатель или ссылка ссылается на объект, производный от
класса CObject, то в этом случае предусмотрен механизм
определения реального типа объекта с помощью макроса
RUNTIME_CLASS(). Хотя в С++ имеется механизм RTTI, механизм,
реализованный в MFC, намного более эффективен по
производительности.
 Диагностика
Каждый класс, производный от CObject, может по запросу проверить
свое внутреннее состояние и выдать диагностическую информацию.
Это интенсивно используется в MFC при отладке.
 Сериализация
Сериализация - это механизм, позволяющий преобразовать текущее
состояние объекта в последовательный поток байтов, который
обычно затем записывается на диск, и восстановить состояние
объекта из последовательного потока, обычно при чтении с диска.
Это позволяет сохранять текущее состояние приложения на диске,
и восстанавливать его при последующем запуске.
Основные классы
 Некоторые классы порождаются непосредственно от CObject. Наиболее
широко используемыми среди них являются CCmdTarget, CFile, CDC,
CGDIObject и CMenu.
 Класс ССmdTarget предназначен для обработки сообщений.
 Класс СFile предназначен для работы с файлами.
 Класс CDC обеспечивает поддержку контекстов устройств. В этот класс включены
практически все функции графики GDI. CGDIObject является базовым классом для
различных GDI-объектов, таких как перья, кисти, шрифты и другие.
 Класс СMenu предназначен для манипуляций с меню.
 От класса CCmdTarget порождается очень важный класс CWnd. Он является
базовым для создания всех типов окон, включая масштабируемые ("обычные") и
диалоговые, а также различные элементы управления.
 Наиболее широко используемым производным классом является CFrameWnd. Как
Вы увидите в дальнейшем, в большинстве программ главное окно создается с
помощью именно этого класса.
 От класса CCmdTarget, через класс CWinThread, порождается, наверное,
единственный из наиболее важных классов, обращение к которому в MFC-
программах происходит напрямую: CWinApp. Это один из фундаментальных
классов, поскольку предназначен для создания самого приложения. В
каждой программе имеется один и только один объект этого класса. Как
только он будет создан, приложение начнет выполняться.
Функции-члены в MFC

Большинство функций, вызываемых в MFC-


программе, являются членами одного из
классов, определенных в библиотеке.
Большинство функций API доступны через
функции-члены MFC. Тем не менее, всегда
можно обращаться к функциям API
напрямую. Иногда это бывает
необходимым, но все же в большинстве
случаев удобнее использовать функции-
члены MFC.
 Глобальные функции в MFC
В библиотеке есть ряд глобальных функций. Все они начинаются с префикса
Afx. (Когда MFC только разрабатывалась, то проект назывался AFX,
Application Framework. После ряда существенных изменений AFX была
переработана в MFC, но прежнее название сохранилось во многих
идентификаторах библиотеки и в названиях файлов). Например, очень часто
используется функция AfxMessageBox(), отображающая заранее
определенное окно сообщения. Но есть и член-функция MessageBox(). Таким
образом, часто глобальные функции перекрываются функциями-членами.
 Файл AFXWIN.H
Все MFC-программы включают заголовочный файл AFXWIN.H. В нем, а также в
различных вспомогательных файлах, содержатся описания классов,
структур, переменных и других объектов MFC. Он автоматически подключает
большинство заголовочных файлов, относящихся к MFC, в том числе и
WINDOWS.H, в котором определены все функции Windows API и другие
объекты, которые используются при традиционном программировании на С и
"чистом" API.
Каркас MFC-программы
simpwin.h simpwin.cpp
#include <afxwin.h> #include <afxwin.h>
#include <string.h>
// Класс основного окна приложения
#include "SIMPWIN.H"
class CMainWin: public CFrameWnd // Создание одного и только одного экземпляра
{ // приложения
public: CApp App;
CMainWin(); // Реализация
BOOL CApp::InitInstance()
// Декларирование карты сообщений
{
DECLARE_MESSAGE_MAP() // Создание главного окна приложения и его
}; // отображение.
// Класс приложения. Должен существовать только // Член CApp::m_pMainWnd - это указатель на объект
// один экземпляр этого класса. // главного окна.
// Член-функция InitInstance() вызывается при запуске m_pMainWnd = new CMainWin;
// приложения. m_pMainWnd->ShowWindow(SW_RESTORE);
class CApp: public CWinApp m_pMainWnd->UpdateWindow();
// Сигнализируем MFC об успешной инициализации
{ // приложения.
public: return TRUE;
BOOL InitInstance(); }
}; CMainWin::CMainWin()
{
// Создание окна с заголовком. Используется
// встроенный в MFC
// класс окна, поэтому первый параметр 0.
this->Create(0, "Простейшее приложение на MFC");
}
// Реализация карты сообщений
BEGIN_MESSAGE_MAP(CMainWin /*класс окна*/, CFrameWnd
/*класс-предок*/)
END_MESSAGE_MAP()
Класс CApp
 CWinApp – базовый класс
 virtual BOOL CWinApp::InitInstance();
Это виртуальная функция, которая вызывается каждый раз
при запуске программы. В ней должны производиться все
действия, связанные с инициализацией приложения. Функция
должна возвращать TRUE при успешном завершении и
FALSE в противном случае.
В нашем случае, в функции сначала создается объект класса
CMainWin, и указатель на него запоминается в переменной
m_pMainWnd. Эта переменная является членом класса
CWinThread. Она имеет тип CWnd* и используется почти во
всех MFC-программах, потому что содержит указатель на
главное окно.
Показать окно
 BOOL CWnd::ShowWindow(int How); Параметр определяет,
каким образом окно будет показано на экране. Наиболее
распространенные значения следующие:
Константа Действие
SW_HIDE Окно становится невидимым
SW_MAXIMIZE Окно максимизируется
SW_MINIMIZE Окно минимизируется
SW_SHOW Окно отображается, если было невидимо
SW_RESTORE Окно приводится к нормальному размеру

При первом вызове в качестве первого параметра функции можно подставить


член m_nCmdShow, в котором хранится значение кода отображения окна,
переданное в программу при запуске
В конце программы помещена реализация карты сообщений:
BEGIN_MESSAGE_MAP(CMainWin /*класс окна*/, CFrameWnd /*класс-предок*/)
END_MESSAGE_MAP()
Первый макрос всегда имеет два параметра, первый - класс окна, второй -
класс, от которого порожден класс окна. В данном примере карта сообщений
пустая, то есть все сообщения обрабатывает MFC.
Создание окна в программе
В примере была использована функция Create(),
которая на самом деле имеет много параметров.
BOOL CFrameWnd::Create(
LPCSTR ClassName, // Имя Windows-класса Константа Элемент окна
окна
LPCSTR Title, // Заголовок
DWORD Style = WS_OVERLAPPEDWINDOW, // Стиль WS_OVERLAPPED Стандартное окно с рамкой
const RECT &XYSize = rectDefault, // Область
WS_MAXIMIZEBOX Кнопка максимизации
CWnd *Parent = 0, //Окно-родитель
LPCSTR MenuName = 0, //Имя ресурса меню WS_MINIMIZEBOX Кнопка минимизации
DWORD ExStyle = 0, //Расширенные стили
CCreateContext *Context = 0 // Доп. данные WS_SYSMENU Системное меню
);
WS_HSCROLL Горизонтальная полоса прокрутки

WS_VSCROLL Вертикальная полоса прокрутки

Первый параметр, ClassName, определяет имя класса окна для оконной подсистемы Windows.
Обычно его не нужно явно задавать, так как MFC выполняет всю необходимую черновую
работу.
Параметр Style задает стиль окна. По умолчанию создается стандартное перекрываемое окно.
Можно задать свой стиль, объединив с помощью операции "или" несколько констант

В дальнейшем мы не будем так подробно расписывать здесь все возможные значения констант,
потому что всегда можно обратиться к справке Visual C++ и получить подробную информацию.
Начальное положение и размер окна определяются параметром XYSize. Это структура, которая
определяет экранные координаты левого верхнего и правого нижнего угла прямоугольника, в котором
будет отображено окно.
Основные типы данных (как в API)
BOOL          Булевское значение Функция обратного вызова или CallBack-
BSTR          32-битный указатель на символ функция вызывается операционной
системой по указателю, переданному ей
BYTE          8-битное целое без знака
ранее приложением. Это весьма удобный
COLORREF  32-битное значение, используемое для задания цвета и часто используемый механизм
DWORD         32-битное целое без знака или адрес подключения к стандартным функциям
LONG          32-битное целое со знаком
специфических обработчиков.

LPARAM        32-битное значение, посылаемое в качестве параметра в оконную процедуру или функцию обратного вызова
LPCSTR        32-битный указатель на константную строку символов 
LPSTR  32-битный указатель на строку символов
LPCTSTR       32-битный указатель на константную строку символов, которая переносима в Unicode и DBCS
LPTSTR        32-битный указатель на строку символов, которая переносима в Unicode и DBCS
LPVOID        32-битный указатель на неопределенный тип данных
LRESULT       32-битное значение, возвращаемое из оконной процедуры или функции обратного вызова
UINT          32-битное целое без знака для Win32 и 16-битное — для Windows 3.x
WNDPROC    32-битный указатель на оконную процедуру
WORD          16-битное целое без знака
WPARAM  Значение, посылаемое в качестве параметра в оконную процедуру или функцию обратного вызова; 32-битное
для Win32 и 16-битное для Windows 3.x
POSITION      32-битное целое без знака, используемое для обозначения позиции элемента в коллекции
LPCRECT       32-битный указатель на немодифицируемую структуру RECT
HINSTANCE     32-битное целое без знака для идентификации дескриптора экземпляра приложения
Получение информации о
приложении
 Когда вы пишете приложение с использованием классов MFC, вы создаете единственный объект, производный от CWinApp. Для получения из
любого места программы информации об этом объекте и связанных с ним параметрах разработчики предоставили следующие функции.
 CWinApp* AfxGetApp()
Возвращает указатель на единственный объект CWinApp приложения.
 LPCNSRT AfxGetAppName()
Возвращает строку, заканчивающуюся нулем, содержащую имя приложения.
 HINSTANCE AfxGetlnstanceHandle()
Возвращает дескриптор текущего приложения для исполняемого файла (.ЕХЕ); в случае, если функция вызвана из динамически подключаемой
библиотеки, связанной с USRDLL-версией MFC, то возвращается HINSTANCE этой библиотеки DLL.
 CWnd* AfxGetMainWhd()
Возвращает значение переменной rn_pMainWnd объекта приложения, если оно не является сервером OLE, или указатель на активное главное окно
приложения — в противном случае.
 HINSTANCE AfxGetResourceHandle()
Возвращает дескриптор загруженных по умолчанию ресурсов приложения. 
 void AfxSetResourceHandle(HINSTANCE hlnstResource)
Устанавливает дескриптор экземпляра приложения или модуля .ЕХЕ или .DLL, из которого будут загружены ресурсы приложения.
 LPCTSTR AFXAPI AfxRegisterWndClass( UINT nClassStyle,  HCURSOR hCursor =0, HBRUSH hbrBackground = 0, HICON hlcon = 0)
Возвращает строку, заканчивающуюся нулем, которая содержит имя зарегистрированного класса. В качестве параметров в эту функцию
передаются: nClassStyle — стиль или комбинация стилей класса окна Windows, которые будут рассмотрены при описании регистрации класса
окна, hCursor— дескриптор ресурса курсора, hbrBackground— дескриптор ресурса кисти, определяющей цвет фона, hlcon — дескриптор
ресурса-пиктограммы.
 BOOL AFXAPI AfxRegisterClass(WNDCLASS* IpWndClass)
Возвращает значение TRUE при успешной регистрации класса окна и FALSE при ошибке. В качестве параметра в функцию передается указатель на
структуру WNDCLASS, которая должна содержать информацию, необходимую для успешной регистрации. При использовании этой функции
для регистрации класса окна в модуле DLL следует иметь в виду, что имя зарегистрированного класса автоматически удаляется из системы
при выгрузке библиотеки из памяти.
 HINSTANCE AFXAPI AfxLoadLibrary(LPCTSTR IpszModuleName)
Загружает в память модуль (.DLL или .ЕХЕ файл), имя которого передается в качестве параметра IpszModuleName. Если загрузка прошла успешно,
то возвращается дескриптор модуля и увеличивается счетчик ссылок для этой библиотеки; в противном случае возвращается NULL.
 BOOL AFXAPI AfxFreeLibrary(HINSTANCE hlnstLib)
Уменьшает счетчик ссылок для загруженного модуля DLL, определяемого параметром hlnstLib. Когда счетчик ссылок становится равным нулю,
модуль выгружается из адресного пространства текущего процесса.
int AfxMessageBox( 
LPCTSTR IpszText,  При использовании в блоке сообщений более одной кнопки можно
UINT nType = MB_OK,  также установить кнопку по умолчанию:
UINT nIDHelp =0)  MB_DEFBUTTONl — по умолчанию первая кнопка 
 И  MB_DEFBUTTON2 — по умолчанию вторая кнопка 
int AfxMessageBox (   MB_DEFBUTTON3 — по умолчанию третья кнопка
UINT nIDPrompt, 
UINT nType = MB_OK, 
UINT nIDHelp = (UINT) -1)
Выводит текст в окно сообщений. Первая форма этой функции выводит в окно сообщений строку IpszText и использует
идентификатор nIDHelp для перехода к соответствующей теме справки при нажатии клавиши. Вторая форма функции
использует для определения текста идентификатор строки ресурса, задаваемый параметром nIDPrompt. Если в качестве
nIDHelp используется значение по умолчанию, равное -1, то nIDPrompt задает тему справки.
 Обе функции возвращают одно из следующих значений:
IDABORT — нажата кнопка Abort
IDCANCEL — нажата кнопка Cancel или нажата клавиша
IDIGNORE — нажата кнопка Ignore
IDNO — нажата кнопка No
IDOK — нажата кнопка ОК
IDRETRY — нажата кнопка Retry
IDYES — нажата кнопка Yes 
О — окно сообщений не может быть создано.
Параметр пТуре определяет тип окна сообщений и может принимать одно из следующих значений:
 MB_ABORTRETRYIGNORE — содержит три кнопки Abort, Retry и Ignore (Стоп, Повтор и Пропустить)
 MBOK — содержит только кнопку ОК
 MB_OKCANCEL — содержит две кнопки ОК и Cancel (OK и Отмена)
 MB_RETRYCANCEL — содержит две кнопки Retry и Cancel (Повтор и Отмена)
 MB_YESNO — содержит две кнопки Yes и No (Да и Нет)
 MB_YESNOCANCEL — содержит три кнопки Yes, No и Cancel (Да, Нет и Отмена)
Кроме того, можно использовать следующие константы для определения степени модальности окна сообщений:
MB_APPLMODAL — модальность на уровне пользователя  
MB_SYSTEMMODAL — модальность на уровне системы
MB_TASKMODAL — модальность на уровне процесса
MB_ICONEXCLAMATION, MB_ICONSTOP, MB_ICONERROR…
Создание проекта на MFC
 Простое приложение с главным окном и
без поддержки архитектуры «Вид-
Документ»
Соглашения об именах
библиотеки MFC
 В качестве префикса, обозначающего имя класса, библиотека MFC
использует заглавную букву С от слова "class" (класс), за которым идет имя,
характеризующее его назначение. Например, CWinApp — класс,
определяющий приложение, CWnd — базовый класс всех оконных объектов,
CDialog — класс окон диалога и т. д. Мы также будем придерживаться этого
соглашения в приводимых примерах.
 Для имен методов классов используется три способа.
 При первом способе имя объединяет глагол и существительное, например,
Loadlcon (Загрузить пиктограмму) или DrawText (Нарисовать текст).
 При втором имя метода состоит только из существительного, например, DialogBox
(Блок диалога).
 Для функций, предназначенных для преобразования одного типа в другой,
обычными являются такие имена, как XtoY(из X в Y).
 Для членов классов библиотеки MFC принят следующий способ назначения
имен: обязательный префикс m_ (от class member — член класса), затем
идет префикс, характеризующий тип данных, и завершается все
содержательным именем переменной, например, m_pMainWnd, где р —
префикс, описывающий указатель (для переменных не членов класса
префикс m_ не используется, чтобы можно было отличить обычные
переменные от элементов любых классов, а не только классов библиотеки
MFC).
Включаемые файлы
 #include “stdafx.h”
 Этот включаемый файл служит для подключения к
программе необходимых ей библиотечных файлов.
Когда вы будете писать большие приложения, вам
обязательно потребуется использование заранее
откомпилированных заголовочных файлов, что
поможет существенно сократить требуемое время
компиляции. Прекомпилированные заголовки
удобны, если у вас есть несколько заголовков,
которые должны быть включены во все или в
большинство ваших исходных файлов. Однако их
использование полезно только в том случае, если
сами они не меняются между компиляциями.
Обработка сообщений
 Windows взаимодействует с приложением,
посылая ему сообщения. Поэтому обработка
сообщений является ядром всех приложений. В
традиционных приложениях Windows (написанных
с использованием только API) каждое сообщение
передается в качестве аргументов оконной
функции. Там обычно с помощью большого
оператора switch определяется тип сообщения,
извлекается информация, и производятся нужные
действия. Это громоздкая и чреватая ошибками
процедура. С помощью MFC все делается намного
проще.
Обработка сообщений в MFC
 В MFC включен набор предопределенных функций-
обработчиков сообщений, которые можно использовать в
программе. Если программа содержит такую функцию, то она
будет вызываться всякий раз, когда поступает связанное с
ней сообщение. При наличии дополнительной информации в
сообщении она передается в качестве аргументов функции.
Для организации обработки сообщений нужно выполнить
следующие действия:
 В карту сообщений программы должна быть включена команда
соответствующего сообщения.
 Прототип функции-обработчика должен быть включен в
описание класса, ответственного за обработку данного
сообщения.
 В программу должна быть включена реализация функции-
обработчика.
Включение макрокоманд в карту
сообщений
 Чтобы программа могла ответить на сообщение, в карту
сообщений должна быть включена соответствующая
макрокоманда. Названия макрокоманд соответствуют
именам стандартных сообщений Windows, но дополнительно
имеют префикс ON_ и заканчиваются парой круглых скобок.
Из этого правила есть исключение: сообщению
WM_COMMAND соответствует макрокоманда
ON_COMMAND(). Причина в том, что это сообщение
обрабатывается особым образом.
 Чтобы включить макрокоманду в очередь сообщений, нужно
просто поместить ее между командами
BEGIN_MESSAGE_MAP() и END_MESSAGE_MAP().
Например, если необходимо обработать в программе
сообщение WM_CHAR, то очередь должна выглядеть так:
BEGIN_MESSAGE_MAP(CMainWin, CFrameWnd) ON_WM_CHAR()
END_MESSAGE_MAP()
В очереди может находиться более одной макрокоманды. Сообщение
WM_CHAR генерируется при нажатии алфавитно-цифровой клавиши
на клавиатуре.
Пример программы

 При нажатии на левую и правую


кнопки мыши выдавать текст
afx_msg void OnLButtonDown(UINT Flags, CPoint Loc);
afx_msg void OnRButtonDown(UINT Flags, CPoint Loc);

Первый параметр указывает на то, была ли при


генерации сообщения нажата какая-нибудь клавиша
или кнопка мыши. Второй параметр определяет
координаты курсора мыши в момент нажатия кнопки.
Класс CPoint порождается от структуры POINT,
определенной так:
typedef struct tagPOINT
{
LONG x;
LONG y;
} POINT;
Сообщение WM_PAINT
 Windows устроена таким образом, что за обновление содержимого
окна отвечает программа. Например, если часть окна была
перекрыта другим окном, а затем вновь открыта, или
минимизированное окно было восстановлено, то окну посылается
сообщение WM_PAINT. В ответ на него окно должно обновить свою
клиентскую область.
Прототип обработчика WM_PAINT следующий:
afx_msg void OnPaint();
Макрокоманда называется ON_WM_PAINT().
Рассмотрим простой обработчик, который выводит строку "Привет"
в клиентскую область по координатам x = 10, y = 20: afx_msg void
CMainWin::OnPaint()
{ CPaintDC paintDC(this);
paintDC.TextOut(10, 20, CString("Привет"));
}
В обработчике WM_PAINT нужно всегда пользоваться классом
CPaintDC, который представляет собой класс клиентской области,
но предназначенный для использования именно с этим
сообщением. Это обусловлено архитектурой самой Windows.
CString. Этот класс входит в MFC и является очень удобной заменой для строк, завершаемых нулем. Он имеет много
полезных функций - аналогов стандартных строковых функций, и поддерживает преобразования типов, в частности, из
char* в CString (поэтому в примере можно было обойтись без явного создания объекта этого класса). Вы можете легко
самостоятельно изучить этот класс и многие другие полезные классы общего назначения из состава MFC, пользуясь
справочной системой Visual C++.
Большинство реальных окон (за исключением диалогов, которые мы рассмотрим
позднее) должны обрабатывать сообщение WM_PAINT.
Более того, если Вы хотите написать корректную программу, то весь вывод в окно
должен осуществляться только в обработчике WM_PAINT, и никак иначе. Например,
крайне нежелательно получать контекст устройства в обработчике сообщения мыши и
пытаться там что-то выводить. Это будет работать в большинстве случаев, но далеко
не всегда.
Дело в том, что Windows может предоставить одновременно всем программам лишь
очень небольшое число контекстов устройств. Поэтому при запросе контекста не из
обработчика WM_PAINT создание класса контекста может потерпеть провал, если
другие приложения уже заняли все контексты. Тогда выводить информацию будет
вообще невозможно. Это может вылиться в непредсказуемое поведение программы.
В случае же получения контекста из обработчика WM_PAINT, обязательно с помощью
класса CPaintDC, Windows гарантирует наличие свободного контекста. На самом деле,
Windows не пошлет программе это сообщение до тех пор, пока в системе не будет
свободного контекста. К сожалению, эта проблема не так широко известна, и во многих
книгах сплошь и рядом правило вывода информации только по сообщению WM_PAINT
не соблюдается. Это вызвано тем, что описанная проблема не проявляется до тех пор,
пока в системе не будет запущено несколько задач, постоянно что-то рисующих (кроме
отображения видео). Но так одна из наших целей - научиться создавать надежные
программы, то мы будем придерживаться данной рекомендации.
Рекомендуется делать это и в дальнейшем. Нужно отметить, что все среды
визуального программирования действуют согласно этой рекомендации.
Генерация сообщения
WM_PAINT
 Так как мы решили весь вывод в окно организовать через
сообщение WM_PAINT, то необходим способ
принудительной отправки этого сообщения, если необходимо
что-то вывести в окно. Это осуществляется с помощью
функции со следующим прототипом:
void CWnd::InvalidateRect(LPRECT pRegion, BOOL
EraseBackground = TRUE);
Параметр pRegion задает область, которую нужно
перерисовать. В большинстве программ эта область вообще
не анализируется, поэтому этот параметр почти всегда равен
0. Параметр EraseBackground определяет, нужно ли перед
отправкой сообщения закрасить окно фоном (по умолчанию
белым). Мы будем почти всегда использовать параметр по
умолчанию, так как это очень удобно для учебных примеров.
void CChildView::OnPaint()
{
CPaintDC dc(this); // device context for painting
dc.TextOut(nOldMouseX, nOldMouseY, " ");
dc.TextOut(nMouseX, nMouseY, pszMouseStr);
dc.TextOut(1, 1, " ");
dc.TextOut(1, 1, str);
// Do not call CWnd::OnPaint() for painting messages class CChildView : public CWnd
} {
void CChildView::OnLButtonDown(UINT nFlags, CPoint loc)
char str[50];
{ int nMouseX, nMouseY,
nOldMouseX = nMouseX; nOldMouseY = nMouseY; nOldMouseX, nOldMouseY;
strcpy(pszMouseStr, "Нажата левая кнопка"); char pszMouseStr[50];
nMouseX = loc.x; nMouseY = loc.y;
this->InvalidateRect(0); // Construction
}
public:
void CChildView::OnRButtonDown(UINT nFlags, CPoint loc) CChildView();
{
nOldMouseX = nMouseX; nOldMouseY = nMouseY;
strcpy(pszMouseStr, "Нажата правая кнопка");
nMouseX = loc.x; nMouseY = loc.y;
this->InvalidateRect(0);
} CChildView::CChildView()
void CChildView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) {
{
sprintf(str, "%c", nChar);
strcpy(str, "");
// Посылаем сообщение WM_PAINT strcpy(pszMouseStr, "");
// с необходимостью стереть и обновить все окно nMouseX = nMouseY =
this->InvalidateRect(0); nOldMouseX = nOldMouseY = 0;

} }
Запустив программу, обратите внимание, что она всегда
восстанавливает содержимое своего окна.
Так должны вести себя все корректные приложения Windows. Также
посмотрите в исходном тексте, как программа затирает в окне уже
выведенную раньше строку перед выводом новой строки.
Это делается с помощью вывода достаточно длинной строки из
пробелов; строка выводится с белым фоном, поэтому предыдущая
строка стирается.
По умолчанию вывод в окно осуществляется системным шрифтом,
ширина символов у которого различна. Поэтому, например, вывод
символа "i" сразу поверх "W" не сотрет предыдущий символ
Сообщение WM_TIMER
В Windows существуют специальные объекты, называемые таймерами. Программа (точнее, окно)
может запросить один или несколько таких объектов. После этого каждый таймер через регулярные
заранее указанные промежутки времени будет посылать сообщение WM_TIMER.
Они будут помещаться в очередь сообщений окна. Таким образом, в функции-обработчике этого
сообщения можно выполнять некоторые действия через регулярные промежутки времени. Если
создано несколько таймеров, то их можно различать по номерам, присвоенным им при запросе.
Для запроса таймера у системы используется следующая функция:
UINT CWnd::SetTimer(UINT Id, UINT Interval, void (CALLBACK EXPORT *TFunc)(HWND, UINT,
UINT, DWORD));
Третьим параметром пользуются очень редко, и обычно он равен 0. Мы не будем его использовать.
Параметр Id задает уникальный идентификационный номер таймера. По этим номерам обычно
различаются несколько созданных таймеров. Параметр Interval задает интервал между двумя
посылками сообщений (интервал таймера) в миллисекундах. Разрешающая способность таймеров
55 мс, поэтому интервалы измеряются с такой точностью. Если даже задать значение интервала
равным 1, то все равно будет использовано значение 55. Если задать 0, то таймер приостановит
свою работу.
Сообщения таймера обрабатываются функцией:
afx_msg void OnTimer(UINT Id);
Все таймеры вызывают один и тот же обработчик. Узнать, какой таймер послал сообщение, можно с
помощью параметра Id, в котором передается номер таймера.
Сообщение WM_DESTROY
Это сообщение посылается окну, когда последнее должно быть
удалено. Если его получает главное окно приложения, то это означает
завершение приложения.
В этом случае обычно приложение должно выполнить действия по
выгрузке.
Обработчик имеет прототип:
afx_msg void OnDestroy();
В нашем случае, мы должны удалить запрошенные таймеры. Все
запрошенные ресурсы перед завершением программы необходимо
освобождать.
Для таймеров это делается с помощью функции:
BOOL CWnd::KillTimer(int Id);
Функция освобождает таймер с идентификатором Id.
class CMainFrame: public CFrameWnd { BOOL CApp::InitInstance()
public: {
m_pMainWnd = new CMainFrame;
CMainFrame();
m_pMainWnd->ShowWindow(SW_RESTORE);
afx_msg void OnPaint(); m_pMainWnd->UpdateWindow();
// Обработчик сообщения WM_DESTROY
afx_msg void OnDestroy(); // Установка таймера с идентификатором 1 и
// Обработчик сообщения WM_TIMER // интервалом 500 мс
afx_msg void OnTimer(UINT ID);
m_pMainWnd->SetTimer(1, 500, 0);
char str[50];
return TRUE;
DECLARE_MESSAGE_MAP() }
};
CMainFrame::CMainFrame()
{
// Определение прямоугольника, в котором будет
// размещено окно
RECT rect;
rect.left = rect.top = 10;
rect.right = 200;
rect.bottom = 60;
// Создание окна в определенном экранном
// прямоугольнике
this->Create(0, "CLOCK", При создании окна мы явно задали координаты
WS_OVERLAPPEDWINDOW, rect); прямоугольника, в котором оно должно быть
strcpy(str, ""); отображено, с помощью структуры RECT. Таким
}
образом, всегда можно задать начальные размеры и
положение окна на экране.
afx_msg void CMainFrame::OnPaint()
{
CPaintDC dc(this);
// Выводим в окно строку текущего времени
dc.TextOut(1, 1, " ", 3);
dc.TextOut(1, 1, str);
}

afx_msg void CMainFrame::OnTimer(UINT ID)


{
// Предполагаем, что в программе один таймер, поэтому
// не проверяем ID.
// Получаем строку текущего времени Мы получаем текущее
CTime curtime = CTime::GetCurrentTime(); время с помощью
tm *newtime;
newtime = curtime.GetLocalTm(); класса CTime. Это еще
sprintf(str, asctime(newtime)); один полезный класс
str[strlen(str) - 1] = '\0'; общего назначения из
// Посылаем сообщение WM_PAINT -- его обработчик
MFC. Вы можете
// отобразит строку. использовать
this->InvalidateRect(0); приведенный выше код
} для получения
afx_msg void CMainFrame::OnDestroy() текущего локального
{ времени.
// При закрытии окна удаляем связанный с ним
// таймер.
KillTimer(1);
}
Добавление обработчиков
 Не используются виртуальные функции
( Почему? CWnd использует более 100
сообщений. А как быть с меню? )
 MFC использует макросы для работы с
сообщениями, причем отображение
макросов, вставка прототипов нужных
функций выполняется с помощью
ClassWizard по идентификатору сообщений
Архитектура «Документ-Вид»
 Отделение информации от способа отображения
 Рамка
 MDI-SDI приложения
 Сохранение и чтение данных – в документе
Глобальные функции для работы
с приложением

Порождая класс от CWinApp, переопределяете метод InitInstance.


Глобальные методы для получения некоторой информации о приложении:
AfxGetApp   Указатель на CWinApp объект.
CWinApp* AFXAPI AfxGetApp( );
AfxGetInstanceHandle   Идентификатор приложения в Windows.
TRACE("Executable filename = %s\n", AfxGetApp()->m_pszExeName);
HINSTANCE AFXAPI AfxGetInstanceHandle( );
TRACE("Application instance handle is 0x%0X\n", AfxGetInstanceHandle());
AfxGetResourceHandle   Идентификатор ресурсов пиложения.
HINSTANCE AfxGetResourceHandle( );
HMENU hMenu = ::LoadMenu(AfxGetResourceHandle(), MAKEINTRESOURCE(IDR_PANEL));
AfxGetAppName  Имя приложения.
LPCTSTR AFXAPI AfxGetAppName( );
TRACE("Application name is %s\n", AfxGetAppName());
Цикл обработки сообщений в
программе MFC
 CWinThread::Run()
BOOL CWinThread::InitInstance()
{
ASSERT_VALID(this);
return FALSE; // by default don't enter run loop
}
// main running routine until thread exits
int CWinThread::Run()
{
ASSERT_VALID(this);
_AFX_THREAD_STATE* pState = AfxGetThreadState();
// for tracking the idle time state
BOOL bIdle = TRUE;
LONG lIdleCount = 0;
// acquire and dispatch messages until a WM_QUIT message is received.
Выбор сообщения
for (;;)
{
из очереди
// phase1: check to see if we can do idle work
while (bIdle &&
!::PeekMessage(&(pState->m_msgCur), NULL, NULL, NULL, PM_NOREMOVE))
{
// call OnIdle while in bIdle state
if (!OnIdle(lIdleCount++)) //фоновая обработка
bIdle = FALSE; // assume "no idle" state
}
// phase2: pump messages while available
do
{
// pump message, but quit on WM_QUIT
if (!PumpMessage())
return ExitInstance();
Обработка
// reset "no idle" state after pumping "normal" message
сообщения из
//if (IsIdleMessage(&m_msgCur)) очереди
if (IsIdleMessage(&(pState->m_msgCur)))
{
bIdle = TRUE;
lIdleCount = 0;
}

} while (::PeekMessage(&(pState->m_msgCur), NULL, NULL, NULL, PM_NOREMOVE));


}
}
Обработка сообщений
Сообщения от
оборудования Приложение 1

Цикл
Очередь сообщений обработки
Системная
приложения 3 сообщений
Асинхронное
очередь сообщение

Очередь сообщений Оконная


приложения 2 процедура PostMessage

Очередь сообщений
приложения 1

Синхронное
сообщение
Стандартная
SendMessage оконная процедура
Категории сообщений

 Сообщения Windows
WM_..., кроме WM_COMMAND Обрабаты-
ваются в
окнах
 Извещения элементов управления CWnd

элементы управления направляют


извещения родительским окнам
 Командные сообщения
меню, кнопки панелей инструментов,
акселераторы Обрабаты-ваются в разных
объектах: приложении,
документе, виде…
 В MFC для обработки каждого
сообщения организуется свой
обработчик
 Взаимосвязь сообщения и
обработчика организуется за счет
использования карты сообщений
BEGIN_MESSAGE_MAP
END_MESSAGE_MAP
DECLARE_MESSAGE_MAP
class CDialogDlg : public CDialog
{
// Construction
public:
CDialogDlg(CWnd* pParent = NULL); // standard constructor

// Dialog Data
enum { IDD = IDD_DIALOG_DIALOG };

protected:
virtual void DoDataExchange(CDataExchange* pDX); //
DDX/DDV support

// Implementation
protected:
HICON m_hIcon;

// Generated message map functions


virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
public:
CString myEdit;
};
afxwin.h
#ifdef _AFXDLL
#define DECLARE_MESSAGE_MAP() \
private: \
static const AFX_MSGMAP_ENTRY _messageEntries[]; \
protected: \
static const AFX_MSGMAP messageMap; \
static const AFX_MSGMAP* PASCAL
GetThisMessageMap(); \
virtual const AFX_MSGMAP* GetMessageMap() const; \

#else
#define DECLARE_MESSAGE_MAP() \
private: \
static const AFX_MSGMAP_ENTRY _messageEntries[]; \
protected: \
static const AFX_MSGMAP messageMap; \
virtual const AFX_MSGMAP* GetMessageMap() const; \

#endif
BEGIN_MESSAGE_MAP(CDialogDlg, CDialog)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
В файле cpp
#ifdef _AFXDLL
#define BEGIN_MESSAGE_MAP(theClass, baseClass) \
const AFX_MSGMAP* PASCAL theClass::GetThisMessageMap() \
{ return &theClass::messageMap; } \
const AFX_MSGMAP* theClass::GetMessageMap() const \
{ return &theClass::messageMap; } \
AFX_COMDAT const AFX_MSGMAP theClass::messageMap = \
{ &baseClass::GetThisMessageMap, &theClass::_messageEntries[0] }; \
AFX_COMDAT const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = \
{\

#else
#define BEGIN_MESSAGE_MAP(theClass, baseClass) \
const AFX_MSGMAP* theClass::GetMessageMap() const \
{ return &theClass::messageMap; } \
AFX_COMDAT const AFX_MSGMAP theClass::messageMap = \
{ &baseClass::messageMap, &theClass::_messageEntries[0] }; \
AFX_COMDAT const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = \
{\

#endif

#define END_MESSAGE_MAP() \
{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \
}; \
AFX_MSGMAP_ENTRY
struct AFX_MSGMAP_ENTRY
{
UINT nMessage; // windows message
UINT nCode; // control code or WM_NOTIFY code
UINT nID; // control ID (or 0 for windows messages)
UINT nLastID; // used for entries specifying a range of
control id's
UINT_PTR nSig;
// signature type (action) or pointer to message #
AFX_PMSG pfn; // routine to call (or special value)
};
Компоненты карты сообщений

 Сообщения Windows
Макрос имеет вид для каждого
сообщения:
ON_WM_xxx
ON_WM_LBUTTONUP
 Для каждого макроса определен код в
файле afxmsg.h
#define ON_WM_LBUTTONUP() \
{ WM_LBUTTONUP, 0, 0, 0, AfxSig_vwp, \
(AFX_PMSG)(AFX_PMSGW) \
(static_cast< void (AFX_MSG_CALL CWnd::*)(UINT, CPoint) >
(OnLButtonUp)) },

AfxSig_vwp, // void (CWnd*, CPoint)


 Соответствующий обработчик должен
быть в классе – наследнике CWnd

afx_msg void OnLButtonUp(UINT nFlags, CPoint point);


Добавим обработчик к диалогу
class CDialogDlg : public CDialog
{
// Construction
public:
CDialogDlg(CWnd* pParent = NULL); // standard constructor

// Dialog Data
enum { IDD = IDD_DIALOG_DIALOG };

protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support

// Implementation
protected:
HICON m_hIcon;

// Generated message map functions


virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
public:
CString myEdit; h
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
};
BEGIN_MESSAGE_MAP(CDialogDlg, CDialog)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
//}}AFX_MSG_MAP
ON_WM_LBUTTONUP()
END_MESSAGE_MAP()

void CDialogDlg::OnLButtonUp(UINT nFlags, CPoint point)


{
// TODO: Add your message handler code here and/or call default
//AfxMessageBox(“Тестирование обработчика”);
CDialog::OnLButtonUp(nFlags, point);
}

сpp
Нотификационные сообщения
// for general controls
#define ON_CONTROL(wNotifyCode, id, memberFxn) \
{ WM_COMMAND, (WORD)wNotifyCode, (WORD)id,
(WORD)id, AfxSigCmd_v, \
(static_cast< AFX_PMSG > (memberFxn)) },

 afx_msg void CWnd::Fxn();


Имя функции можно менять
 Помимо общих макросов, есть макросы,
специфичные для контрольных элементов

AfxSigCmd_v, // void ()
class CDialogDlg : public CDialog
{
// Construction
public:
CDialogDlg(CWnd* pParent = NULL); // standard constructor

// Dialog Data
enum { IDD = IDD_DIALOG_DIALOG };

protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support

// Implementation
protected:
HICON m_hIcon;

// Generated message map functions


virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
public:
CString myEdit;
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
afx_msg void OnEnChangeEdit1();
};
BEGIN_MESSAGE_MAP(CDialogDlg, CDialog)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
//}}AFX_MSG_MAP
ON_WM_LBUTTONUP()
ON_EN_CHANGE(IDC_EDIT1, OnEnChangeEdit1)
END_MESSAGE_MAP()

void CDialogDlg::OnEnChangeEdit1()
{
// TODO: If this is a RICHEDIT control, the control will not
// send this notification unless you override the CDialog::OnInitDialog()
// function and call CRichEditCtrl().SetEventMask()
// with the ENM_CHANGE flag ORed into the mask.

// TODO: Add your control notification handler code here


}
Для сложных элементов
управления
#define ON_NOTIFY(wNotifyCode, id, memberFxn) \
{ WM_NOTIFY, (WORD)(int)wNotifyCode, (WORD)id,
(WORD)id, AfxSigNotify_v, \
(AFX_PMSG) \
(static_cast< void (AFX_MSG_CALL CCmdTarget::*)
(NMHDR*, LRESULT*) > \
(memberFxn)) },

AfxSigNotify_v, // void (NMHDR*, LRESULT*)


class CDialogDlg : public CDialog
{
// Construction
public:
CDialogDlg(CWnd* pParent = NULL); // standard constructor

// Dialog Data
enum { IDD = IDD_DIALOG_DIALOG };

protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support

// Implementation
protected:
HICON m_hIcon;

// Generated message map functions


virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
public:
CString myEdit;
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
afx_msg void OnEnChangeEdit1();
afx_msg void OnNMDblclkTree1(NMHDR *pNMHDR, LRESULT *pResult);
};
BEGIN_MESSAGE_MAP(CDialogDlg, CDialog)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
//}}AFX_MSG_MAP
ON_WM_LBUTTONUP()
ON_EN_CHANGE(IDC_EDIT1, OnEnChangeEdit1)
ON_NOTIFY(NM_DBLCLK, IDC_TREE1, OnNMDblclkTree1)
END_MESSAGE_MAP()

void CDialogDlg::OnNMDblclkTree1(NMHDR *pNMHDR, LRESULT


*pResult)
{
// TODO: Add your control notification handler code here
*pResult = 0;
}
typedef struct tagNMHDR
{
HWND hwndFrom; // handle of control sending message
//To convert this handle to a CWnd pointer, use CWnd::FromHandle.
UINT_PTR idFrom; ;// identifier of control sending message
UINT code; // NM_ code
} NMHDR;
NM_CLICK   The user has clicked the left mouse button within the
control.
NM_DBLCLK   The user has double-clicked the left mouse button within
the control.
NM_KILLFOCUS   The control has lost the input focus.
NM_OUTOFMEMORY   The control could not complete an operation
because there is not enough memory available.
NM_RCLICK   The user has clicked the right mouse button within the
control.
NM_RDBLCLK   The user has double-clicked the right mouse button
within the control.
NM_RETURN   The control has the input focus, and the user has
pressed
Этот заголовок the ENTER
– первая часть key.
более сложных структур ( рассмотрим при
NM_SETFOCUS   The controlуправления
анализе работы с панелями элементов has received) the input focus.
Командные сообщения
#define ON_COMMAND(id, memberFxn) \
{ WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id,
AfxSigCmd_v, \
static_cast<AFX_PMSG> (memberFxn) },
// ON_COMMAND(id, OnBar) is the same as
// ON_CONTROL(0, id, OnBar) or ON_BN_CLICKED(0, id, OnBar)

AfxSigCmd_v, // void ()
#define ON_UPDATE_COMMAND_UI(id, memberFxn) \
{ WM_COMMAND, CN_UPDATE_COMMAND_UI, (WORD)id,
(WORD)id, AfxSigCmdUI, \
(AFX_PMSG) \
(static_cast< void (AFX_MSG_CALL CCmdTarget::*)
(CCmdUI*) > \
(memberFxn)) },

AfxSigCmdUI, // void (CCmdUI*)


Добавлять обработчик можно в любой класс:
класс-вид,
класс-документ, Порядок
шаблон документа, обработки
класс-рамка(дочернее окно, окно приложения),
класс-приложение
Порядок обработки командных
сообщений
 В карте сообщений ищется
обработчик
 Если не найдется, то переход в карту
базового класса
 Если не найдется, то передача
следующему кандидату
 Стандартные сообщения передаются
непосредственно нужному объекту
class CmessApp : public CWinApp void CmessApp::On32771()
{ {
public: int a = 33;
CmessApp(); // TODO: Add your command
handler code here
}
// Overrides
public: void CmessApp::OnUpdate32771(CCmdUI
virtual BOOL InitInstance(); *pCmdUI)
{
// Implementation int a = 33;
afx_msg void OnAppAbout(); // TODO: Add your command
DECLARE_MESSAGE_MAP() update UI handler code here
afx_msg void On32771(); }
afx_msg void OnUpdate32771(CCmdUI *pCmdUI);
};

BEGIN_MESSAGE_MAP(CmessApp, CWinApp)
ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
// Standard file based document commands
ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
// Standard print setup command
ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)
ON_COMMAND(ID_32771, On32771)
ON_UPDATE_COMMAND_UI(ID_32771, OnUpdate32771)
END_MESSAGE_MAP()
Обработка команд в диапазоне
#define ON_COMMAND_RANGE(id, idLast, memberFxn) \
{ WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)idLast, AfxSigCmd_RANGE, \
(AFX_PMSG) \
(static_cast< void (AFX_MSG_CALL CCmdTarget::*)(UINT) > \
(memberFxn)) },
// ON_COMMAND_RANGE(id, idLast, OnBar) is the same as
// ON_CONTROL_RANGE(0, id, idLast, OnBar)

AfxSigCmd_RANGE, // void (UINT)

#define ON_UPDATE_COMMAND_UI_RANGE(id, idLast, memberFxn) \


{ WM_COMMAND, CN_UPDATE_COMMAND_UI, (WORD)id, (WORD)idLast,
AfxSigCmdUI_RANGE, \
(AFX_PMSG) \
(static_cast< void (AFX_MSG_CALL CCmdTarget::*)(CCmdUI*) > \
(memberFxn)) },

AfxSigCmdUI_RANGE, // void (CCmdUI*, UINT)


Контрольные элементы в
диапазоне
#define ON_CONTROL_RANGE(wNotifyCode, id, idLast, memberFxn) \
{ WM_COMMAND, (WORD)wNotifyCode, (WORD)id, (WORD)idLast,
AfxSigCmd_RANGE, \
(AFX_PMSG) \
(static_cast< void (AFX_MSG_CALL CCmdTarget::*)(UINT) > (memberFxn)) },

AfxSigCmd_RANGE, // void (UINT)


#define ON_NOTIFY_RANGE(wNotifyCode, id, idLast, memberFxn) \
{ WM_NOTIFY, (WORD)(int)wNotifyCode, (WORD)id, (WORD)idLast, AfxSigNotify_RANGE, \
(AFX_PMSG) \
(static_cast< void (AFX_MSG_CALL CCmdTarget::*)(UINT, NMHDR*, LRESULT*) > \
(memberFxn)) },

AfxSigNotify_RANGE void (UINT, NMHDR*, LRESULT*)


Работа с классом CCmdUI
CCmdUI не имеет базового класса.
CCmdUI используется только внутри обраблтчика ON_UPDATE_COMMAND_UI в
классе, производном от CCmdTarget.
Когда пользователь нажимает команду в меню, для каждой команды вызывается
обработчик ON_UPDATE_COMMAND_UI.
В нем вызываются методы CCmdUI, например, Enable или Check, и
соответственно отображаются пункты меню и кнопки в панелях управления.
User-Interface
Enable SetCheck SetRadio SetText
Item
Menu item Enables or Checks (×) or Checks using dot Sets item text
disables unchecks (•)
Toolbar button Enables or Selects, Same as (Not applicable)
disables unselects, or SetCheck
indeterminat
e
Status-bar pane Makes text Sets pop-out or Same as Sets pane text
visible or normal SetCheck
invisible border
Normal button in Enables or Checks or Same as Sets button text
CDialogBar disables unchecks SetCheck
check box
Normal control in Enables or (Not applicable) (Not applicable) Sets window text
CDialogBar disables
CCmdUI::Enable
virtual void Enable( BOOL bOn = TRUE );
Parameters
bOn
TRUE to enable the item, FALSE to disable it.
Example
BEGIN_MESSAGE_MAP(CFileDoc, CDocument)
// Many ON_* macros removed...
ON_UPDATE_COMMAND_UI(ID_FILE_SAVE, OnUpdateFileSave)
END_MESSAGE_MAP()
.
.
.
void CFileDoc::OnUpdateFileSave(CCmdUI* pCmdUI)
{
// Enable the menu item if the file has been modified.
pCmdUI->Enable(m_fModified);
}
Сообщения пользователя
// inside the class declaration
afx_msg LRESULT OnMyMessage(WPARAM wParam, LPARAM lParam);

#define WM_MYMESSAGE (WM_USER + 100)

BEGIN_MESSAGE_MAP(CMyWnd, CMyParentWndClass)
ON_MESSAGE(WM_MYMESSAGE, OnMyMessage)
END_MESSAGE_MAP()
LRESULT OnMyMessage(WPARAM wParam, LPARAM lParam)
{

CWnd* pWnd = ...;


pWnd->SendMessage(WM_MYMESSAGE);
pWnd->PostMessage(WM_MYMESSAGE);
::RegisterWindowMessage определяет уникальное для системы сообщение.
Макрос - ON_REGISTERED_MESSAGE.
class CMyWnd : public CMyParentWndClass
{
public: CMyWnd();
//{{AFX_MSG(CMyWnd)
afx_msg LRESULT OnFind(WPARAM wParam, LPARAM lParam);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
static UINT NEAR WM_FIND = RegisterWindowMessage("COMMDLG_FIND");
BEGIN_MESSAGE_MAP(CMyWnd, CMyParentWndClass)
//{{AFX_MSG_MAP(CMyWnd)
ON_REGISTERED_MESSAGE(WM_FIND, OnFind)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
Диапазон ьаких сообщений 0xC000 to 0xFFFF.
LRESULT SendMessage(  HWND hWnd,     UINT Msg,     WPARAM wParam,     LPARAM lParam );
Parameters
hWnd
[in] Handle to the window whose window procedure will receive the message.
If this parameter is HWND_BROADCAST, the message is sent to all top-level windows in the
system, including disabled or invisible unowned windows, overlapped windows, and pop-up
windows; but the message is not sent to child windows.
Msg
[in] Specifies the message to be sent.
wParam
[in] Specifies additional message-specific information.
lParam
[in] Specifies additional message-specific information.
Return Value
The return value specifies the result of the message processing; it depends on the message sent.
CWnd::SendMessage
Sends the specified message to this window.
LRESULT SendMessage( UINT message, WPARAM wParam = 0, LPARAM
lParam = 0 );
Parameters
message
Specifies the message to be sent.
wParam
Specifies additional message-dependent information.
lParam
Specifies additional message-dependent information.
Return Value
The result of the message processing; its value depends on the message sent.
Remarks
The SendMessage member function calls the window procedure directly and does
not return until that window procedure has processed the message. This is in
contrast to the PostMessage member function, which places the message into the
window's message queue and returns immediately.
The PostMessage function places (posts) a message in the message queue associated with the thread that
created the specified window and returns without waiting for the thread to process the message.
To post a message in the message queue associate with a thread, use the PostThreadMessage function.
Syntax
BOOL PostMessage(  HWND hWnd,     UINT Msg,     WPARAM wParam,     LPARAM lParam );
Parameters
hWnd
[in] Handle to the window whose window procedure is to receive the message. The following values have
special meanings.
HWND_BROADCAST
The message is posted to all top-level windows in the system, including disabled or invisible
unowned windows, overlapped windows, and pop-up windows. The message is not posted to child
windows.
NULL
The function behaves like a call to PostThreadMessage with the dwThreadId parameter set to the
identifier of the current thread.
Msg
[in] Specifies the message to be posted.
wParam
[in] Specifies additional message-specific information.
lParam
[in] Specifies additional message-specific information.
Return Value
If the function succeeds, the return value is nonzero.
If the function fails, the return value is zero.
CWnd::PostMessage
Places a message in the window's message queue and then returns without waiting for
the corresponding window to process the message.
BOOL PostMessage( UINT message, WPARAM wParam = 0, LPARAM lParam = 0 );
Parameters
message
Specifies the message to be posted.
wParam
Specifies additional message information. The content of this parameter depends
on the message being posted.
lParam
Specifies additional message information. The content of this parameter depends
on the message being posted.
Return Value
Nonzero if the message is posted; otherwise 0.
Remarks
Messages in a message queue are retrieved by calls to the GetMessage or
PeekMessage Windows function.
The Windows PostMessage function can be used to access another application.
SendNotifyMessage Function
The SendNotifyMessage function sends the specified message to a window or windows. If the window was created by the calling
thread, SendNotifyMessage calls the window procedure for the window and does not return until the window procedure has
processed the message. If the window was created by a different thread, SendNotifyMessage passes the message to the window
procedure and returns immediately; it does not wait for the window procedure to finish processing the message.
Syntax
BOOL SendNotifyMessage( HWND hWnd,     UINT Msg,     WPARAM wParam,     LPARAM lParam );
Parameters
hWnd
[in] Handle to the window whose window procedure will receive the message. If this parameter is HWND_BROADCAST, the
message is sent to all top-level windows in the system, including disabled or invisible unowned windows, overlapped
windows, and pop-up windows; but the message is not sent to child windows.
Msg
[in] Specifies the message to be sent.
wParam
[in] Specifies additional message-specific information.
lParam
[in] Specifies additional message-specific information.
Return Value
If the function succeeds, the return value is nonzero.
If the function fails, the return value is zero. To get extended error information, call GetLastError.

Remarks
If you send a message in the range below WM_USER to the asynchronous message functions (PostMessage,
SendNotifyMessage, and SendMessageCallback), its message parameters cannot include pointers. Otherwise, the operation will
fail. The functions will return before the receiving thread has had a chance to process the message and the sender will free the
memory before it is used.
Applications that need to communicate using HWND_BROADCAST should use the RegisterWindowMessage function to obtain a
unique message for inter-application communication.
The system only does marshalling for system messages (those in the range 0 to WM_USER). To send other messages (those
above WM_USER) to another process, you must do custom marshalling.
CWnd::SendNotifyMessage
Sends the specified message to the window.
BOOL SendNotifyMessage( UINT message, WPARAM wParam, LPARAM lParam );

Parameters

message
Specifies the message to be sent.
wParam
Specifies additional message-dependent information.
lParam
Specifies additional message-dependent information.
Return Value
Nonzero if the function is successful; otherwise 0.
Remarks
If the window was created by the calling thread, SendNotifyMessage calls the window
procedure for the window and does not return until the window procedure has
processed the message. If the window was created by a different thread,
SendNotifyMessage passes the message to the window procedure and returns
immediately; it does not wait for the window procedure to finish processing the
message.

Вам также может понравиться