Windows h создать окно

Обновлено: 03.07.2024

Пишем на WinAPI с «нуля»

Данная статья является введением в программирование на WinAPI. Важно понимать, что она носит скорее информационный характер, чем служит примером кода для реальных приложений. Дело в том, что WinAPI создавался для языка С, и имеет целый ряд недостатков в применении, как-то: невысокая безопасность, большой объем ручного кодирования для решения простейших задач, С-стиль, плохо выглядящий в C++-приложениях. Практически любое средство разработки на языке C++ для Windows включает те или иные высокоуровневые C++-библиотеки (MFC, ATL/WTL для Visual C++, VCL для C++ Builder), значительно упрощающие разработку Windows-приложений, и делающие ее более безопасной.

Предисловие

Эта статья посвящена описанию программирования приложений на «чистом» Win32 API. Она написана в основном для начинающих программистов, пишущих программы на Visual C++ 6 с использованием библиотеки MFC, но я надеюсь, может пригодиться и более опытным людям.

First Blood

После создания нового проекта Win32 Application, в зависимости от выбранных опций, мастер генерирует стартовый код. Из этого кода программисту впоследствии, и придется писать программу. Создавая новый проект Win32 Application, выберите в окне мастера опцию An empty project и добавьте в раздел Source Files новый файл с расширением .cpp.

В этом файле добавьте функцию WinMain вида:

  • HINSTANCE hInstance – дескриптор экземпляра приложения. Этот дескриптор содержит адрес начала кода программы в ее адресном пространстве. Дескриптор hInstance чаще всего требуется функциям, работающим с ресурсами программы.
  • HINSTANCE hPrevInstance – дескриптор предыдущего экземпляра приложения. Этот дескриптор остался от старых версий Windows - скорее всего, вам он никогда не пригодится.
  • LPSTR lpCmdLine – указатель на начало командной строки, введенной при запуске программы.
  • int nCmdShow – это значение содержит желаемый вид окна (например, свернутый или развернутый)

Значение, которое возвращается функцией WinMain (тип int ) – код завершения программы. Принято, что если программа завершила свое выполнение без ошибок, возвращается 0.

Функция WinMain – первая функция, которая выполнятся в программе (ее еще называют «точка входа» или «entry point»). С нее все начинается, и ею (желательно) все должно закончиться.

ПРИМЕЧАНИЕ

Функция WinMain – это первая функция, которую вы можете увидеть и заполнить кодом. На самом деле до этой функции выполняется достаточно много кода из библиотеки C++

You have a Message!

Программисты, незнакомые с программированием на WinAPI, спросят: «Что с этим делать?!», - или: «Где создавать CDialog?». В данном случае ответ прост – нигде! В нашем проекте нет класса CDialog или, предположим, CButton – ведь эта статья посвящена тому, как обойтись без них.

СОВЕТ

Функция GetMessage принимает следующие параметры:

СОВЕТ

При вызове этой функции ей передаются следующие параметры:

CLASSные окна

Итак, регистрация! За нее отвечает функция RegisterClass . В ее параметре необходимо передать указатель на структуру WNDCLASS . Обычно для заполнения структуры и вызова RegisterClass создают отдельную функцию. Но это - дело вкуса.

Вот простейший пример такой функции:

Функция RegisterClass возвращает уникальный «описатель класса окна» типа ATOM . Если при регистрации класса произошла ошибка, это значение будет равно нулю. Чтобы узнать, что произошло, можно вызвать функцию GetLastError() .

Существует также функция RegisterClassEx. Это аналог функции RegisterClass с возможностью присвоения окнам маленькой иконки. При работе с этой функцией необходимо пользоваться структурой WNDCLASSEX.

ПРИМЕЧАНИЕ

Если вы решились работать с GUI Windows вручную, то пользоваться нужно именно RegisterClassEx, поскольку приложение, не имеющее маленькой иконки, сегодня выглядит в Windows как минимум странно. – прим.ред.

СОВЕТ

Следите, чтобы имя вашего класса не совпадало с именами системных классов (например: button или edit).

ПРИМЕЧАНИЕ

Я описал не всю структуру. Все незаполненные поля, которых нет в примере, сейчас равны нулю. Об их значениях можно узнать из MSDN.

Our Windows

На вашем месте у меня возникло бы желание увидеть те самые пресловутые окна, из-за которых столько шума. Окно в Windows создается функцией CreateWindow . Вот ее прототип:

Как видите, у функции множество параметров:

Функция CreateWindow возвращает уникальный описатель окна HWND . Если функция вернула ноль, значит, во время создания окна произошла ошибка. Какая именно, можно узнать, вызвав функцию GetLastError .

ПРИМЕЧАНИЕ

Существует также функция CreateWindowEx, в которой дополнительно присутствует параметр dwExStyle. С его помощью можно создать окно с дополнительными стилями.

План полета

Итак, сейчас я упрощенно расскажу, что же произойдет, если щелкнуть по окну левой кнопкой мыши.

Итого

Вот, в принципе, и все! Это полноценное приложение на WinAPI.

Признаю, что данная статья и программа опускает очень много деталей! Многие вещи были не раскрыты (например, остальные переменные структуры WNDCLASS ). Все это сделано для того, чтобы максимально упростить статью и уменьшить код программы.

Казалось бы, что WinAPI уходит в прошлое. Давно уже существует огромное количество кросс-платформенных фреймфорков, Windows не только на десктопах, да и сами Microsoft в свой магазин не жалуют приложения, которые используют этого монстра. Помимо этого статей о том, как создать окошки на WinAPI, не только здесь, но и по всему интернету, исчисляется тысячами по уровню от дошколят и выше. Весь этот процесс разобран уже даже не по атомам, а по субатомным частицам. Что может быть проще и понятнее? А тут я еще…

Но не все так просто, как кажется.

Почему о WinAPI сейчас?

В один прекрасный момент, изучая потроха одной из игр в весьма неплохом эмуляторе NES, я подумал: Вроде неплохой такой эмуль, а в отладчике нет такой простой вещи, как навигация по кнопкам клавиатуры, которая есть в любом нормальном отладчике.

Здесь я не зря дал ссылку на репозиторий, т.к. видно, что ребята столкнулись с проблемой, о которой речь пойдет ниже, но так и не решили ее.

О чем это я? А вот об этом кусочке кода:

Ответ такой: так делать нельзя!

И, возвращаясь, к изначальному вопросу о WinAPI: очень много популярных, и не очень, проектов продолжают его использовать и в настоящее время, т.к. лучше, чем на чистом API многие вещи не сделать (тут можно бесконечно приводить аналогии вроде сравнения высокоуровневых языков и ассемблера, но сейчас не об этом). Да и мало ли почему? Просто используют и все тут.

О проблеме

Обойти такие мелкие неприятности просто. Есть, как минимум, два вполне легальных способа:

Tutorials?


Здесь действительно все просто:

Because the return value can be nonzero, zero, or -1, avoid code like this:

И ниже приводится пример правильного цикла.

Стоит сказать, что в шаблонах VS для Win32 приложений, написан именно такой неправильный цикл. И это очень печально. Ведь мало кто будет вникать в то, что сделали сами авторы, ведь это априори правильно. И неправильный код множится вместе с багами, которые очень сложно отловить.

После этого фрагмента кода, как правило, следует рассказ про акселераторы, и добавляется пара новых строчек (учитывая замечание в MSDN, предлагаю сразу писать правильный цикл):


Этот вариант я видел чаще всего. И он (та-дам) снова неправильный!

Сперва о том, что изменилось (потом о проблемах этого кода):

Ясно, что TranslateAccelerator надо вызывать для нашего созданного окна:


И вроде все хорошо и замечательно теперь: мы разобрали все детально и все должно работать идеально.

И снова нет. :-) Это будет работать правильно, пока у нас ровно одно окно — наше. Как только появится немодальное новое окно (диалог), все клавиши, которые будут в нем нажаты оттранслируются в WM_COMMAND и отправляться куда? И опять же правильно: в наше главное окно.

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

IsDialogMessage

На самом деле, делает она чуть больше, чем следует из названия. А именно:

  • Осуществляет навигацию по дочерним контролам кнопками Tab/Shift+Tab/вверх/вниз/вправо/влево. Плюс еще кое-что, но этого нам достаточно
  • По нажатии на ESC формирует WM_COMMAND( IDCANCEL )
  • По нажатии на Enter формирует WM_COMMAND( IDOK ) или нажатие на текущую кнопку по умолчанию
  • Переключает кнопки по умолчанию (рамочка у таких кнопок чуть ярче остальных)
  • Ну и еще разные штуки, которые облегчают пользователю работу с диалогом

Во-вторых, она нам облегчит жизнь по всем остальным пунктам, перечисленным в списке (и даже немного больше).

Вообще, она используется где-то в недрах Windows для обеспечения работы модальных диалоговых окон, а программистам дана, чтобы вызывать ее для немодальных диалогов. Однако мы ее можем использовать где угодно:

Although the IsDialogMessage function is intended for modeless dialog boxes, you can use it with any window that contains controls, enabling the windows to provide the same keyboard selection as is used in a dialog box.

Т.е. теперь, если мы оформим цикл так:


То наше окошко будет иметь навигацию, как в родном диалоге Windows. Но теперь мы получили два недостатка:

Пора поговорить о том, чего нет в туториалах и ответах.

Как правило (как правило! Если кому-то захочется большего, то можно регистрировать свой класс для диалогов и работать так. И, если же, кому-то это интересно, я могу дополнить этим статью) WM_KEYDOWN хотят тогда, когда хотят обработать нажатие на клавишу, которая выполнит функцию в независимости от выбранного контрола в окне — т.е. некая общая функция для всего данного конкретного диалога. А раз так, то почему бы не воспользоваться богатыми возможностями, которые нам сама WinAPI и предлагает: TranslateAccelerator.

Везде используют ровно одну таблицу акселераторов, и только для главного окна. Ну действительно: цикл GetMessage-loop один, значит и таблица одна. Куда еще их девать?

На самом деле, циклы GetMessage-loop могут быть вложенными. Давайте еще раз посмотрим описание PostQuitMessage:

The PostQuitMessage function posts a WM_QUIT message to the thread's message queue and returns immediately; the function simply indicates to the system that the thread is requesting to quit at some time in the future.
If the function retrieves the WM_QUIT message, the return value is zero.

Таким образом, выход из GetMessage-loop осуществится, если мы вызовем PostQuitMessage в процедуре окна. Что это значит?

Мы можем для каждого немодального окна в нашей программе создавать свой собственный подобный цикл. В данном случае DialogBoxParam нам не подходит, т.к. оно крутит свой собственный цикл и повлиять мы на него не можем. Однако если создадим диалог через CreateDialogBoxParam или окно через CreateWindow, то можно закрутить еще один цикл. При этом в каждом таком окне и диалоге мы должны вызывать PostQuitMessage:


Обратите внимание: теперь для каждого нового окна в нашей программе мы можем добавить в обработку собственную таблицу акселераторов. WM_QUIT будет выхватывать GetMessage из цикла для диалога, а внешний цикл его даже не увидит. Почему так происходит?

Дело в том, что внешний цикл «встал» на вызове DispatchMessage, который вызвал нашу процедуру, которая крутит свой собственный внутренний цикл GetMessage с таким же DispatchMessage. Классический вложенный вызов (в данном случае DispatchMessage). Посему внешний цикл не получит WM_QUIT и не завершится на этом этапе. Все будет работать стройно.

Делаем красиво

Т.к. правильная постановка задачи является половиной решения, то сперва надо эту самую задачу правильно же и поставить.

Создадим простой std::map, который будет мапить дескриптор окна в дескриптор таблицы акселераторов. Вот так:


И по мере создания окон будем в него добавлять новые окна с дескриптором на свою любимую таблицу (или нуль, если такая обработка не требуется).


Ну и после закрытия окна удалять. Вот так:


Теперь, как создаем новый диалог/окно, вызываем AddAccelerators( hNewDialog, IDR_MY_ACCEL_TABLE ). Как закрываем: DelAccel( hNewDialog ).


Значительно лучше! Что же там в HandleAccelArray и зачем там GetActiveWindow()?

Есть две функции, возвращающих дескриптор активного окна GetForegroundWindow и GetActiveWindow. Отличие первой от второй вполне доходчиво описано в описании второй:

The return value is the handle to the active window attached to the calling thread's message queue. Otherwise, the return value is NULL.


Теперь каждое дочернее окно вправе добавить себе любимую таблицу акселераторов и спокойно ловить и обрабатывать WM_COMMAND с нужным кодом.

А что там еще об одной строчке в коде обработчика WM_COMMAND?

To differentiate the message that this function sends from messages sent by menus or controls, the high-order word of the wParam parameter of the WM_COMMAND or WM_SYSCOMMAND message contains the value 1.

Обычно код обработки WM_COMMAND выглядит так:


Теперь можно написать так:

P.S.: Мало кто знает, но можно создавать свою собственную таблицу акселераторов, а теперь и применять ее прямо налету.

P.P.S.: Т.к. DialogBox/DialogBoxParam крутит собственный цикл, то от при вызове диалога через них акселераторы работать не будут и наш цикл (или циклы) будет «простаивать».

P.P.P.S.: После вызова HandleAccelWindow мап l_mAccelTable может измениться, т.к. TranslateAccelerator или IsDialogMessage вызывают DispatchMessage, а там может встретиться AddAccelerators или DelAccel в наших обработчиках! Поэтому лучше его после этой функции не трогать.

Пощупать код можно здесь. За основу был взят код, генерируемый из стандартного шаблона MS VS 2017.

WinAPI или Windows API (Application Programming Interface) - это библиотека для создания классических приложений Windows. Сама библиотека WinAPI написана на языке C и представляет собой коллекцию функций, структур и констант. Она объявлена в определённых заголовочных файлах и реализована в статических (.lib) и динамических (.dll) библиотеках.

В данном уроке мы создадим нашу первую программу на WinAPI. Для начала создайте проект. Выберите меню File -> New -> Project:

Новое окно VisualStudio WinAPI

В открытом окне в левой панели выберите Other, затем Empty Project (пустой проект). Там же доступен шаблон Windows Desktop Application, но мы напишем программу с нуля, так как шаблон по умолчанию пока слишком сложен для нас. В нижней части выберите имя проекта, его местоположение и хотите ли вы создать решение для него. Местоположение может быть любым, для меня это C:\prog\cpp\

WinAPI Окна

В обозревателе решений щёлкните правой кнопкой мышки и выберите Add -> New Item.

WinAPI Окно

В открывшемся окне выберите C++ File (.cpp) и введите имя файла в нижней части - main.cpp.

WinAPI Привет Мир

Перед тем как мы начнём рассматривать код, давайте поговорим о типах данных в WinAPI и соглашениях вызова (calling conventions).

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

WinAPI переопределяет множество стандартных типов языка C. Некоторые переопределения зависят от платформы для которой создаётся программа. Например, тип LRESULT, если его скомпилировать для x86, будет типом long. Но если скомпилировать программу для x64, то LRESULT будет типом __int64. Вот так LRESULT определяется на самом деле (он зависит от LONG_PTR, а LONG_PTR может уже быть или __int64, или long):

Соглашения по вызову (Calling Conventions) - __stdcall

В коде ниже перед именами функций вы встретите __stdcall. Это одно из соглашений по вызову функций. Соглашение по вызову функций определяет каким образом аргументы будут добавляться в стек. Для __stdcall аргументы помещаются в стек в обратном порядке - справа налево. Также, __stdcall говорит, что после того как функция завершится, она сама (а не вызывающая функция) удалит свои аргументы из стека. Все функции WinAPI используют __stdcall соглашение.

WinAPI переопределяет __stdcall в WINAPI, CALLBACK или APIENTRY, которые используются в разных ситуациях. Поэтому в примерах из MSDN вы не увидите __stdcall, но нужно помнить что именно оно будет использоваться.

Типы WinAPI пишутся в верхнем регистре.

Описатели/дескрипторы (Handles) в WinAPI

Handle на русский язык сложно перевести однозначно. Наверное, наиболее частое употребление в русском имеет слово дескриптор. По сути это ссылка на ресурс в памяти. Например, вы создаёте окно. Это окно хранится в памяти и оно имеет запись в таблице, которая хранит указатели на все созданные системные ресурсы: окна, шрифты, файлы, картинки. Указатель на ваше окно в данной таблице называется дескриптором окна (handle of the window).

Любой указатель это просто переопределение типа void*. Примеры дескрипторных типов в WinAPI: HWND, HINSTANCE, HBITMAP, HCURSOR, HFILE, HMENU.

Подытожим: дескрипторы используются для получения доступа к каким-либо системным ресурсам.

WinAPI окна

Давайте посмотрим на код самой простой WinAPI программы:

Вначале нужно добавить WinAPI: статичную библиотеку, которая содержит определения различных функций и включить заголовочный файл с объявлениями этих функций, структур и констант. user32.lib содержит основные возможности Windows - всё, что касается окон и обработки событий.

Функция WinMain

WinMain - точка входа в программу, а как мы помним такая функция вызывается операционной системой.

Главная функция приложений под Windows отличается от консольной версии. Она возвращает целое число и это всегда ноль. __sdtcall говорит, что аргументы добавляются в стек в обратном порядке и WinMain сама удаляет их из стека по завершении. WinMain принимает 4 аргумента:

int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow)

hInstance - дескриптор экземпляра приложения. Можете думать о нём, как о представлении вашего приложения в памяти. Он используется для создания окон.

Второй аргумент - наследие шестнадцатибитных версий Windows. Уже давно не используется.

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

nCmdShow - специальный флаг, который можно использовать при создании окон. Он говорит о состоянии окна: должно ли оно показываться нормально, на полный экран или быть свёрнутым.

Теперь давайте посмотрим как создаются окна.

Классы окон (Window Classes)

Сначала нужно заполнить структуру WNDCLASS. Пусть вас не смущает название WNDCLASS - это не C++ класс. В данном случае, класс - всего лишь термин используемый в WinAPI:

WNDCLASS windowClass = < 0 >; windowClass.lpfnWndProc = WindowProc; windowClass.hInstance = hInstance; windowClass.lpszClassName = "HELLO_WORLD"; RegisterClass(&windowClass);

Здесь мы инициализируем структуру WNDCLASS нулями, определяем обязательные поля и регистрируем класс.

lpfnWndProc имеет тип WNDPROC. Как говорилось выше, это указатель на функцию WindowProc, которую мы объявили в самом начале. У каждого оконного класса должна быть своя оконная процедура.

hInstance - дескриптор экземпляра приложения. Все оконные классы должны сообщать, какое приложение их зарегистрировало. Мы используем первый параметр функции WinMain.

lpszClassName - имя класса, задаётся пользователем. В Windows все классы называются в верхнем регистре (примеры: BUTTON, EDIT, LISTBOX), мы будем делать также в наших уроках.

WNDCLASS содержит больше полей: стиль, иконка, имя меню, но мы можем пропустить их. Некоторые из них мы рассмотрим в следующих уроках. Вы можете посмотреть полный список в документации к WinAPI на MSDN (официальном сайте Microsoft с документацией).

В конце мы регистрируем наш класс с помощью функции RegisterClass. Мы передаём адрес структуры WNDCLASS. Теперь мы можем создать окно.

Первая WinAPI программа - Пустое окно

В WinAPI есть функция для создания окон - CreateWindow:

HWND hwnd = CreateWindow( windowClass.lpszClassName, "Пустое WinAPI окно - Привет Мир!", WS_OVERLAPPEDWINDOW, 100, 50, 1280, 720, nullptr, nullptr, hInstance, nullptr);

Первый параметр - имя класса. В данном случае он совпадает с именем класса, который мы зарегистрировали. Второй - имя окна, это та строка, которую пользователи программы будут видеть в заголовке. Следующий - стиль. WS_OVERLAPPEDWINDOW говорит, что WinAPI окно имеет заголовок (caption), кнопки сворачивания и разворачивания, системное меню и рамку.

Четыре числа определяют позицию левого верхнего угла окна и ширину/высоту.

Затем идут два указателя nullptr. Первый - дескриптор родительского окна, второй - меню. У нашего окна нет ни того, ни другого.

hInstance - дескриптор на экземпляр приложения, с которым связано окно.

В последний аргумент мы передаём nullptr. Он используется для специальных случаев - MDI (Multiple Document Interface ) - окно в окне.

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

ShowWindow (показать окно) использует параметр nCmdShow функции WinMain для контроля начального состояния (развёрнуто на весь экран, минимизировано, обычный размер). UpdateWindow (обновить окно) мы обсудим в следующих уроках.

Главный цикл WinAPI

Далее идёт бесконечный цикл. В этом цикле мы будем реагировать на разные события, возникающие при взаимодействии пользователя с нашей программой.

Оконная процедура (Window Procedure) WindowProc

LRESULT __stdcall WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) < switch (message) < case WM_DESTROY: PostQuitMessage(0); return 0; >return DefWindowProc(hWnd, message, wParam, lParam); >

Обратите внимание, что мы сами нигде не вызываем WindowProc. Оконная процедура привязана к классу окна. И когда мы вызываем DispatchMessage, система сама вызывает оконную процедуру, связанную с окном. Такие функции называются функциями обратного вызова (callback functions) - они не вызываются напрямую. Также обратите внимание, что данная функция получает только часть свойств MSG структуры.

Заключение

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

Каждое окно имеет рамку и заголовок. Рамка служит для определения рабочей области, а так же для изменения размеров окна. Заголовок содержит имя запущенной программы и ряд управляющих кнопок: свернуть, распахнуть на весь экран и закрыть и применяется для изменения местоположения окна на экране.

Рассмотрим как же создать простое приложение выводящее на экран окно в операционной системе Windows с использованием Win API на языке С++:

Как точка входа в программу в оконных приложениях Windows используется функция WinMain. Именно с вызова этой функции и начинает работать наша программа.

Прототип функции выглядит следующим образом:

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

Эту функцию вызывает операционная система при запуске приложения и передает в неё 4 параметра:

hInstance Дескриптор текущего экземпляра приложения. Так как Windows является многозадачной системой, то очевидно, что одна и та же программа может быть запущена несколько раз. Для того чтобы различать экземпляры программ, каждому экземпляру присваивается условный номер – хэндл (handle). Отмечу, так же, что в Win32 присваиваются хэндлы чему угодно – окну, меню, курсору, иконке и т.д.
hPrevInctance Указатель на предыдущий запущенный экземпляр. Всегда равен NULL. В Windows 3.1 hPrevInctance являлся хэндлом предыдущего экземпляра программы. Если запускается первый экземпляр программы, то параметр hPrevInctance был равен нулю. Этот параметр можно было использовать для того, чтобы не позволить системе запускать более одного экземпляра программы. В Win32 hPrevInctance оставлен исключительно для совместимости с предыдущими версиями Windows.
lpCmdLine Указатель на командную строку запускаемой программы. Представляет собой указатель на строку параметров, которые были указаны после имени запускаемой программы. При необходимости программа может проанализировать этот аргумент и выполнить некоторые действия.
nCmdShow Способ визуализации окна. Определяет, в каком виде создаваемое окно будет появляться на экране. Окно может появиться развернутым на весь экран, свернутым в панель задач, иметь произвольный размер на рабочем столе и другие характеристики. В Win API определяется 11 возможных значений этого параметра. Их идентификаторы начинаются с SW (Show Window).

Возможные значения параметра nCmdShow приведены ниже:

SW_HIDE Скрывает окно и активизирует другое окно.
SW_MAXIMIZE Распахивает указанное окно на весь экран.
SW_MINIMIZE Минимизирует указанное окно и активизирует следующее окно верхнего уровня Z буфера.
SW_RESTORE Активизирует и отображает окно. Если окно минимизировано или развернуто, система восстанавливает его в исходное положение и размер. Приложение должно указать этот флаг при восстановлении свернутого окна.
SW_SHOW Активирует окно и выводит на экран это в его текущем размере и позиции.
SW_SHOWMAXIMIZED Активизирует окно и отображает его как развернутое окно.
SW_SHOWMINIMIZED Активизирует окно и отображает его как свернутое окно.
SW_SHOWMINNOACTIVE Отображает окно как свернутое окно. Это значение аналогично SW_SHOWMINIMIZED, за исключением того, что окно не активизируется.
SW_SHOWNA Отображает окно в его текущем размере и позиции. Это значение аналогично SW_SHOW, за исключением того, что окно не активизируется.
SW_SHOWNOACTIVATE Отображает окно в его последнем размер и положение. Это значение аналогично SW_SHOWNORMAL, за исключением того, что окно не активируется.
SW_SHOWNORMAL Активизирует и отображает окно. Если окно минимизировано или развернуто, система восстанавливает его в исходное положение и размер. Приложение должно указать этот флаг при показе окна в первый раз.

Указатель типа int предшествующий названию функции говорит о том, что функция должна вернуть вызывающей её системе целое значение, а характеристика WINAPI – определяет порядок передачи параметров при вызове процедуры. Наименование характеристик говорит само за себя – Windows Application Programming Interface – применяются соглашения о передаче параметров, принятых в системах Windows.

Следующим шагом нужно создать, заполнить и зарегистрировать класс окна. Класс окна представляет собой структуру с именем WNDCLASS.

Прототип структуры WNDCLASS выглядит следующим образом:

ATOM RegisterClass( CONST WNDCLASS *lpWndClass );

В качестве параметров функция принимает указатель на структуру WNDCLASS созданную нами ранее.

HWND CreateWindow ( LPCTSTR lpClassName, LPCTSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam );

lpClassName Указатель на имя класса окна.
lpWindowName Указатель на имя окна. При использовании CreateWindow для создания элементов управления, таких как кнопки, флажки и статические элементы управления, в lpWindowName указывается идентификатор элемента.
dwStyle Определяет стиль окна. Этот параметр может быть комбинацией стилей окна.
x Задает начальное горизонтальное положение окна.
у Задает начальное вертикальное положение окна.
nWidth Определяет ширину окна в пикселях.
nHeight Определяет высоту окна в пикселях.
hWndParent Дескриптор родителя(владельца) создаваемого окна.
hMenu Дескриптор меню.
hInstance Хэндл экземпляра окна.
lpParam Мы пока использовать не будем.

При успешном выполнении функция возвращает дескриптор созданного окна, при ошибке NULL.

Для того чтобы корректно отобразить окно на экране, следует выполнить ещё две функции ShowWindow и UpdateWindow.

BOOL ShowWindow( HWND hWnd, int nCmdShow )

Функция ShowWindow отображает окно на экране. Первый параметр – дискриптор окна, второй – режим отображения. В качестве этого параметра обычно используют параметр nWinMode функции WinMain.

BOOL UpdateWindow( HWND hWnd )

while (GetMessage(&msg, NULL, 0, 0))

Во всех трех функциях присутствует указатель на строку MSG. Разберём её:

Вот прототип оконной функции:

LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )

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

Читайте также: