Исходники.Ру - Программирование
Исходники
Статьи
Книги и учебники
Скрипты
Новости RSS
Магазин программиста

Главная » Статьи по программированию » Visual C++ - Все статьи »

Обсудить на форуме Обсудить на форуме

Программирование для окон

Вам еще не надоело создавать консольные приложения? Я так и знал: Ну что ж, тогда я не зря старался при написании данного материала, который обязан пролить свет на программирование в среде Windows, и научить вас создавать полноценные оконные приложения, в зависть вашему соседу, который еще этого не умеет.

Система, основанная на сообщениях

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

Как программа узнает о том, что ее окну послано сообщение? Ответ напрашивается сам. На протяжении всей работы программы необходимо проверять, имеются ли в очереди сообщения. Делается это, конечно-же, с помощью цикла, который выполняется в течении всей работы программы. При каждом проходе цикла проверяется наличие сообщений в очереди, и если таковые имеются, тогда программа поочередно получает их и обрабатывает.

Win32 API

Win32 API (Application Programming Interface - интерфейс прикладного программирования) - это набор функций, позволяющих программисту создавать приложения для Windows. Win32 API является основой для каждой Windows-программы.

Программисты, пишущие на С++ уже привыкли, что точкой входа в программу является функция main(). Но в системе Windows это не так. Все Win32-приложения используют в качестве точки входа в программу функцию WinMain. Ее объявление можно найти в заголовочном файле winbase.h:

int WINAPI WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nShowCmd
);

Давайте рассмотрим все ее аргументы:

  • hInstance - идентификатор экземпляра приложения.
  • hPrevInstance - идентификатор предыдущего экземпляра приложения. В Win32 он всегда равен нулю.
  • lpCmdLine - указатель на командную строку.
  • nShowCmd - флаги для окна.

Как видите - все не так сложно! Вас, наверное, удивили только какие-то непонятные типы данных. Не волнуйтесь - разберемся.

Типы данных в Windows

При первом взгляде на исходный код Windows-приложения, начинающих программистов начинают пугать "странные" типы данных, используемые в программе. Но это только на первый взгляд. На самом, деле разобраться в них вам не составит особого труда. Основные типы данных Win32 API приведены в таблице 1.

Таблица 1
BOOL, BOOLEAN Булев. Имеет только 2 значения: TRUE или FALSE
CHAR 8-битный символ (ANSI Windows)
WCHAR 16-битный символ (Unicode)
TCHAR CHAR или WCHAR (если используется Unicode)
USHORT, WORD Целое беззнаковое 16-битное число
DWORD, DWORD32, UINT32 Целое беззнаковое 32-битное число
DWORD64, UINT64, ULONGLONG Целое беззнаковое 64-битное число
FLOAT Число с плавающей точкой
SHORT Целое знаковое 16-битное число
INT, INT32, LONG, LONG32 Целое знаковое 32-битное число
INT64, LONG64, LONGLONG Целое знаковое 64-битное число
VOID Пустой тип

Структура Windows-программ

Каждая Windows-программа состоит как минимум из двух основных функций. Это WinMain и функция окна.

Давайте напишем простую программу, создающую пустое окно. Для этого в Visual C++ создайте пустой проект Win32 Application, добавьте новый файл (например, myprog.cpp), и вставьте туда следующий код:

// Включаем необходимый заголовочный файл для Windows-программ
#include <windows.h>

// Объявление функции окна (оконной процедуры)
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

HINSTANCE hInst; // Идентификатор приложения

// Указатель на константную строку символов - имя программы и класса окна
LPCSTR AppName = "MyProgramm";

// Точка входа в программу - функция WinMain
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
			LPSTR lpCmdLine, int nCmdShow)
{
HWND hWnd; // Уникальный идентификатор окна (handle)
MSG msg; // Объявление структуры типа MSG, для работы с сообщениями

hInst = hInstance; // Сохраняем идентификатор приложения

// Заполняем структуру WNDCLASS
WNDCLASS wc;
	// Инициализируем выделенную для структуры память нулями
	ZeroMemory(&wc, sizeof(wc));
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = (WNDPROC)WndProc;
wc.hInstance = hInst; 
wc.hIcon = LoadIcon(hInst, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.lpszClassName = AppName;

RegisterClass(&wc); // Создаем и регистрируем оконный класс

// Создаем окно программы
hWnd = CreateWindow(
	AppName, // Имя класса окна
	AppName, // Заголовок окна
	WS_OVERLAPPEDWINDOW, // Стиль окна
	CW_USEDEFAULT, 0, // Горизонтальная и вертикальная позиции окна
	300, 300, // Ширина и высота окна
	NULL, // Хендл родительского окна
	NULL, // Хендл меню
	hInst, // Идентификатор приложения
	NULL); // Дополнительные данные окна

ShowWindow(hWnd, SW_SHOW); // Отображаем окно
UpdateWindow(hWnd); // Перерисовываем окно

	// Стандартный цикл обработки сообщений
	while(GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

return msg.wParam;
}

// Оконная процедура
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch(msg)
	{
	case WM_DESTROY:
	PostQuitMessage(0);
	break;

	default:
	return DefWindowProc(hWnd, msg, wParam, lParam);
	}

return 0;
}

Теперь давайте рассмотрим приведенный код подробнее.

К сожалению, привести здесь описания всех типов данных, структур, функций и т.д., используемых в Win32 API, не представляется возможным, поэтому советую обратиться к официальному источнику, где можно найти всю необходимую информацию о программировании под Windows - Microsoft Developer Network (msdn.microsoft.com).

Для того чтобы создать окно, сначала необходимо зарегистрировать в системе новый оконный класс. Для этого нужно заполнить структуру WNDCLASS и передать указатель на область памяти, содержащую эту стуктуру, функции RegisterClass(). Данная функция создает и регистрирует новый оконный класс, используя значения элементов переданной ей структуры для определения характеристик класса. Ниже приведены описания всех элементов структуры WNDCLASS.

  • style - стиль окна данного класса.
  • lpfnWndProc - указатель на функцию окна, которая вызывается каждый раз при получении окном нового сообщения.
  • cbClsExtra и cbWndExtra - дополнительный размер резервируемой памяти (в байтах) для структур класса и окна.
  • hInstance - идентификатор приложения.
  • hIcon - идентификатор иконки, связанной с классом.
  • hCursor - идентификатор курсора, связанного с классом.
  • hbrBackground - идентификатор кисти, которой будет закрашен фон окна.
  • lpszMenuName - имя меню.
  • lpszClassName - имя создаваемого класса окна.

Как вы, наверное, заметили, в приведенном выше коде явно заполняются не все поля структуры, а только те, которые необходимы в для данного класса. Все остальные поля инициализируются нулевыми значениями с помощью функции ZeroMemory(), которая выполняет аналогичное ее имени действие.

После регистрации класса окна можно приступать к созданию самого окна. Для этого используется функция CreateWindow(), которая возвращает хендл создаваемого окна.

Создав окно, нам необходимо отобразить его на экране. Этим занимается функция ShowWindow(), которая принимает в качестве аргументов идентификатор окна, и флаг, указывающий на способ отображения окна (в данном случае SW_SHOW, определяющий, что окно необходимо показать на экране). Затем, с помощью функции UpdateWindow() мы посылаем нашему окну сообщение WM_PAINT, указывающее на необходимость перерисовать клиентскую область окна.

Далее следует цикл обработки сообщений. Он состоит из управляющей структуры while, которая при каждом проходе цикла получает очередное сообщение из очереди, посредством функции GetMessage(), затем переводит все сообщения от виртуальных клавиш в символьные сообщения с помощью функции TranslateMessage() (о предназначении данной операции мы поговорим позже), и после этого отсылает полученное сообщение на обработку оконной процедуре, используя функцию DispatchMessage().

Функция GetMessage() возвращает ненулевое значение, поэтому цикл не завершается до момента завершения программы. При завершении программы окну посылается сообщение WM_QUIT, которое является единственным сообщением, при получении которого функция GetMessage() возвращает ноль, и цикл обработки сообщений завершается, а код выхода из программы, хранящийся в элементе wParam структуры MSG, возвращается функцией WinMain.

И наконец, пора разобраться каким же образом оконная процедура обрабатывает переданные ей сообщения. У функции окна имеется четыре аргумента:

  • hWnd - идентификатор окна.
  • msg - код текущего сообщения.
  • wParam и lParam - дополнительная информация о сообщении.

В Windows существует более тысячи стандартных сообщений. Конечно же, программист не должен обрабатывать их все. Для этого существует функция DefWindowProc(), которая обрабатывает переданное ей сообщение по умолчанию. Таким образом, вы должны обрабатывать только те сообщения, обработка по умолчанию которых вас не устраивает. Также, функция DefWindowProc() не обрабатывает сообщение WM_DESTROY, поэтому вы должны предусмотреть его обработку самостоятельно. В приведенном примере, при получении окном сообщения WM_DESTROY, мы, с помощью функции PostQuitMessage(), ставим в очередь сообщение WM_QUIT, чтобы завершить работу программы.

Заметьте, каким образом сообщения обрабатываются по умолчанию. В структуре switch оконной процедуры предусмотрена метка default, которая пересылает все необрабатываемые нашей программой сообщения функции DefWindowProc() и возвращает результат этой функции. А если сообщение обрабатывается нашей программой, тогда возвращается ноль.

Вот оно, чудо

Теперь, разобравшись с кодом программы, откомпилируйте созданный проект и запустите приложение. Вы увидите пустое окно, с которым уже можно выполнять стандартные действия - перемещать, изменять размеры, сворачивать/разворачивать, и даже закрыть! : Все это достигается одним единственным стилем окна WS_OVERLAPPEDWINDOW, определенным при создании окна функцией CreateWindow().

Примечание: в элементе style, структуры WNDCLASS, определяется общий стиль для всех окон данного класса. Следует заметить, что стиль класса это не тоже самое что и стиль окна, указанный в вызове функции CreateWindow(). Тот стиль, который устанавливается посредством функции CreateWindow(), является индивидуальным стилем окна, а не общим стилем, определенным в классе.

Ресурсы программы

Практически в каждой Windows-программе можно увидеть различные элементы управления, меню, и другие ресурсы программы. Создать в окне какой либо элемент управления, например, кнопку, можно двумя способами. Первый, это создать новое окно используя функцию CreateWindow() с предопределенным в системе оконным классом "button". Второй способ, это использовать файлы ресурсов, в которых содержится описания всех ресурсов программы, будь то меню, элементы управления, иконки и даже диалоговые окна. Каждый элемент управления имеет свой уникальный идентификатор (хендл) определяемый программистом. Когда пользователь совершает какие либо действия над элементом управления, сообщение об этом поступают окну, и после этого выполняются соответствующие действия. К примеру, при нажатии на кнопку окно получает сообщение WM_COMMAND, которое в параметре wParam содержит идентификатор кнопки.

Подробнее о ресурсах приложения и работе с сообщениями мы поговорим несколько позже.


Может пригодится:


Автор: DigitalManiac
Прочитано: 35317
Рейтинг:
Оценить: 1 2 3 4 5

Комментарии: (17)

Прислал: TheGhost
Просто обожаю этот сайт спасибо.

Прислал: TheGhost
Просто обожаю этот сайт спасибо.

Прислал: Alexes
У меня установлен Visual C 4.0 и я сталкнулся с вот такой проблемой => error C2731: 'WinMain' : function cannot be overloaded и "see declaration of 'WinMain' ". я посмотрел во встроином help'е что и как и по идее должно работать.... если

Прислал: Alexes
если не трудно - подкажите что можно сделать ?

Прислал: гость
А ссылку для скачивания???

Прислал: Владислав
Это же статья

Прислал: Игорь
Очень полезная статья!

Прислал: Диман
Ваще рульный сайт! Процветания и успеха! И побольше таких исходников примеров и статей (которые дают азы знаний)

Прислал: Некит
Мда, холрошо что есть люди которые не только тупол спрашивают друг друг,а и пишут вот такие хорошие полезные статьи.

Прислал: AZ
Статья УКРФ =)

Прислал: Indrih
у меня установлена 6 версия программы, но ОС Vista стоит. И с программированием возникают чатсенько проблемы. Например выше описанная прога на вистаке не пашет а на хр(я в инсте проверял) работает! Стоит ли вообще что то на Висте писать?

Прислал: Алексей
Спасибо Indrih, а то собрался висту ставить.

Прислал: corey
помогите люди!я не могу открыть папку Winbase.h: и найти её пожалуста помогите,где её искать и как открыть?

Прислал: ssss
Для висты и вин7 строку 10 нужно адаптировать под юникод: LPCWSTR AppName = L"MyProgramm";

Прислал: ssss
..в среде VS2010

Прислал: Олег
Юзаю VS2010, выдает такий ошибки: IntelliSense: аргумент типа "LPSTR" несовместим с параметром типа "LPCWSTR" И Ошибка 1 error C2664: CreateWindowExW: невозможно преобразовать параметр 2 из "LPSTR" в "LPCWSTR&quo

Прислал: mihail
При компиляции пишет что не является приложением win32

Добавить комментарий
Ваше имя*:
Ваш email:
URL Вашего сайта:
Ваш комментарий*:
Код безопастности*:

Рассылка новостей
Рейтинги
© 2007, Программирование Исходники.Ру