Помогите пожалуйста с кодом C++ который создает окно на Windows.
Хочу создать окно самое обычное но без заголовка окна. Тень и рамка быть должны а вот заголовка у окна быть не должно. При этом в панели задач в миниатюре этого окна и заголовочное имя окна и иконка и крестик быть должны. Помогите пожалуйста я бьюсь с этим blYat простите безумные 7 часов в поисках такой возможности. Может кто написать код как пример? Сил нет уже
Понимаю твою боль, сам в свое время убил кучу времени на эту виндовс апи. Чтобы убрать заголовок но оставить стандартную тень и рамку, тебе нужно убрать стиль WS_CAPTION, но обязательно оставить WS_THICKFRAME и WS_SYSMENU, а затем в WM_NCCALCSIZE вернуть ноль. Вот рабочий пример самого скелета окна:
C++
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
if (uMsg == WM_NCCALCSIZE && wParam == TRUE) {
return 0;
}
if (uMsg == WM_DESTROY) {
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow) {
const wchar_t CLASS_NAME[] = L"SampleWindowClass";
WNDCLASS wc = {};
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);
HWND hwnd = CreateWindowEx(
WS_EX_APPWINDOW,
CLASS_NAME,
L"Мое окно",
WS_OVERLAPPEDWINDOW & ~WS_CAPTION,
CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
NULL, NULL, hInstance, NULL
);
ShowWindow(hwnd, nCmdShow);
MSG msg = {};
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
Иконка и крестик в миниатюре панели задач тут сохраняются корректно за счет системного меню и правильных стилей. Пробуй, должно заработать.
Вот готовый, полностью рабочий пример кода на C++ (чистый Win32 API), который делает ровно то, что вы хотите. Создается окно без заголовка, но с нативной тенью, изменяемым размером (если захотите) и полным функционалом в панели задач:
C++
#include <windows.h>
#include <dwmapi.h>
#pragma comment(lib, "dwmapi.lib")
// Оконная процедура
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_NCCALCSIZE: {
// ЭТО ГЛАВНЫЙ СЕКРЕТ:
// Когда wParam == TRUE, мы возвращаем 0, тем самым заявляем системе,
// что вся клиентская область окна равна размеру всего окна (включая область заголовка).
// Система думает, что заголовка нет, но при этом сохраняет тень и рамку!
if (wParam == TRUE) {
// Если вы хотите, чтобы окно вообще не имело отступов рамки при изменении размера,
// просто возвращаем 0. Рамка и тень останутся благодаря стилям окна.
return 0;
}
break;
}
case WM_NCHITTEST: {
// Так как мы убрали заголовок, окно теперь нельзя двигать за шапку.
// Этот блок возвращает системе команду, что зажатие ЛКМ в верхней части окна
// означает "перетаскивание окна" (HTCAPTION).
// Сделайте область захвата сверху в 30 пикселей, чтобы можно было таскать за верх.
POINT pt = { LOWORD(lParam), HIWORD(lParam) };
ScreenToClient(hwnd, &pt);
RECT rcClient;
GetClientRect(hwnd, &rcClient);
// Если курсор в верхней части окна (первые 35 пикселей) — позволяем таскать окно
if (pt.y < 35 && pt.x > 35 && pt.x < rcClient.right - 35) {
return HTCAPTION;
}
// Тут же можно добавить изменение размеров за края, если нужен стиль WS_THICKFRAME
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
case WM_CLOSE:
DestroyWindow(hwnd);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
const wchar_t CLASS_NAME[] = L"BorderlessShadowWindowCls";
WNDCLASSEX wc = {};
wc.cbSize = sizeof(WNDCLASSEX);
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszClassName = CLASS_NAME;
wc.hIcon = LoadIcon(nullptr, IDI_APPLICATION); // Иконка для панели задач
wc.hIconSm = LoadIcon(nullptr, IDI_APPLICATION);
RegisterClassEx(&wc);
// ВНИМАНИЕ НА СТИЛИ:
// WS_POPUP или WS_OVERLAPPEDWINDOW с добавлением WS_THICKFRAME / WS_MAXIMIZEBOX / WS_MINIMIZEBOX.
// Нам нужен стиль WS_THICKFRAME (или WS_DLGFRAME), чтобы DWM понимал, что у окна есть рамка и тень,
// но стандартную шапку мы убираем через WM_NCCALCSIZE.
DWORD style = WS_OVERLAPPEDWINDOW;
// Убираем WS_CAPTION, чтобы шапка исчезла физически, но оставляем рамку/тень
style &= ~WS_CAPTION;
HWND hwnd = CreateWindowEx(
0, // Дополнительные стили
CLASS_NAME, // Имя класса
L"Мое крутое окно без шапки", // Заголовок (отображается в панели задач и миниатюре!)
style, // Стили окна
CW_USEDEFAULT, CW_USEDEFAULT, // Положение X, Y
800, 600, // Ширина, Высота
nullptr, // Родительское окно
nullptr, // Меню
hInstance, // Приложение
nullptr // Доп. параметры
);
if (hwnd == nullptr) {
return 0;
}
// Дополнительно страхуем отрисовку тени на Windows 10/11
MARGINS margins = { 1, 1, 1, 1 };
DwmExtendFrameIntoClientArea(hwnd, &margins);
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// Цикл сообщений
MSG msg = {};
while (GetMessage(&msg, nullptr, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
Что здесь происходит и почему это работает:
Имя и иконка в панели задач: Задаются стандартным путем в функции CreateWindowEx (передачей текста L"Мое крутое окно без шапки") и структуре WNDCLASSEX (иконки hIcon). Поскольку мы используем стили винды (просто без шапки), ДWM и проводник Windows видят это окно как полноценное приложение: с крестиком при наведении на иконку в панели задач, миниатюрой и правильным именем.
Тень и рамка: Достигаются за счет того, что мы оставляем системе системные рамки (WS_OVERLAPPEDWINDOW минус WS_CAPTION), но в сообщении WM_NCCALCSIZE «обманываем» Windows, заставляя её считать, что область заголовка имеет нулевую высоту. Рамка и тень от DWM остаются на месте, а серой шапки с кнопками свернуть/закрыть сверху больше нет.
Как его двигать: Так как шапку мы стерли, зажать мышку и потащить окно не за что. В коде выше в обработчике WM_NCHITTEST прописано: если зажать левую кнопку мыши в верхней части окна (в пределах 35 пикселей), система думает, что вы держитесь за заголовок, и позволяет таскать окно по экрану.
#include <windows.h>
#include <dwmapi.h>
#pragma comment(lib, "dwmapi.lib")
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_CREATE: {
// Включаем тень Aero/DWM для окна с кастомной неклиентской областью
MARGINS margins = { 0, 0, 0, 0 };
DwmExtendFrameIntoClientArea(hwnd, &margins);
// Принудительно обновляем рамки окна
SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
SWP_ACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
return 0;
}
case WM_NCCALCSIZE: {
// Если wParam == TRUE, мы сообщаем Windows, что клиентская область
// занимает всё окно, тем самым скрывая стандартный заголовок (WS_CAPTION).
if (wParam == TRUE) {
return 0;
}
break;
}
case WM_NCHITTEST: {
// Позволяет перетаскивать окно за любую его часть,
// так как стандартного заголовка больше нет
LRESULT hit = DefWindowProc(hwnd, uMsg, wParam, lParam);
if (hit == HTCLIENT) {
return HTCAPTION;
}
return hit;
}
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
const wchar_t CLASS_NAME[] = L"NoCaptionWindowClass";
WNDCLASS wc = { 0 };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
// Загрузите здесь вашу иконку, чтобы она отображалась в панели задач и миниатюре:
// wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_YOUR_ICON));
RegisterClass(&wc);
// Важно: оставляем WS_OVERLAPPEDWINDOW, чтобы Windows сгенерировала
// полноценную миниатюру в панели задач с заголовком и иконкой
HWND hwnd = CreateWindowEx(
0,
CLASS_NAME,
L"Имя окна для панели задач", // Это имя будет видно в миниатюре
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
NULL,
NULL,
hInstance,
NULL
);
if (hwnd == NULL) {
return 0;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
MSG msg = { 0 };
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}