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):
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?
