GDI - Why the printing StartPage() function works in 32 bit but raises an exception in 64 bit?

23 views Asked by At

In Visual Studio 2019, I use the below code to print a jpeg image into a PDF document:

// sdk
#include <string>
#include <memory>

// windows
#include <Windows.h>
#include <Winspool.h>
#include <gdiplus.h>
#pragma comment (lib, "Gdiplus.lib")

// resources
#include "Resource.h"

//------------------------------------------------------------------------------
HWND g_hWnd      = nullptr;
HWND g_hPrintBtn = nullptr;
//------------------------------------------------------------------------------
void PrintToPDF();
//------------------------------------------------------------------------------
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
        case WM_CLOSE:
            PostQuitMessage(0);
            break;

        case WM_DESTROY:
            return 0;

        case WM_COMMAND:
            // search for the pressed button
            if (g_hPrintBtn == reinterpret_cast<HWND>(lParam))
                PrintToPDF();

            break;

        default:
            return DefWindowProc(hWnd, uMsg, wParam, lParam);
    }

    return 0;
}
//------------------------------------------------------------------------------
HWND CreateButton(const RECT& clientRect, const std::wstring& caption, LONG pos)
{
    // create button
    return ::CreateWindow(L"BUTTON",
                          caption.c_str(),
                          WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
                          clientRect.left + 3,
                          clientRect.bottom - ((25 * pos) + 3),
                          (clientRect.right - clientRect.left) - 6,
                          25,
                          g_hWnd,
                          nullptr,
                          (HINSTANCE)GetWindowLongPtr(g_hWnd, GWLP_HINSTANCE),
                          nullptr);
}
//---------------------------------------------------------------------------
HBITMAP LoadJpgImage(const std::wstring& fileName)
{
    HBITMAP hBitmap = nullptr;

    // initialize GDI+
    Gdiplus::GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR                    gdiplusToken;

    Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);

    // load the .jpg image
    std::unique_ptr<Gdiplus::Bitmap> pBitmap = std::make_unique<Gdiplus::Bitmap>(fileName.c_str());

    // check if the image is loaded successfully
    if (pBitmap->GetLastStatus() == Gdiplus::Ok)
    {
        Gdiplus::Status result = pBitmap->GetHBITMAP(Gdiplus::Color::Transparent, &hBitmap);

        // free resources
        pBitmap.reset();
    }
    else
        // error handling if image loading fails
        ::MessageBoxW(nullptr, L"Failed to load image.", L"Error", MB_OK | MB_ICONERROR);

    // shutdown GDI+
    Gdiplus::GdiplusShutdown(gdiplusToken);

    return hBitmap;
}
//------------------------------------------------------------------------------
HDC GetPrinterDC(const std::wstring& printerName)
{
    HDC hDC = ::CreateDC(NULL, printerName.c_str(), NULL, NULL);

    if (!hDC)
        return nullptr;

    return hDC;
}
//------------------------------------------------------------------------------
void PrintToPDF()
{
    HBITMAP hBitmap = LoadJpgImage(L"..\\PrintToPDF\\Resources\\Coffee.jpg");

    BITMAP bm;
    ::GetObject(hBitmap, sizeof(BITMAP), &bm);

    const std::size_t bmWidth  = bm.bmWidth;
    const std::size_t bmHeight = bm.bmHeight;

    HDC hDC = GetPrinterDC(L"Microsoft Print to PDF");

    const std::size_t cxPage = ::GetDeviceCaps(hDC, HORZRES);
    const std::size_t cyPage = ::GetDeviceCaps(hDC, VERTRES);

    ::DOCINFO di;
    di.cbSize      = sizeof(DOCINFO);
    di.lpszDocName = L"INVOICE TABLE : Printing...";
    di.lpszOutput  = L"..\\PrintToPDF\\Printed docs\\MyDoc.pdf";

    ::StartDoc(hDC, &di);
    ::StartPage(hDC);

    HDC hDcMem = ::CreateCompatibleDC(hDC);
    ::SelectObject(hDcMem, hBitmap);

    ::BitBlt(hDC, (int)((cxPage - bmWidth) / 2), (int)((cyPage - bmHeight) / 2), 4032, 3024, hDcMem, 0, 0, SRCCOPY);

    ::DeleteDC(hDcMem);

    ::EndPage(hDC);
    ::EndDoc(hDC);

    ::DeleteDC(hDC);
}
//------------------------------------------------------------------------------
int APIENTRY wWinMain(_In_     HINSTANCE hInstance,
    _In_opt_ HINSTANCE hPrevInstance,
    _In_     LPWSTR    lpCmdLine,
    _In_     int       nCmdShow)
{
    WNDCLASSEX wcex  = {};
    MSG        msg   = {};
    BOOL       bQuit = FALSE;

    // register the window class
    wcex.cbSize        = sizeof(WNDCLASSEX);
    wcex.style         = CS_OWNDC;
    wcex.lpfnWndProc   = WindowProc;
    wcex.cbClsExtra    = 0;
    wcex.cbWndExtra    = 0;
    wcex.hInstance     = hInstance;
    wcex.hIcon         = ::LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPICON));
    wcex.hIconSm       = ::LoadIcon(hInstance, MAKEINTRESOURCE(IDI_SMALL));
    wcex.hCursor       = ::LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)::GetStockObject(BLACK_BRUSH);
    wcex.lpszMenuName  = NULL;
    wcex.lpszClassName = L"PrintToPDF";

    if (!::RegisterClassEx(&wcex))
        return 0;

    // create the main window
    g_hWnd = ::CreateWindowEx(0,
                              L"PrintToPDF",
                              L"Print to PDF",
                              WS_OVERLAPPEDWINDOW,
                              CW_USEDEFAULT,
                              CW_USEDEFAULT,
                              800,
                              650,
                              NULL,
                              NULL,
                              hInstance,
                              NULL);

    ::ShowWindow(g_hWnd, nCmdShow);

    RECT clientRect;
    ::GetClientRect(g_hWnd, &clientRect);

    g_hPrintBtn = CreateButton(clientRect, L"Print to PDF", 1);

    // application main loop
    while (!bQuit)
    {
        // check for messages
        if (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            // handle or dispatch messages
            if (msg.message == WM_QUIT)
                bQuit = TRUE;
            else
            {
                ::TranslateMessage(&msg);
                ::DispatchMessage(&msg);
            }
        }
        else
        {
            ::Sleep(10);
        }
    }

    // destroy the window explicitly
    ::DestroyWindow(g_hWnd);

    return (int)msg.wParam;
}
//------------------------------------------------------------------------------

This above code works perfectly as long as I compile and execute it in 32 bit (x86). However the exact same code raises the below exception if I compile and execute it in 64 bit (x64):

enter image description here

As seen above, the exception happens while the ::StartDoc() function is executed. If I continue the execution (by pressing F5), the image is printed in the document, as expected, and no other error happen.

I cannot figure out why. Can someone point me what I'm doing wrong?

-------- EDIT 1 --------

It seems that it's a old known issue:

https://github.com/wxWidgets/wxWidgets/issues/23850

Print.PrintSupport.Source.dll Exception(1) Element Not Found Calling CDC::StartDoc

Unfortunately these posts are quite old (more than 1 year). As it seems that this is a Visual Studio debugger issue instead of a real one, should I ignore this issue, or maybe someone know a solution?

0

There are 0 answers