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

Windows C++自定义通知窗口图片加载失败问题求助

自定义Windows通知窗口图片加载失败排查与解决

问题背景

开发一款可发送自定义通知的Windows C++程序,通知窗口能正常显示,但图片无法加载。尝试用ChatGPT排查,但因其知识截止到2021年9月,无法适配API更新给出有效方案。

相关代码

#include <Windows.h>
#include <Shellapi.h>
 
// Constants
#define WM_CLOSENOTIFICATION (WM_USER + 1)
#define BUTTON_OPENURL_ID 100
#define BUTTON_CLOSE_ID 101
#define IDB_NOTIFICATION_BITMAP 108 // Replace with your bitmap resource ID
 
// Global variables
HWND hMainWindow;
HWND hNotificationWindow;
HBITMAP hBitmap;
 
// Function to create and show the notification window
void ShowNotification()
{
    // Get the screen dimensions
    int screenWidth = GetSystemMetrics(SM_CXSCREEN);
    int screenHeight = GetSystemMetrics(SM_CYSCREEN);
 
    // Calculate the center position for the notification window
    int notificationWidth = 400; // Set your desired width
    int notificationHeight = 200; // Set your desired height
    int notificationX = (screenWidth - notificationWidth) / 2;
    int notificationY = (screenHeight - notificationHeight) / 2;
 
    // Create the custom notification window
    hNotificationWindow = CreateWindow(
        L"NotificationWindowClass",         // Class name
        L"Custom Notification",             // Window title
        WS_POPUP | WS_VISIBLE | WS_BORDER,  // Window styles
        notificationX, notificationY,        // Position
        notificationWidth, notificationHeight,   // Size
        nullptr, nullptr, nullptr, nullptr   // Parent window, Menu handle, Instance, Additional data
    );
 
    // Load the bitmap resource
    hBitmap = LoadBitmap(GetModuleHandle(nullptr), MAKEINTRESOURCE(IDB_NOTIFICATION_BITMAP));
    if (hBitmap == nullptr)
    {
        OutputDebugStringA("Error Bitmap Null Value");
        // Display appropriate error message or take necessary actions
    }
 
    // Create a static control for the image
    HWND hImageStatic = CreateWindow(
        L"STATIC",                      // Window class name
        nullptr,                        // Text content (empty for image)
        WS_CHILD | WS_VISIBLE | SS_BITMAP | SS_CENTERIMAGE,  // Window styles
        10, 10, notificationWidth - 20, notificationHeight - 20, // Position and size
        hNotificationWindow, nullptr, nullptr, nullptr  // Parent window, Menu handle, Instance, Additional data
    );
    SendMessage(hImageStatic, STM_SETIMAGE, IMAGE_BITMAP, reinterpret_cast<LPARAM>(hBitmap));
 
    // Create a button to open a website
    HWND hOpenButton = CreateWindow(
        L"BUTTON",                      // Window class name
        L"Open Website",                // Text content
        WS_CHILD | WS_VISIBLE | BS_CENTER | BS_DEFPUSHBUTTON,  // Window styles
        10, notificationHeight - 60, (notificationWidth - 40) / 2, 30, // Position and size
        hNotificationWindow, reinterpret_cast<HMENU>(BUTTON_OPENURL_ID), nullptr, nullptr  // Parent window, Menu handle, Instance, Additional data
    );
 
    // Create a button to close the notification
    HWND hCloseButton = CreateWindow(
        L"BUTTON",                      // Window class name
        L"Close",                       // Text content
        WS_CHILD | WS_VISIBLE | BS_CENTER | BS_DEFPUSHBUTTON,  // Window styles
        20 + (notificationWidth - 40) / 2, notificationHeight - 60, (notificationWidth - 40) / 2, 30, // Position and size
        hNotificationWindow, reinterpret_cast<HMENU>(BUTTON_CLOSE_ID), nullptr, nullptr  // Parent window, Menu handle, Instance, Additional data
    );
}
 
// Window procedure for the notification window
LRESULT CALLBACK NotificationWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_COMMAND:
        // Handle command messages
        if (LOWORD(wParam) == BUTTON_OPENURL_ID)
        {
            // Open a website when the button is clicked
            ShellExecute(nullptr, L"open", L"https://example.com", nullptr, nullptr, SW_SHOWNORMAL);
        }
        else if (LOWORD(wParam) == BUTTON_CLOSE_ID)
        {
            // Show the main window again
            ShowWindow(hMainWindow, SW_SHOW);
            // Clean up and exit the notification window
            DestroyWindow(hNotificationWindow);
        }
        break;
 
    case WM_CLOSE:
        // Show the main window again
        ShowWindow(hMainWindow, SW_SHOW);
        // Clean up and exit the notification window
        DestroyWindow(hNotificationWindow);
        break;
    }
 
    return DefWindowProc(hWnd, message, wParam, lParam);
}
 
// Entry point
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
    // Create and register the window class for the notification window
    WNDCLASS wc = {};
    wc.lpfnWndProc = NotificationWindowProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = L"NotificationWindowClass";
    RegisterClass(&wc);
 
    // Create the main window
    hMainWindow = CreateWindow(
        L"MainWindowClass",             // Window class name
        L"Main Window",                 // Window title
        WS_OVERLAPPEDWINDOW,            // Window styles
        CW_USEDEFAULT, CW_USEDEFAULT,   // Position
        CW_USEDEFAULT, CW_USEDEFAULT,   // Size
        nullptr, nullptr, hInstance, nullptr   // Parent window, Menu handle, Instance, Additional data
    );
 
    // Show the main window
    ShowWindow(hMainWindow, nCmdShow);
 
    // Show the initial notification
    ShowNotification();
 
    // Message loop
    MSG msg = {};
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
 
    // Clean up resources
    if (hBitmap != nullptr)
    {
        DeleteObject(hBitmap);
    }
 
    return 0;
}

补充信息

  • VS中.rc文件截图显示:资源定义内包含ID为108的位图资源IDB_NOTIFICATION_BITMAP
  • 程序运行效果:通知窗口正常显示,但图片区域为空,仅可见按钮和窗口边框

排查与解决方案

1. 替换老旧的位图加载API

LoadBitmap是兼容性较差的旧API,在高DPI环境或资源格式不匹配时易加载失败,改用LoadImage替代:

// 替换原LoadBitmap代码
hBitmap = (HBITMAP)LoadImage(
    GetModuleHandle(nullptr),
    MAKEINTRESOURCE(IDB_NOTIFICATION_BITMAP),
    IMAGE_BITMAP,
    0, 0, // 按资源原始尺寸加载,可指定宽高强制缩放
    LR_DEFAULTCOLOR | LR_SHARED
);

若需适配高DPI,可结合GetDpiForWindow获取当前窗口DPI值,计算缩放后的宽高传入LoadImage。

2. 修正资源释放逻辑

调用STM_SETIMAGE后,静态控件会接管位图资源的所有权,原代码在消息循环结束后删除位图会导致资源提前释放。应在通知窗口销毁时释放资源,修改窗口过程:

LRESULT CALLBACK NotificationWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    // ... 保留原有WM_COMMAND、WM_CLOSE处理逻辑
    case WM_DESTROY:
        if (hBitmap != nullptr)
        {
            DeleteObject(hBitmap);
            hBitmap = nullptr;
        }
        break;
    }
    return DefWindowProc(hWnd, message, wParam, lParam);
}

3. 增强错误排查能力

添加详细错误日志,通过GetLastError获取具体错误码,快速定位问题:

if (hBitmap == nullptr)
{
    DWORD errCode = GetLastError();
    char errMsg[256];
    sprintf_s(errMsg, "位图加载失败,错误码:%lu", errCode);
    OutputDebugStringA(errMsg);
}

常见错误码对应问题:

  • ERROR_RESOURCE_NAME_NOT_FOUND:资源名称/ID不匹配
  • ERROR_RESOURCE_TYPE_NOT_FOUND:资源类型错误(非BITMAP)
  • ERROR_FILE_NOT_FOUND:位图文件路径错误或缺失

4. 项目资源编译检查

  • 确认位图文件已正确添加到项目,.rc文件被包含在编译流程中
  • 检查位图格式是否为BMP(LoadBitmap仅支持BMP,LoadImage支持更多格式但需对应参数)
  • 清理项目后重新编译,确保资源被正确嵌入可执行文件

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 13:14:54