请问C语言GDI如何将bmp图像加载到窗口上?

我最近在研究如何在win窗口程序中将一张图片绘制到窗口上
我在我桌面的“t”文件夹内绘制了一个名为“a.bmp”的图像:

img

然后编写了如下代码:
#include <windows.h>
#include "math.h"
#include "stdio.h"

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

HBITMAP hbmpBack = NULL;//定义全局变量

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) {
    WNDCLASS wc;

    static TCHAR *szAppName = TEXT("ck");

    HWND hwnd = NULL;

    MSG msg;

    wc.style = CS_HREDRAW | CS_VREDRAW; //窗口样式
    wc.lpfnWndProc = WndProc; //过程函数
    wc.cbClsExtra = 0; //扩展字段
    wc.cbWndExtra = 0; //扩展字段
    wc.hInstance = hInstance; //当前实例句柄
    wc.hIcon = LoadIcon(hInstance, IDI_APPLICATION); //设置程序图标
    wc.hCursor = LoadCursor(NULL, IDC_ARROW); //设置鼠标
    wc.hbrBackground = CreateSolidBrush(RGB(0, 0, 0));
    wc.lpszMenuName = NULL;
    wc.lpszClassName = szAppName;

    if (!RegisterClass(&wc)) {

        MessageBox(NULL, TEXT("程序只能在windowsNT下运行"),

                   szAppName, MB_ICONERROR);

        return 0;

    }

    hwnd = CreateWindow(szAppName, //要注册的窗口类名
                        TEXT("c"),//窗口标题
                        WS_OVERLAPPEDWINDOW, //窗口样式
                        CW_USEDEFAULT, //窗口距离屏幕左上角都横坐标
                        CW_USEDEFAULT, //窗口距离屏幕左上角都纵坐标
                        1000, //窗口宽度
                        800, //窗口高度
                        NULL, //父窗口句柄
                        NULL, //菜单句柄
                        hInstance, //当前实例句柄
                        NULL);
    ShowWindow(hwnd, iCmdShow);
    UpdateWindow(hwnd);

    hbmpBack = (HBITMAP) LoadImage(NULL, "C:\\Users\\Administrator\\Desktop\\t\\a.bmp",
                                   IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);

    if (hbmpBack == NULL) {
        return 0;
    }

    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;

}

/**
*
*消息处理函数
*
*/

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
    int wmId, wmEvent;
    PAINTSTRUCT ps;
    HDC hdc;
    switch (message) {
        case WM_PAINT: {
            hdc = BeginPaint(hwnd, &ps);
            HDC hDCMem = CreateCompatibleDC(hdc);//创建兼容DC
            HBITMAP hOldBmp = (HBITMAP) SelectObject(hDCMem,hbmpBack);//把hbmBack的位图选择到兼容DC HDCMem,之后这个兼容DC就拥有和hbmpBack同样大小的绘图区域
            BITMAP bmp;
            GetObject(hbmpBack, sizeof(BITMAP), &bmp);//获取位图的大小信息,事实上也是兼容DC绘图输出的范围
            BitBlt(hdc,0,0,bmp.bmWidth,bmp.bmHeight,hDCMem,0,0,SRCCOPY);//复制位图
            DeleteDC(hDCMem);//删除兼容DC,避免内存泄漏
            EndPaint(hwnd, &ps);
            break;

            break;
        }
        case WM_DESTROY:

            PostQuitMessage(0);

            break;

    }



//调用默认的过程函数

    return DefWindowProc(hwnd, message, wParam, lParam);

}

但是运行后这个程序并没有在窗口上加载出我准备好得bmp文件:

img

请问为什么没有加载图片?是程序出现问题了吗?需要怎么改进才可以让图片显示到窗口上?求解答,感谢!

引用chatgpt内容作答:
从您提供的代码来看,您的窗口程序的结构基本是正确的,但是在绘制图片的部分存在一些问题。让我帮您分析一下并提供修改建议。

问题出现在消息处理函数 WndProc 中的 WM_PAINT 分支中,具体是绘制图片的部分。您需要确保在绘制图片之前创建一个兼容的内存设备上下文(Memory DC),并在绘制完毕后进行清理。以下是经过修改的代码片段,我在注释中说明了每一步的作用:

case WM_PAINT: {
    hdc = BeginPaint(hwnd, &ps);

    // 创建兼容的内存设备上下文
    HDC hDCMem = CreateCompatibleDC(hdc);

    // 将位图对象选择进入内存设备上下文
    HBITMAP hOldBmp = (HBITMAP) SelectObject(hDCMem, hbmpBack);

    // 获取位图对象的信息
    BITMAP bmp;
    GetObject(hbmpBack, sizeof(BITMAP), &bmp);

    // 在窗口的设备上下文中绘制位图
    BitBlt(hdc, 0, 0, bmp.bmWidth, bmp.bmHeight, hDCMem, 0, 0, SRCCOPY);

    // 清理内存设备上下文
    SelectObject(hDCMem, hOldBmp);  // 恢复原来的位图对象
    DeleteDC(hDCMem);               // 删除兼容的内存设备上下文

    EndPaint(hwnd, &ps);
    break;
}

此外,我还注意到您的程序中有一个多余的 break 语句,您可以将其删除。修改后的代码应该可以在窗口中正确加载并显示位图图像了。

如果在实际运行中仍然遇到问题,请确保图片路径是正确的,并且图片确实存在。

该回答通过自己思路及引用到GPTᴼᴾᴱᴺᴬᴵ搜索,得到内容具体如下:根据你提供的代码,有几个可能导致图片没有显示在窗口上的问题:

1、 图片路径问题:你在LoadImage函数中使用的是绝对路径,路径中包含了你电脑上的具体用户名,这会导致在其他电脑上无法找到该图片。你可以尝试使用相对路径或者将图片放在一个固定位置,例如与可执行文件相同的目录下。

2、 图片加载失败:你在加载图片时使用了LoadImage函数,但没有进行错误检查。你可以添加错误检查代码,查看是否成功加载了图片。例如:

hbmpBack = (HBITMAP)LoadImage(NULL, "C:\\Users\\Administrator\\Desktop\\t\\a.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);

if (hbmpBack == NULL) {
    MessageBox(NULL, TEXT("无法加载图片"), szAppName, MB_ICONERROR);
    return 0;
}

如果加载图片失败,可能是图片路径错误或者图片格式不受支持。

3、 绘图位置问题:在WM_PAINT消息处理函数中,你使用BitBlt函数将位图复制到窗口的设备上下文中。确保绘制的位置是正确的,即 (0, 0) 处开始,并且绘制的尺寸与位图的尺寸一致。如果图片尺寸过大,可能会超出窗口可见范围。

以下是修改后的代码,修复了路径问题、错误检查和绘图位置问题:

#include <windows.h>
#include "stdio.h"

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

HBITMAP hbmpBack = NULL; // 定义全局变量

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) {
    WNDCLASS wc;

    static TCHAR *szAppName = TEXT("ck");

    HWND hwnd = NULL;

    MSG msg;

    wc.style = CS_HREDRAW | CS_VREDRAW; // 窗口样式
    wc.lpfnWndProc = WndProc; // 过程函数
    wc.cbClsExtra = 0; // 扩展字段
    wc.cbWndExtra = 0; // 扩展字段
    wc.hInstance = hInstance; // 当前实例句柄
    wc.hIcon = LoadIcon(hInstance, IDI_APPLICATION); // 设置程序图标
    wc.hCursor = LoadCursor(NULL, IDC_ARROW); // 设置鼠标
    wc.hbrBackground = CreateSolidBrush(RGB(0, 0, 0));
    wc.lpszMenuName = NULL;
    wc.lpszClassName = szAppName;

    if (!RegisterClass(&wc)) {

        MessageBox(NULL, TEXT("程序只能在Windows NT下运行"), szAppName, MB_ICONERROR);

        return 0;
    }

    hwnd = CreateWindow(szAppName, // 要注册的窗口类名
        TEXT("c"), // 窗口标题
        WS_OVERLAPPEDWINDOW, // 窗口样式
        CW_USEDEFAULT, // 窗口距离屏幕左上角的横坐标
        CW_USEDEFAULT, // 窗口距离屏幕左上角的纵坐标
        1000, // 窗口宽度
        800, // 窗口高度
        NULL, // 父窗口句柄
        NULL, // 菜单句柄
        hInstance, // 当前实例句柄
        NULL);
    ShowWindow(hwnd, iCmdShow);
    UpdateWindow(hwnd);

    hbmpBack = (HBITMAP)LoadImage(NULL, "a.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);

    if (hbmpBack == NULL) {
        MessageBox(NULL, TEXT("无法加载图片"), szAppName, MB_ICONERROR);
        return 0;
    }

    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}

/**
* 消息处理函数
*/
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
    switch (message) {
        case WM_PAINT: {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hwnd, &ps);
            HDC hDCMem = CreateCompatibleDC(hdc); // 创建兼容DC
            HBITMAP hOldBmp = (HBITMAP)SelectObject(hDCMem, hbmpBack); // 把hbmpBack的位图选择到兼容DC hDCMem,之后这个兼容DC就拥有和hbmpBack同样大小的绘图区域
            BITMAP bmp;
            GetObject(hbmpBack, sizeof(BITMAP), &bmp); // 获取位图的大小信息,事实上也是兼容DC绘图输出的范围
            BitBlt(hdc, 0, 0, bmp.bmWidth, bmp.bmHeight, hDCMem, 0, 0, SRCCOPY); // 复制位图
            SelectObject(hDCMem, hOldBmp); // 恢复原来的位图
            DeleteDC(hDCMem); // 删除兼容DC,避免内存泄漏
            EndPaint(hwnd, &ps);
            break;
        }
        case WM_DESTROY:

            PostQuitMessage(0);

            break;
    }

    // 调用默认的过程函数
    return DefWindowProc(hwnd, message, wParam, lParam);
}

注意,上述代码假设a.bmp文件与可执行文件在同一目录下。如果不是,请根据实际情况修改LoadImage函数中的图片路径。另外,确保编译链接时包含了G根据你提供的代码。

尝试检查以上问题,并进行相应的调整,应该能够使图片正确显示在窗口上。如果问题仍然存在,请提供详细的错误信息或调试信息,以便进一步排查。


如果以上回答对您有所帮助,点击一下采纳该答案~谢谢

【以下回答由 GPT 生成】

要将位图加载到窗口上,你可以按照以下步骤修改代码:

  1. 在 WinMain 函数中,将位图的路径修改为正确的路径。由于位图文件保存在桌面的"t"文件夹中,你需要将路径修改为你的用户名。例如,将 C:\\Users\\YourUsername\\Desktop\\t\\a.bmp 修改为 C:\\Users\\YourUsername\\Desktop\\t\\a.bmpc hbmpBack = (HBITMAP)LoadImage(NULL, TEXT("C:\\Users\\YourUsername\\Desktop\\t\\a.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);

  2. 在 WM_CREATE 消息处理中,释放设备上下文的句柄之前,将设备上下文的句柄保存到 hdc 之前。 c hdc = GetDC(hwnd);

  3. 使用 LoadImage 函数加载位图文件,并将返回的句柄赋值给 hbmpBack。 c hbmpBack = (HBITMAP)LoadImage(NULL, TEXT("C:\\Users\\YourUsername\\Desktop\\t\\a.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);

  4. 使用 GetObject 函数获取位图的信息,包括宽度和高度。 c GetObject(hbmpBack, sizeof(BITMAP), (LPSTR)&bm);

  5. 在 WM_SIZE 消息处理中,根据窗口的大小计算在窗口中绘制位图所需的坐标。 c xClient = (cxClient - cxSource) / 2; yClient = (cyClient - cySource) / 2;

  6. 根据位图的信息设置 BITMAPINFOHEADER 结构体的成员变量。 c bi.biSize = sizeof(BITMAPINFOHEADER); bi.biWidth = cxSource; bi.biHeight = cySource; bi.biPlanes = 1; bi.biBitCount = 24; bi.biCompression = BI_RGB; bi.biSizeImage = 0; bi.biXPelsPerMeter = 0; bi.biYPelsPerMeter = 0; bi.biClrUsed = 0; bi.biClrImportant = 0;

  7. 在 WM_PAINT 消息处理中,创建一个兼容的设备上下文 hdcMem,并将位图选入设备上下文。 c hdcMem = CreateCompatibleDC(hdc); SelectObject(hdcMem, hbmpBack);

  8. 使用 SetStretchBltMode 函数将拉伸模式设置为 HALFTONE,并使用 StretchDIBits 函数将位图绘制在窗口上。 c SetStretchBltMode(hdc, HALFTONE); StretchDIBits(hdc, xClient, yClient, cxSource, cySource, 0, 0, cxSource, cySource, (LPSTR)&bi, (LPBYTE)bm.bmBits, DIB_RGB_COLORS, SRCCOPY);

  9. 在 WM_DESTROY 消息处理中,删除位图对象的句柄。 c DeleteObject(hbmpBack);

下面是修改后的代码:

#include <windows.h>
#include <stdio.h>
#include <math.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

HBITMAP hbmpBack = NULL; // 定义全局变量

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) {
    WNDCLASS wc;

    static TCHAR *szAppName = TEXT("ck");

    HWND hwnd = NULL;

    MSG msg;

    wc.style = CS_HREDRAW | CS_VREDRAW; //窗口样式
    wc.lpfnWndProc = WndProc; //过程函数
    wc.cbClsExtra = 0; //扩展字段
    wc.cbWndExtra = 0; //扩展字段
    wc.hInstance = hInstance; //当前实例句柄
    wc.hIcon = LoadIcon(hInstance, IDI_APPLICATION); //设置程序图标
    wc.hCursor = LoadCursor(NULL, IDC_ARROW); //设置鼠标
    wc.hbrBackground = CreateSolidBrush(RGB(0, 0, 0)); //设置背景颜色为黑色
    wc.lpszMenuName = NULL;
    wc.lpszClassName = szAppName;

    if (!RegisterClass(&wc)) {
        MessageBox(NULL, TEXT("程序只能在Windows NT下运行"), szAppName, MB_ICONERROR);
        return 0;
    }

    hwnd = CreateWindow(szAppName, //要注册的窗口类名
        TEXT("c"), //窗口标题
        WS_OVERLAPPEDWINDOW, //窗口样式
        CW_USEDEFAULT, //窗口距离屏幕左上角的横坐标
        CW_USEDEFAULT, //窗口距离屏幕左上角的纵坐标
        1000, //窗口宽度
        800, //窗口高度
        NULL, //父窗口句柄
        NULL, //窗口菜单句柄
        hInstance, //当前实例句柄
        NULL); //附加数据

    if (hwnd == NULL) {
        MessageBox(NULL, TEXT("无法创建窗口"), szAppName, MB_ICONERROR);
        return 0;
    }

    ShowWindow(hwnd, iCmdShow);
    UpdateWindow(hwnd);

    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
    HDC hdc;
    PAINTSTRUCT ps;
    BITMAP bm;
    HDC hdcMem;
    static int cxClient, cyClient;
    static int cxSource, cySource;
    static int xClient, yClient;
    static BITMAPINFOHEADER bi;

    switch (message) {
    case WM_CREATE:
        hdc = GetDC(hwnd);

        hbmpBack = (HBITMAP)LoadImage(NULL, TEXT("C:\\Users\\YourUsername\\Desktop\\t\\a.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);

        GetObject(hbmpBack, sizeof(BITMAP), (LPSTR)&bm);

        cxSource = bm.bmWidth;
        cySource = bm.bmHeight;

        ReleaseDC(hwnd, hdc);
        return 0;

    case WM_SIZE:
        cxClient = LOWORD(lParam);
        cyClient = HIWORD(lParam);

        xClient = (cxClient - cxSource) / 2;
        yClient = (cyClient - cySource) / 2;

        return 0;

    case WM_PAINT:
        hdc = BeginPaint(hwnd, &ps);

        hdcMem = CreateCompatibleDC(hdc);
        SelectObject(hdcMem, hbmpBack);

        bi.biSize = sizeof(BITMAPINFOHEADER);
        bi.biWidth = cxSource;
        bi.biHeight = cySource;
        bi.biPlanes = 1;
        bi.biBitCount = 24;
        bi.biCompression = BI_RGB;
        bi.biSizeImage = 0;
        bi.biXPelsPerMeter = 0;
        bi.biYPelsPerMeter = 0;
        bi.biClrUsed = 0;
        bi.biClrImportant = 0;

        SetStretchBltMode(hdc, HALFTONE);
        StretchDIBits(hdc, xClient, yClient, cxSource, cySource, 0, 0, cxSource, cySource, (LPSTR)&bi, (LPBYTE)bm.bmBits, DIB_RGB_COLORS, SRCCOPY);

        DeleteDC(hdcMem);
        EndPaint(hwnd, &ps);
        return 0;

    case WM_DESTROY:
        DeleteObject(hbmpBack);
        PostQuitMessage(0);
        return 0;
    }

    return DefWindowProc(hwnd, message, wParam, lParam);
}

请注意,你需要将 C:\\Users\\YourUsername\\Desktop\\t\\a.bmp 中的 YourUsername 修改为你的用户名,并确保位图文件保存在正确的文件夹中。

希望对你有帮助!如果还有其他问题,请随时提问。



【相关推荐】



如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^

log一下看看报什么错?

结合GPT给出回答如下请题主参考
首先需要使用Windows GDI API加载BMP图像,可以使用以下代码实现:

HBITMAP hBmp = (HBITMAP)LoadImage(NULL, L"C:\\Users\\user\\Desktop\\t\\a.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);

其中,LoadImage函数用于加载BMP图像,其参数依次为:NULL表示不使用模块句柄;BMP图像的完整路径;图像类型为IMAGE_BITMAP;图像的宽度和高度为0表示使用原图像大小;LR_LOADFROMFILE表示从文件加载。

接下来需要将BMP图像绘制到窗口上,可以在WM_PAINT消息的处理函数中使用以下代码实现:

PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
HDC hdcMem = CreateCompatibleDC(hdc);
SelectObject(hdcMem, hBmp);
BITMAP bmp;
GetObject(hBmp, sizeof(bmp), &bmp);
BitBlt(hdc, 0, 0, bmp.bmWidth, bmp.bmHeight, hdcMem, 0, 0, SRCCOPY);
DeleteDC(hdcMem);
EndPaint(hwnd, &ps);

其中,BeginPaint函数用于开始绘制窗口,并返回设备上下文句柄hdcCreateCompatibleDC函数创建一个与窗口设备相关的内存设备上下文句柄hdcMemSelectObject函数将BMP图像选入内存设备上下文;GetObject函数获取BMP图像的信息;BitBlt函数将BMP图像从内存设备上下文复制到窗口上;DeleteDC函数释放内存设备上下文;EndPaint函数结束绘制窗口。

完整的窗口程序如下:

#include <windows.h>

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    switch (msg) {
        case WM_PAINT:
            {
                PAINTSTRUCT ps;
                HDC hdc = BeginPaint(hwnd, &ps);
                HDC hdcMem = CreateCompatibleDC(hdc);
                HBITMAP hBmp = (HBITMAP)LoadImage(NULL, L"C:\\Users\\user\\Desktop\\t\\a.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
                SelectObject(hdcMem, hBmp);
                BITMAP bmp;
                GetObject(hBmp, sizeof(bmp), &bmp);
                BitBlt(hdc, 0, 0, bmp.bmWidth, bmp.bmHeight, hdcMem, 0, 0, SRCCOPY);
                DeleteDC(hdcMem);
                EndPaint(hwnd, &ps);
            }
            break;
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    WNDCLASS wc = {0};
    wc.lpfnWndProc = WndProc;
    wc.hInstance = hInstance;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.lpszClassName = L"MyWindowClass";

    if (!RegisterClass(&wc)) {
        MessageBox(NULL, L"Window Registration Failed!", L"Error", MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }

    HWND hwnd = CreateWindow(L"MyWindowClass", L"Window Title", WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 640, 480, NULL, NULL, hInstance, NULL);

    MSG msg = {0};
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return msg.wParam;
}

注意,需要将BMP图像路径替换成实际路径。同时,建议在程序结束之前释放BMP图像资源,可以在WM_DESTROY消息的处理函数中使用以下代码实现:

case WM_DESTROY:
    {
        HBITMAP hBmp = (HBITMAP)LoadImage(NULL, L"C:\\Users\\user\\Desktop\\t\\a.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
        DeleteObject(hBmp);
        PostQuitMessage(0);
    }
    break;

LoadImage这句代码放到CreateWindow之前就可以了

在 LoadImage 函数中,确保文件路径是正确的。您提供的路径是 "C:\Users\Administrator\Desktop\t\a.bmp",请确保该路径下确实存在 a.bmp 文件。如果文件不存在或路径错误,图片将无法加载。