Windows API (Win32) — это фундамент, на котором строятся все приложения для Windows. Если вы пишете код, который взаимодействует с системой на низком уровне, вам нужен быстрый доступ к ключевым функциям: работа с файлами, процессами, памятью и реестром. Эта статья — не общий обзор, а компактный справочник с конкретными сигнатурами, примерами и типичными сценариями использования. Здесь нет лишней теории — только то, что пригодится в ежедневной разработке.
Базовые понятия, которые нужно знать перед стартом
Прежде чем переходить к функциям, разберём четыре важных механизма, без которых невозможно правильно работать с Win32.
Дескрипторы (HANDLE) и их закрытие
Большинство функций Win32, открывающих ресурсы (файлы, процессы, ключи реестра), возвращают значение типа HANDLE — это целочисленный дескриптор (непрозрачный идентификатор), который ядро использует для ссылки на объект. Он не является указателем на память и не должен интерпретироваться как адрес. Дескриптор нужно обязательно закрывать с помощью CloseHandle (или специализированной функции, например RegCloseKey), иначе произойдёт утечка ресурсов. Каждый открытый дескриптор потребляет память и может привести к нестабильности системы при долгой работе.
INVALID_HANDLE_VALUE
При ошибке функция CreateFile возвращает не NULL, а специальное значение INVALID_HANDLE_VALUE (определено как (HANDLE)-1). Это важно помнить: проверяйте именно на это значение, а не на NULL.
Коды ошибок и GetLastError
Каждая функция Win32 возвращает своё специальное значение ошибки: это может быть FALSE (0) для функций с типом BOOL, NULL для функций, возвращающих указатели (например, VirtualAlloc, OpenProcess), INVALID_HANDLE_VALUE для CreateFile, или код, отличный от ERROR_SUCCESS для функций реестра. Чтобы получить детальную причину, сразу после неудачного вызова используйте GetLastError(). Код ошибки можно сопоставить с константами из winerror.h (например, ERROR_FILE_NOT_FOUND).
Версии функций: A и W
Многие строковые функции существуют в двух вариантах: с суффиксом A (ANSI, однобайтовая кодировка) и W (широкие символы, UTF-16). В современных проектах рекомендуется использовать W-версии, так как Windows внутренне работает с UTF-16. При использовании A-версий происходит преобразование в кодовую страницу системы (например, Windows-1251), что может исказить нелатинские символы. Убедитесь, что в проекте определены макросы UNICODE и _UNICODE, чтобы по умолчанию вызывались широкие версии.
Ключевые функции Win32: сводная таблица
Ниже приведены наиболее востребованные функции, сгруппированные по категориям. Для каждой указаны: назначение, основные параметры, возвращаемое значение и типичный сценарий использования.
Работа с файлами и каталогами
| Функция | Детали |
|---|---|
| CreateFile |
Назначение: Открывает или создаёт файл, устройство, канал
Параметры:
lpFileName dwDesiredAccess dwShareMode lpSecurityAttributes dwCreationDisposition dwFlagsAndAttributes hTemplateFile Возвращает: HANDLE или INVALID_HANDLE_VALUE
Сценарий: Открыть файл для чтения/записи
|
| ReadFile |
Назначение: Читает данные из файла или устройства
Параметры:
hFile lpBuffer nNumberOfBytesToRead lpNumberOfBytesRead lpOverlapped Возвращает: BOOL (ненулевое при успехе)
Сценарий: Прочитать содержимое файла в буфер
|
| WriteFile |
Назначение: Записывает данные в файл или устройство
Параметры:
hFile lpBuffer nNumberOfBytesToWrite lpNumberOfBytesWritten lpOverlapped Возвращает: BOOL
Сценарий: Сохранить данные в файл
|
| CloseHandle |
Назначение: Закрывает любой дескриптор
Параметры:
hObject Возвращает: BOOL
Сценарий: Освободить ресурс после работы
|
Управление процессами
| Функция | Детали |
|---|---|
| CreateProcess |
Назначение: Запускает новый процесс
Параметры:
lpApplicationName lpCommandLine lpProcessAttributes lpThreadAttributes bInheritHandles dwCreationFlags lpEnvironment lpCurrentDirectory lpStartupInfo lpProcessInformation Возвращает: BOOL
Сценарий: Запустить внешнее приложение (например, калькулятор)
|
| OpenProcess |
Назначение: Открывает существующий процесс по PID
Параметры:
dwDesiredAccess bInheritHandle dwProcessId Возвращает: HANDLE или NULL
Сценарий: Получить доступ к памяти другого процесса
|
| TerminateProcess |
Назначение: Принудительно завершает процесс
Параметры:
hProcess uExitCode Возвращает: BOOL
Сценарий: Аварийно остановить процесс (используйте с осторожностью)
|
Управление памятью
| Функция | Детали |
|---|---|
| VirtualAlloc |
Назначение: Резервирует или фиксирует память в виртуальном адресном пространстве
Параметры:
lpAddress dwSize flAllocationType flProtect Возвращает: LPVOID или NULL
Сценарий: Выделить буфер для чтения больших файлов
|
| VirtualFree |
Назначение: Освобождает память
Параметры:
lpAddress dwSize dwFreeType Возвращает: BOOL
Сценарий: Освободить выделенный участок
|
| ReadProcessMemory |
Назначение: Читает память другого процесса
Параметры:
hProcess lpBaseAddress lpBuffer nSize lpNumberOfBytesRead Возвращает: BOOL
Сценарий: Отладка или мониторинг
|
Работа с реестром
| Функция | Детали |
|---|---|
| RegOpenKeyEx |
Назначение: Открывает существующий раздел реестра
Параметры:
hKey lpSubKey ulOptions samDesired phkResult Возвращает: LONG (ERROR_SUCCESS при успехе)
Сценарий: Открыть ключ для чтения настроек
|
| RegQueryValueEx |
Назначение: Получает значение из открытого раздела
Параметры:
hKey lpValueName lpReserved lpType lpData lpcbData Возвращает: LONG
Сценарий: Прочитать строковое или DWORD-значение
|
| RegSetValueEx |
Назначение: Записывает значение в раздел
Параметры:
hKey lpValueName Reserved dwType lpData cbData Возвращает: LONG
Сценарий: Сохранить настройки приложения
|
| RegCloseKey |
Назначение: Закрывает дескриптор раздела
Параметры:
hKey Возвращает: LONG
Сценарий: Закрыть ключ после работы
|
Примитивы синхронизации
| Функция | Детали |
|---|---|
| CreateMutex |
Назначение: Создаёт или открывает именованный мьютекс
Параметры:
lpMutexAttributes bInitialOwner lpName Возвращает: HANDLE или NULL
Сценарий: Обеспечить одиночный запуск приложения
|
| WaitForSingleObject |
Назначение: Ожидает сигнального состояния объекта
Параметры:
hHandle dwMilliseconds Возвращает: DWORD (WAIT_OBJECT_0 и др.)
Сценарий: Ожидать завершения потока или освобождения мьютекса
|
Практические примеры кода
Чтение файла с обработкой ошибок
#include <windows.h>
#include <stdio.h>
int main() {
HANDLE hFile = CreateFileW(
L"example.txt",
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (hFile == INVALID_HANDLE_VALUE) {
DWORD err = GetLastError();
printf("Не удалось открыть файл, ошибка: %lu\n", err);
return 1;
}
char buffer[1024];
DWORD bytesRead;
if (!ReadFile(hFile, buffer, sizeof(buffer) - 1, &bytesRead, NULL)) {
DWORD err = GetLastError();
printf("Ошибка чтения: %lu\n", err);
CloseHandle(hFile);
return 1;
}
buffer[bytesRead] = '\0';
printf("Содержимое: %s\n", buffer);
CloseHandle(hFile);
return 0;
}
Запуск процесса (калькулятор) и ожидание его завершения
STARTUPINFOW si = { sizeof(si) };
PROCESS_INFORMATION pi;
if (CreateProcessW(
L"C:\\Windows\\System32\\calc.exe",
NULL, NULL, NULL, FALSE,
0, NULL, NULL, &si, &pi)) {
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
} else {
printf("Ошибка запуска: %lu\n", GetLastError());
}
Чтение значения из реестра
HKEY hKey;
LONG result = RegOpenKeyExW(
HKEY_LOCAL_MACHINE,
L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
0, KEY_READ, &hKey);
if (result == ERROR_SUCCESS) {
WCHAR buffer[256];
// Внимание: размер буфера передаётся в байтах, а не в символах.
// Для WCHAR-массива sizeof(buffer) даёт общий размер в байтах,
// что корректно для RegQueryValueExW.
DWORD bufferSize = sizeof(buffer);
DWORD type;
if (RegQueryValueExW(hKey, L"ProductName", NULL, &type,
(LPBYTE)buffer, &bufferSize) == ERROR_SUCCESS) {
wprintf(L"Имя продукта: %s\n", buffer);
}
RegCloseKey(hKey);
} else {
printf("Не удалось открыть ключ, код: %ld\n", result);
}
Частые ошибки и как их избежать (кратко)
- Забыли закрыть дескриптор — всегда используйте
CloseHandleилиRegCloseKeyв паре с открытием. - Проверка на NULL вместо INVALID_HANDLE_VALUE — для
CreateFileпроверяйте именно наINVALID_HANDLE_VALUE. - Игнорирование кодов ошибок — вызывайте
GetLastError()сразу после неудачного вызова. - Смешивание ANSI и Unicode — используйте
W-версии функций и определитеUNICODE. - Неправильный размер буфера — всегда передавайте актуальный размер и проверяйте, сколько байт записано.
Часто задаваемые вопросы (FAQ)
Это константа, равная -1. Её возвращает
CreateFile и некоторые другие функции (например, CreateNamedPipe). Однако функции OpenProcess, CreateMutex, CreateThread при ошибке возвращают NULL. Всегда сверяйтесь с документацией конкретной функции, чтобы знать, какое значение ошибки ожидать.CloseHandle закрывает общие дескрипторы (файлы, процессы, мьютексы). Для ключей реестра используйте RegCloseKey — она делает то же самое, но специально для реестра.Рекомендуется
W (Unicode), так как это родной формат Windows. Использование A может привести к проблемам с кодировкой для неанглийских символов.Возможно, вы вызвали другую функцию до
GetLastError. Код ошибки сохраняется только до следующего вызова Win32 API. Вызывайте GetLastError сразу после неудачной функции.Заключение
Этот справочник покрывает основные функции Win32, которые понадобятся в большинстве прикладных задач. Освоив чтение файлов, запуск процессов, работу с реестром и базовую синхронизацию, вы сможете создавать надёжные системные утилиты и тонко настраивать поведение приложений. Для углублённого изучения обращайтесь к официальной документации Microsoft Learn — там вы найдёте полные списки параметров и дополнительные примеры.
Если вы тестируете Win32-приложения, которые требуют изолированного окружения, удобно использовать выделенный сервер — например, VPS от Serverspace. На нём можно развернуть чистую Windows, проводить эксперименты с дескрипторами и памятью, не боясь нарушить стабильность основной рабочей станции.