GDI+ - 无法使用Gdiplus :: Graphics执行任何操作(C ++)

3

我对GDI+不熟悉,也不知道问题出在哪里,所以我将所有代码都贴出来。我试图简单地绘制一张图片,但是在我的调试器中,我可以看到我尝试在WM_PAINT中使用的图形是NULL。我看到很多人做了和我尝试做的几乎完全相同的事情,所以我很困惑到底发生了什么。

#include "stdafx.h"
#include "GUI.h"
#include <objidl.h>
#include <gdiplus.h>
#include <iostream>

using namespace Gdiplus;
#pragma comment (lib,"Gdiplus.lib")

#define MAX_LOADSTRING 100

// Global Variables:
HINSTANCE hInst;                                // current instance
WCHAR szTitle[MAX_LOADSTRING];                  // The title bar text
WCHAR szWindowClass[MAX_LOADSTRING];            // the main window class name

// Forward declarations of functions included in this code module:
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);


    // Initialize global strings
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_GUI, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    // Perform application initialization:
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }

    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_GUI));

    MSG msg;

    // Main message loop:
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return (int) msg.wParam;
}

ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_GUI));
    wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_GUI);
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassExW(&wcex);
}

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance; // Store instance handle in our global variable

   HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);

   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    PAINTSTRUCT ps;
    HDC hdc;
    switch (message)
    {
    case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
            // Parse the menu selections:
            switch (wmId)
            {
            case IDM_ABOUT:
                DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                break;
            case IDM_EXIT:
                DestroyWindow(hWnd);
                break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;
    case WM_PAINT:
        {
            hdc = BeginPaint(hWnd, &ps);
            Graphics graphics(hdc);
            Image image(L"C:\\light.png");
            graphics.DrawImage(&image, 0, 0);

            EndPaint(hWnd, &ps);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}

1
你应该同时发布错误消息。另外,似乎缺少了gdi启动/关闭的部分。 - user7860670
没有错误消息。问题是我期望图片显示在窗口中,但窗口完全为空白。 - Johan Karlsson
2
代码应该会失败,因为忘记初始化库。请查看此帖子以获取样板代码。建议使用Image::FromFile()而不是Image构造函数,这样您可以诊断失败并利用assert()。 - Hans Passant
太棒了,这个方法起作用了。谢谢你。 - Johan Karlsson
1个回答

5
您缺少对 GdiplusStartup()GdiplusShutdown() 的调用。
GdiplusStartup 的参考文献中可以得知:
必须在创建任何 GDI+ 对象之前调用 GdiplusStartup,并且必须在调用 GdiplusShutdown 之前删除所有 GDI+ 对象(或使它们超出作用域)。
... 而从 GdiplusShutdown 中也可以看到: GdiplusShutdown 函数清理 Windows GDI+ 使用的资源。每次调用 GdiplusStartup 都应该与一次调用 GdiplusShutdown 配对。
我正在使用一个 RAII 类来简化任务,用法如下:
class GdiPlusInit
{
public:
    GdiPlusInit()
    {
        Gdiplus::GdiplusStartupInput startupInput;
        Gdiplus::GdiplusStartup( &m_token, &startupInput, NULL );
        // NOTE: For brevity I omitted error handling, check function return value!
    }

    ~GdiPlusInit()
    {
        if( m_token )
            Gdiplus::GdiplusShutdown( m_token );
    }

    // Class is non-copyable.
    GdiPlusInit( const GdiPlusInit& ) = delete;
    GdiPlusInit& operator=( const GdiPlusInit& ) = delete;

private:
    ULONG_PTR m_token = 0;
};

用法:

在您想使用GDI +函数的范围开头创建该类的实例(出于性能原因,我不会在经常调用的函数中这样做)。我通常将其创建为窗口类或其他使用GDI +的类的成员变量,以便我的代码客户端不需要被告知初始化GDI +。如果客户端已经自行调用了GdiplusStartup()GdiplusShutdown(),那么也没关系,因为这些调用可以嵌套,只要它们正确配对即可。

在您的情况下:

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    GdiPlusInit gdiplus;  // calls GdiplusStartup() and stores the returned token

    // ... remaining code of your application ... 

    // When the scope ends, the destructor of GdiPlusInit calls GdiplusShutdown(),
    // passing the stored token.
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接