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

GDI中StartDoc函数32位正常,64位抛异常问题咨询

64位编译下调用StartDoc触发异常但程序仍能正常运行的问题

在Visual Studio 2019环境下,我使用以下C++代码将JPEG图片打印到PDF文档:

// 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;
}
//------------------------------------------------------------------------------

上述代码在32位(x86)编译执行时完全正常,但在64位(x64)编译执行时会抛出如下异常:

异常截图

如截图所示,异常在调用::StartDoc()函数时触发。若继续执行程序(按下F5),图片仍能按预期打印到PDF文档,且无其他错误。

我无法找出原因,能否有人指出我哪里出错了?


编辑1

这是一个已知的旧问题,相关反馈显示这是调试器捕获的驱动内部异常,而非代码本身问题。我是否应忽略该问题,或是有已知的解决方案?


解决方案

这个异常确实是Microsoft Print to PDF驱动在64位调试环境下的兼容性问题,你的代码本身没有错误:

  1. 该异常是调试器捕获到的驱动内部错误,不会影响程序的实际运行逻辑,PDF能正常生成就是直接证明。
  2. 可以通过以下方式解决调试时的异常提示:
    • 在Visual Studio的「调试」→「窗口」→「异常设置」中,找到Win32异常类别,勾选"Element not found"(错误代码0x80070490),或者直接添加规则忽略Print.PrintSupport.Source.dll抛出的异常。
    • 升级Visual Studio到更高版本(比如2022),微软后续版本可能修复了调试器与该打印驱动的兼容性问题。
    • 如果不需要调试打印流程,直接切换到Release模式编译运行,调试器不会触发该异常提示。

内容的提问来源于stack exchange,提问作者Jean-Milost Reymond

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 00:15:55