ПРИМЕЧАНИЕ
Я описал не всю структуру. Все незаполненные поля, которых нет в примере, сейчас равны нулю. Об их значениях можно узнать из MSDN.
Our Windows
На вашем месте у меня возникло бы желание увидеть те самые пресловутые окна, из-за которых столько шума. Окно в Windows создается функцией CreateWindow . Вот ее прототип:
Как видите, у функции множество параметров:
Функция CreateWindow возвращает уникальный описатель окна HWND . Если функция вернула ноль, значит, во время создания окна произошла ошибка. Какая именно, можно узнать, вызвав функцию GetLastError .
ПРИМЕЧАНИЕ
Существует также функция CreateWindowEx, в которой дополнительно присутствует параметр dwExStyle. С его помощью можно создать окно с дополнительными стилями.
План полета
Итак, сейчас я упрощенно расскажу, что же произойдет, если щелкнуть по окну левой кнопкой мыши.
Итого
Вот, в принципе, и все! Это полноценное приложение на WinAPI.
Признаю, что данная статья и программа опускает очень много деталей! Многие вещи были не раскрыты (например, остальные переменные структуры WNDCLASS ). Все это сделано для того, чтобы максимально упростить статью и уменьшить код программы.
Уроки программирования, алгоритмы, статьи, исходники, примеры программ и полезные советы
Параллельные вычисления с помощью WinAPI
В этой статье поговорим о параллельных вычислениях, реализуемых средствами Windows API.
Рассмотрим функций доступные в WIndows API для работы с потоками. Для того, чтобы создать поток используется функция CreateThread.
- lpSecAttr – указатель на SECURITY_ATTRIBUTES, этот параметр также можно указать равным NULL, тогда возвращаемый дескриптор не будет наследоваться
- StackSize – начальный размер стека (указывается в байтах). Если в качестве параметра указать ноль, то система задаст размер стека по умолчанию
- lpStartFuncAddr – это указатель на функцию, которая будет исполняться потоком, таким образом здесь будет указан стартовый адрес потока; эта функция должна быть определена в программе следующим образом: DWORD WINAPI FunctionName(LPVOID)
- p – это указатель на переменную, которую нам необходимо передать в функцию FunctionName(LPVOID p), исполняемую потоком
- dwCreatParam – параметры, которые управляют созданием потока. Можно указать: 0 (тогда поток начнет исполняться сразу после его создания), CREATE_SUSPENDED (поток начнет исполняться, когда будет выполнена функция ResumeThread, о ней позже)
- thrId – это указатель на переменную, в которую будет записан идентификатор потока
Если нужно возобновить данный поток, то необходимо вызвать функцию ResumeThread. При успешном выполнении возвращается предыдущее количество остановок потока, иначе -1.
Теперь рассмотрим пример программы, которая осуществляет параллельную многопоточную генерацию и обработку строк двумерной матрицы. То есть каждый отдельный поток будет генерировать строку матрицы и находить произведение нечётных элементов этой строки. В каждый поток будем передавать в качестве параметра указатель на структуру, в которой будет хранится строка матрицы и результат произведения элементов.
Подключим необходимые библиотеки: для вывода результатов на экран, для использования функций WinAPI и для использования времени в качестве инициализирующего элемента генератора случайных чисел.
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.
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.
Читайте также: