Вызов какой функции в winapi в ос windows служит для создания нити

Обновлено: 07.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 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.

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