Why is the Win32 Tab and Tab ID not showing in the client area of the main window?

Cristoforo Montanari 0 Reputation points
2025-04-19T02:19:28.6166667+00:00

WinMain.cpp:

#define _CRT_SECURE_NO_WARNINGS
#define WIN32_LEAN_AND_MEAN
#define _WIN32_WINNT 0x0601 // Target Windows 7 or later
#define WIN_32_EXTRA_LEAN
#define UNICODE
#define _UNICODE
#include "glad/glad.h"
#include <Windows.h>
#include <commctrl.h>
#include <windowsx.h>
#include <iostream>
#include <utility>
#include "Application.h"
#include "OpenGLDefinitions.h"
int WINAPI WinMain(HINSTANCE, HINSTANCE, PSTR, int);
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam);
bool SetPixelFormatForContext(HDC hdc, BYTE colorBits = 24, BYTE depthBits = 32, BYTE stencilBits = 8);
WNDCLASSEX RegisterWindowClasses(HINSTANCE hinstance);
std::pair<int, int> GetScreenDimensions();
HGLRC CreateOpenGLContext(HDC hdc, const int* attribList = nullptr);
bool EnableVSync(HDC hdc);
void isGLADInit();
#if _DEBUG 
#pragma comment (linker, "/subsystem:console")
int main(int argc, const char** argv) {
	return WinMain(GetModuleHandle(NULL), NULL, GetCommandLineA(), SW_SHOWDEFAULT);
}
#else 
#pragma comment (linker, "/subsystem:windows")
#endif
#pragma comment(lib, "comctl32.lib")
#pragma comment(lib, "opengl32.lib")
#pragma comment(lib, "glu32.lib")
Application* gApplication = nullptr;
GLuint gVertexArrayObject = 0;
std::pair<int, int> GetScreenDimensions() {
	int screenWidth = GetSystemMetrics(SM_CXSCREEN);
	int screenHeight = GetSystemMetrics(SM_CYSCREEN);
	return std::make_pair(screenWidth, screenHeight);
}
void isGLADInit() {
	if (!gladLoadGL())
	{
		std::cout << "Could not initialize GLAD \n";
	}
	else {
		std::cout << "OpenGL Version " << GLVersion.major << std::endl;
	}
}
int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int ICmdShow) {
	INITCOMMONCONTROLSEX icex;
	icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
	icex.dwICC = ICC_TAB_CLASSES; 
	InitCommonControlsEx(&icex);
	gApplication = new Application();
	WNDCLASSEX mainWndClass = RegisterWindowClasses(hinstance);
    std::pair<int, int> screenDimensions = GetScreenDimensions();  
    int ScreenWidth = screenDimensions.first;  
    int ScreenHeight = screenDimensions.second;
	int clientWidth = 800;
	int clientHeight = 600;
	RECT windowRect;
	SetRect(&windowRect, (ScreenWidth / 2) - (clientWidth / 2),
		(ScreenHeight / 2) - (clientHeight / 2),
		(ScreenWidth / 2) + (clientWidth / 2),
		(ScreenHeight / 2) + (clientHeight / 2));
	DWORD mainWindowStyle = (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME);
	AdjustWindowRectEx(&windowRect, mainWindowStyle, FALSE, 0);
	const wchar_t windowName[] = "Win32 Tab Window";
	HWND hwndMainWindow = CreateWindowEx(0, mainWndClass.lpszClassName, windowName, mainWindowStyle, 
		windowRect.left, windowRect.top, windowRect.right - windowRect.left,
		windowRect.bottom - windowRect.top, NULL, NULL, hinstance, szCmdLine);
	HDC hdcMainWindow = GetDC(hwndMainWindow);
	if (!SetPixelFormatForContext(hdcMainWindow)) {
		std::cerr << "Error setting pixel format for the device context." << std::endl;
		return -1; 
	}
	ShowWindow(hwndMainWindow, SW_SHOW);
	UpdateWindow(hwndMainWindow);
	gApplication->Initialize();
	int vsynch = 0; // Default to no vsync
	DWORD lastTick = GetTickCount();
	MSG msg;
	while (true) {
		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
			if (msg.message == WM_QUIT) {
				break;
			}
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		DWORD thisTick = GetTickCount();
		float dt = float(thisTick - lastTick) * 0.001f;
		lastTick = thisTick;
		if (gApplication != nullptr) {
			gApplication->Update(dt);
			RECT clientRect;
			GetClientRect(hwndMainWindow, &clientRect);
			clientWidth = clientRect.right - clientRect.left;
			clientHeight = clientRect.bottom - clientRect.top;
		}
	}
	if (gApplication != nullptr) {
		std::cout << "Expected application to be null on exit \n";
		delete gApplication;
	}
	return (int)msg.wParam;
}

WndProc.cpp:

#include <Windows.h>
#include <commctrl.h>
#include <windowsx.h>
#include <iostream>
#include "Application.h"
#include "WndProc.h"
#include "glad/glad.h"
extern Application* gApplication;
extern GLuint gVertexArrayObject;
HWND hTabControl = nullptr; // Handle to the Tab control
WNDPROC gOriginalTabProc = nullptr; // Global variable to store the original Tab Control procedure
LRESULT CALLBACK TabControlProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) {
    switch (iMsg) {
    case WM_PAINT: {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hwnd, &ps);
        // Get the client area of the Tab Control
        RECT clientRect;
        GetClientRect(hwnd, &clientRect);
        // Set a background color
        HBRUSH hBrush = CreateSolidBrush(RGB(255, 255, 255)); // White background
        FillRect(hdc, &clientRect, hBrush);
        DeleteObject(hBrush);
        // Draw a cross in the Tab Control's client area
        HPEN hPen = CreatePen(PS_SOLID, 2, RGB(0, 0, 255)); // Blue cross
        SelectObject(hdc, hPen);
        MoveToEx(hdc, clientRect.left, clientRect.top, nullptr);
        LineTo(hdc, clientRect.right, clientRect.bottom);
        MoveToEx(hdc, clientRect.right, clientRect.top, nullptr);
        LineTo(hdc, clientRect.left, clientRect.bottom);
        DeleteObject(hPen);
        EndPaint(hwnd, &ps);
        return 0;
    }
    }
    // Forward unhandled messages to the original Tab Control procedure
    return CallWindowProc(gOriginalTabProc, hwnd, iMsg, wParam, lParam);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) {
    switch (iMsg) {
 
    case WM_CREATE: {
        std::cout << "Window created successfully! \n";
        // Create the Tab Control
        hTabControl = CreateWindowEx(
            0, WC_TABCONTROL, nullptr,
            WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS,
            0, 0, 0, 0, // Initial size will be adjusted later
            hwnd, nullptr, GetModuleHandle(nullptr), nullptr
        );
        if (!hTabControl) {
            std::cerr << "Failed to create Tab control.\n";
        }
        // Add a tab
        TCITEM tie;
        tie.mask = TCIF_TEXT;
        tie.pszText = const_cast<LPWSTR>(L"Tab 1");
        SendMessage(hTabControl, TCM_INSERTITEM, 0, (LPARAM)&tie);
        // Subclass the Tab Control
        gOriginalTabProc = (WNDPROC)SetWindowLongPtr(hTabControl, GWLP_WNDPROC, (LONG_PTR)TabControlProc);
        break;
    }
    case WM_SIZE: {
        // Resize the Tab control to fill the client area
        if (hTabControl) {
            RECT clientRect;
            GetClientRect(hwnd, &clientRect);
            MoveWindow(hTabControl, 0, 0, clientRect.right, clientRect.bottom, TRUE);
        }
        break;
    }
    case WM_PAINT: {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hwnd, &ps);
        // Draw a cross in the client area of the Tab control
        if (hTabControl) {
            RECT clientRect;
            GetClientRect(hTabControl, &clientRect);
            // Set a background color
            HBRUSH hBrush = CreateSolidBrush(RGB(200, 200, 200));
            FillRect(hdc, &clientRect, hBrush);
            DeleteObject(hBrush);
            // Draw a cross
            HPEN hPen = CreatePen(PS_SOLID, 2, RGB(0, 0, 0));
            SelectObject(hdc, hPen);
            MoveToEx(hdc, clientRect.left, clientRect.top, nullptr);
            LineTo(hdc, clientRect.right, clientRect.bottom);
            MoveToEx(hdc, clientRect.right, clientRect.top, nullptr);
            LineTo(hdc, clientRect.left, clientRect.bottom);
            DeleteObject(hPen);
        }
        EndPaint(hwnd, &ps);
        break;
    }
    case WM_CLOSE:
        if (gApplication != nullptr) {
            gApplication->Shutdown();
            delete gApplication;
            gApplication = nullptr;
            DestroyWindow(hwnd);
        }
        else {
            std::cout << "Already shut down! \n";
        }
        break;
    case WM_DESTROY:
        if (gVertexArrayObject != 0) {
            HDC hdc = GetDC(hwnd);
            HGLRC hglrc = wglGetCurrentContext();
            glBindVertexArray(0);
            glDeleteVertexArrays(1, &gVertexArrayObject);
            gVertexArrayObject = 0;
            wglMakeCurrent(NULL, NULL);
            wglDeleteContext(hglrc);
            ReleaseDC(hwnd, hdc);
            PostQuitMessage(0);
        }
        else {
            std::cout << "Multiple destroy messages \n";
        }
        break;
    }
    return DefWindowProc(hwnd, iMsg, wParam, lParam);
}

Win32TabWindow

I no longer see the Tab and text string ID in the upper left of the window like I did before adding the code to paint the 'x'. Where did the tab control go?

So far there is an output in the console window that "Multiple destroy messages" are taking place. Is that relevant to the tab visibility problem that is going on?

So far as I know the client area of the tab is being recieved to draw to so it seems that currently the client area is looking too large and would be drawing over the tab currently. It seems if I turn off the drawing code the Tab still isn't visible in the client area of the window which will appear on my system with a gray background color.

The functions that were not included in this post came from:
https://github.com/Eversmile12/OpenGLGLAD

as far as the OpenGL bootstrapping and the RegisterWindowClass function...

Windows API - Win32
Windows API - Win32
A core set of Windows application programming interfaces (APIs) for desktop and server applications. Previously known as Win32 API.
2,761 questions
0 comments No comments
{count} votes

1 answer

Sort by: Most helpful
  1. Castorix31 88,871 Reputation points
    2025-04-22T21:58:59.5333333+00:00

    It is normal, in TabControlProc, you paint over the whole Tab Control

    You can check with Spy++ that it is behind

    What are you trying to do ?

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.