You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Win32 UI工具栏位置过低问题排查求助

Win32工具栏位置异常(上方空白)问题排查与解决

问题描述

我正在学习Win32 UI开发,参考微软官方文档实现了一个工具栏示例,但运行后发现工具栏位置过低,上方出现大量空白。

以下是原代码:

#include <Windows.h>
#include <CommCtrl.h>
#include <cstdio>

HIMAGELIST g_hImageList = NULL;
const int IDM_NEW = 201;
const int IDM_OPEN = 202;
const int IDM_SAVE = 203;

HWND MainWindowToolbar( HWND Window )
{
    // Declare and initialize local constants.
    const int ImageListID    = 0;
    const int numButtons     = 3;
    const int bitmapSize     = 16;
    

    RECT MainWindowRect;
    GetClientRect(Window, &MainWindowRect);
    const DWORD buttonStyles = BTNS_AUTOSIZE;

    // Create the toolbar.
    HWND hWndToolbar = CreateWindowEx(0, TOOLBARCLASSNAME, NULL, 
                                      WS_CHILD | TBSTYLE_WRAPABLE, 0, 0, 0, 0, 
                                      Window, NULL, GetModuleHandle(NULL), NULL);
        
    if (hWndToolbar == NULL)
        return NULL;

    // Create the image list.
    g_hImageList = ImageList_Create(bitmapSize, bitmapSize,   // Dimensions of individual bitmaps.
                                    ILC_COLOR16 | ILC_MASK,   // Ensures transparent background.
                                    numButtons, 0);

    // Set the image list.
    SendMessage(hWndToolbar, TB_SETIMAGELIST, 
                (WPARAM)ImageListID, 
                (LPARAM)g_hImageList);

    // Load the button images.
    SendMessage(hWndToolbar, TB_LOADIMAGES, 
                (WPARAM)IDB_STD_SMALL_COLOR, 
                (LPARAM)HINST_COMMCTRL);

    // Initialize button info.
    // IDM_NEW, IDM_OPEN, and IDM_SAVE are application-defined command constants.
    
    TBBUTTON tbButtons[numButtons] = 
    {
        { MAKELONG(STD_FILENEW,  ImageListID), IDM_NEW,  TBSTATE_ENABLED, buttonStyles, {0}, 0, (INT_PTR)L"New" },
        { MAKELONG(STD_FILEOPEN, ImageListID), IDM_OPEN, TBSTATE_ENABLED, buttonStyles, {0}, 0, (INT_PTR)L"Open"},
        { MAKELONG(STD_FILESAVE, ImageListID), IDM_SAVE, 0,               buttonStyles, {0}, 0, (INT_PTR)L"Save"}
    };

    // Add buttons.
    SendMessage(hWndToolbar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
    SendMessage(hWndToolbar, TB_ADDBUTTONS,       (WPARAM)numButtons,       (LPARAM)&tbButtons);

    // Resize the toolbar, and then show it.
    SendMessage(hWndToolbar, TB_AUTOSIZE, 0, 0); 
    ShowWindow(hWndToolbar,  TRUE);
    
    return hWndToolbar;
}

HWND TreeViewHandle;
LRESULT CALLBACK MainWindowCallback(
    HWND Window,
    UINT Message,
    WPARAM WParam,
    LPARAM LParam
)
{
    LRESULT Result;
    switch(Message)
    {
        case WM_CREATE:
        {
            MainWindowToolbar( Window );
        } break;
        case WM_SIZE:
        {
            OutputDebugString("WM_SIZE\n");
        } break;
        case WM_DESTROY:
        {
            PostQuitMessage(0);
        } break;
        case WM_CLOSE:
        {
            OutputDebugString("WM_CLOSE\n");
        } break;
        case WM_ACTIVATEAPP:
        {
            OutputDebugString("WM_ACTIVATEAPP\n");
        } break;

        case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(Window, &ps);
            FillRect(hdc, &ps.rcPaint, (HBRUSH) (COLOR_WINDOW+1));
            EndPaint(Window, &ps);

        } break;
    }

    return DefWindowProc(Window, Message, WParam, LParam);
}

int CALLBACK WinMain(
    HINSTANCE Instance,
    HINSTANCE PrevInstance,
    LPSTR     lpCmdLine,
    int       nShowCmd
)
{
    WNDCLASS WindowClass = {};
    WindowClass.style         = CS_HREDRAW | CS_VREDRAW;
    WindowClass.lpfnWndProc   = MainWindowCallback;
    WindowClass.hInstance     = Instance;
    WindowClass.lpszMenuName = "mainMenu";
    WindowClass.lpszClassName = "MainWindow";

    RegisterClass(&WindowClass);

    HWND Window = CreateWindowEx(
        0,
        WindowClass.lpszClassName,
        "Win32 Test App",
        WS_OVERLAPPEDWINDOW | WS_VISIBLE,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        0,
        0,
        Instance,
        0
    );
    if (Window == nullptr)
    {
        printf("Error CreateWindow()\n");
        exit(1);
    }

    ShowWindow(Window, nShowCmd);

    MSG Message;
    while ( GetMessage(&Message, 0, 0, 0) > 0 )
    {
        TranslateMessage(&Message);
        DispatchMessage(&Message);
    }
}

问题原因

  1. 无效菜单引用:窗口类中指定了lpszMenuName = "mainMenu",但无对应菜单资源,系统会为不存在的菜单栏预留顶部空间,导致工具栏被向下挤压。
  2. 未处理WM_SIZE消息:窗口创建或尺寸改变时,未重新调整工具栏的位置与大小,无法自适应客户区变化。

修复方案

1. 移除无效菜单引用

在WNDCLASS初始化中删除WindowClass.lpszMenuName = "mainMenu";,避免系统预留无效的菜单栏空间。

2. 保存工具栏句柄并处理WM_SIZE消息

  • 替换无用的TreeViewHandle为全局工具栏句柄:
    HWND g_hWndToolbar = NULL;
    
  • 在WM_CREATE中保存工具栏句柄:
    case WM_CREATE:
    {
        g_hWndToolbar = MainWindowToolbar(Window);
    } break;
    
  • 修改WM_SIZE消息处理,确保工具栏贴顶并铺满客户区宽度:
    case WM_SIZE:
    {
        if (g_hWndToolbar != NULL)
        {
            RECT clientRect;
            GetClientRect(Window, &clientRect);
            // 让工具栏自动计算合适高度
            SendMessage(g_hWndToolbar, TB_AUTOSIZE, 0, 0);
            // 获取工具栏实际高度
            RECT toolbarRect;
            GetWindowRect(g_hWndToolbar, &toolbarRect);
            int toolbarHeight = toolbarRect.bottom - toolbarRect.top;
            // 移动并调整工具栏大小
            MoveWindow(g_hWndToolbar, 0, 0, clientRect.right, toolbarHeight, TRUE);
        }
    } break;
    

3. 初始化通用控件

在WinMain开头添加InitCommonControls();,确保工具栏等通用控件正确加载:

int CALLBACK WinMain(
    HINSTANCE Instance,
    HINSTANCE PrevInstance,
    LPSTR     lpCmdLine,
    int       nShowCmd
)
{
    InitCommonControls(); // 添加这一行
    // 剩余代码不变
}

修复后的完整代码

#include <Windows.h>
#include <CommCtrl.h>
#include <cstdio>

HIMAGELIST g_hImageList = NULL;
const int IDM_NEW = 201;
const int IDM_OPEN = 202;
const int IDM_SAVE = 203;
HWND g_hWndToolbar = NULL; // 全局工具栏句柄

HWND MainWindowToolbar( HWND Window )
{
    const int ImageListID    = 0;
    const int numButtons     = 3;
    const int bitmapSize     = 16;

    const DWORD buttonStyles = BTNS_AUTOSIZE;

    // 创建工具栏
    HWND hWndToolbar = CreateWindowEx(0, TOOLBARCLASSNAME, NULL, 
                                      WS_CHILD | TBSTYLE_WRAPABLE, 0, 0, 0, 0, 
                                      Window, NULL, GetModuleHandle(NULL), NULL);
        
    if (hWndToolbar == NULL)
        return NULL;

    // 创建图像列表
    g_hImageList = ImageList_Create(bitmapSize, bitmapSize,  
                                    ILC_COLOR16 | ILC_MASK,   
                                    numButtons, 0);

    // 设置图像列表
    SendMessage(hWndToolbar, TB_SETIMAGELIST, 
                (WPARAM)ImageListID, 
                (LPARAM)g_hImageList);

    // 加载标准按钮图像
    SendMessage(hWndToolbar, TB_LOADIMAGES, 
                (WPARAM)IDB_STD_SMALL_COLOR, 
                (LPARAM)HINST_COMMCTRL);

    // 初始化按钮信息
    TBBUTTON tbButtons[numButtons] = 
    {
        { MAKELONG(STD_FILENEW,  ImageListID), IDM_NEW,  TBSTATE_ENABLED, buttonStyles, {0}, 0, (INT_PTR)L"New" },
        { MAKELONG(STD_FILEOPEN, ImageListID), IDM_OPEN, TBSTATE_ENABLED, buttonStyles, {0}, 0, (INT_PTR)L"Open"},
        { MAKELONG(STD_FILESAVE, ImageListID), IDM_SAVE, 0,               buttonStyles, {0}, 0, (INT_PTR)L"Save"}
    };

    // 添加按钮
    SendMessage(hWndToolbar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
    SendMessage(hWndToolbar, TB_ADDBUTTONS,       (WPARAM)numButtons,       (LPARAM)&tbButtons);

    // 自动调整大小并显示
    SendMessage(hWndToolbar, TB_AUTOSIZE, 0, 0); 
    ShowWindow(hWndToolbar,  TRUE);
    
    return hWndToolbar;
}

LRESULT CALLBACK MainWindowCallback(
    HWND Window,
    UINT Message,
    WPARAM WParam,
    LPARAM LParam
)
{
    switch(Message)
    {
        case WM_CREATE:
        {
            g_hWndToolbar = MainWindowToolbar( Window );
        } break;
        case WM_SIZE:
        {
            if (g_hWndToolbar != NULL)
            {
                RECT clientRect;
                GetClientRect(Window, &clientRect);
                SendMessage(g_hWndToolbar, TB_AUTOSIZE, 0, 0);
                RECT toolbarRect;
                GetWindowRect(g_hWndToolbar, &toolbarRect);
                int toolbarHeight = toolbarRect.bottom - toolbarRect.top;
                MoveWindow(g_hWndToolbar, 0, 0, clientRect.right, toolbarHeight, TRUE);
            }
        } break;
        case WM_DESTROY:
        {
            PostQuitMessage(0);
        } break;
        case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(Window, &ps);
            FillRect(hdc, &ps.rcPaint, (HBRUSH) (COLOR_WINDOW+1));
            EndPaint(Window, &ps);
        } break;
    }

    return DefWindowProc(Window, Message, WParam, LParam);
}

int CALLBACK WinMain(
    HINSTANCE Instance,
    HINSTANCE PrevInstance,
    LPSTR     lpCmdLine,
    int       nShowCmd
)
{
    InitCommonControls(); // 初始化通用控件

    WNDCLASS WindowClass = {};
    WindowClass.style         = CS_HREDRAW | CS_VREDRAW;
    WindowClass.lpfnWndProc   = MainWindowCallback;
    WindowClass.hInstance     = Instance;
    WindowClass.lpszClassName = "MainWindow";

    RegisterClass(&WindowClass);

    HWND Window = CreateWindowEx(
        0,
        WindowClass.lpszClassName,
        "Win32 Test App",
        WS_OVERLAPPEDWINDOW | WS_VISIBLE,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        0,
        0,
        Instance,
        0
    );
    if (Window == nullptr)
    {
        printf("Error CreateWindow()\n");
        exit(1);
    }

    ShowWindow(Window, nShowCmd);

    MSG Message;
    while ( GetMessage(&Message, 0, 0, 0) > 0 )
    {
        TranslateMessage(&Message);
        DispatchMessage(&Message);
    }
}

内容的提问来源于stack exchange,提问作者Commodore63

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 21:19:59