Microsoft Print to PDF で C/C++ コードから出力ファイル名を指定する方法

Windows 10 標準装備の仮想プリンタ Microsoft Print to PDF ですが、作成されるPDFファイル名をアプリケーションプログラムのコードから指定できないか探していたところ、以下を見つけました。

How to programmatically print to PDF file without prompting for filename in C# using the Microsoft Print To PDF printer that comes with Windows 10

https://stackoverflow.com/questions/31896952/how-to-programmatically-print-to-pdf-file-without-prompting-for-filename-in-c-sh

C/C++ から Windwos API を直接呼び出している場合は、CreateDC の第3パラメータ DOCINFO 構造体の3つ目のメンバ lpszOutput にデフォルトの "PORTPROMPT:" ではなく出力したいファイル名を指定すればいいです。

    DOCINFO di = { sizeof(di), "テストDocName" };
    di.lpszOutput = "C:\\HOGE\\HOGEHOGE.PDF";

OWLNext の場合は、TPrinter インスタンスメンバ関数 GetData を呼び出して取得する TPrintDialog::TData クラスインスタンスに SetDevNames で設定するときの第3パラメータに、出力したいファイル名を指定すればいいです。OWLNext はそのデータを元に TCreatedDC::TCreatedDC にて、Windows API 関数 CreateDC を呼び出すようになっています。

    TPrinter printer;
    printer.GetData()->SetDevNames("winspool", "Microsoft Print to PDF", "C:\\HOGE\\HOGEHOGE.PDF");


VC141 + OWLNext6.44.2 で動作するサンプルコードができましたので載せておきます。

// <pdf.cpp>
// Copyright 2018 Perce-neige https://perce-neige.hateblo.jp/
// Copyright を削除しない限り、ご自由にお使いください

#define _USE_MATH_DEFINES       // <cmath> にて M_PI を定義してもらう
#define _OWLPCH

#include <owl/pch.h>
#pragma hdrstop

#include <owl/printer.h>        // owl::TPrintout
#include <owl/printdia.h>       // owl::TPrintDialog
#include <owl/applicat.h>       // owl::TApplication
#include <owl/framewin.h>       // owl::TFrameWindow
#include <owl/menu.h>           // owl::TMenu

#include <windows.h>

#include <algorithm>            // std::min
#include <cmath>                // std::sin
#include <memory>               // std::unique_ptr
#include <string>               // std::string

#include "pdf.rh" // リソースID定義

class TMyPrintOut : public owl::TPrintout {
public:
    TMyPrintOut(const char* title) : owl::TPrintout(title) {}

    // 1ページだけ
    void GetDialogInfo(int& minPage, int& maxPage, int& selFromPage, int& selToPage)
        { minPage = maxPage = selFromPage = selToPage = 1; }
    bool HasPage(int pageNumber) { return pageNumber == 1; }
    
    void BeginPage(owl::TRect& clientR);
    void PrintPage(int page, owl::TRect& bandRect, unsigned flags);

};

void TMyPrintOut::BeginPage(owl::TRect& clientRect)
{
    DC->SetMapMode(MM_ANISOTROPIC);
    DC->SetViewportExt(PageSize);
    // 印字可能サイズ [mm]
    owl::TSize printableSize(DC->GetDeviceCaps(HORZSIZE), DC->GetDeviceCaps(VERTSIZE));
    // スケーリング: これで以降論理単位が 0.1mm となる
    owl::TSize windowExtt(printableSize.cx * 10, printableSize.cy * 10);
    DC->SetWindowExt(windowExtt);
    //
    clientRect.left = clientRect.top = 0;
    clientRect.right = clientRect.left + windowExtt.cx;
    clientRect.bottom = clientRect.top + windowExtt.cy;
}

// サンプルのページ描画
void TMyPrintOut::PrintPage(int /*page*/, owl::TRect& /*bandRect*/, unsigned /*flags*/)
{
    owl::TRect clientRect;
    BeginPage(clientRect);
    DC->MoveTo(clientRect.left, clientRect.bottom / 2);
    DC->LineTo(clientRect.right, clientRect.bottom / 2);
    DC->MoveTo(clientRect.right / 2, clientRect.bottom);
    DC->LineTo(clientRect.right / 2, clientRect.top);
    double r = std::min(clientRect.right, clientRect.bottom) / 4;
    // カージオイド を (3/2)π 回転したもの
    // 媒介変数 t を -π <= t <= π で動かす
    int n = int(M_PI * r);
    for (int i = -n; i <= n; i++) {
        double t = i * M_PI / n;
        double c = std::cos(t);
        double s = std::sin(t);
        int dx = int(r * (1 + c) * (-s));
        int dy = int(r * (1 + c) * c);
        int x = int(clientRect.right  / 2 + dx);
        int y = int(clientRect.bottom / 2 + dy);
        if (i == -n)
            DC->MoveTo(x, y);
        else
            DC->LineTo(x, y);
    }
}

class TMyFrame : public owl::TFrameWindow {
public:
    TMyFrame(const char* name) : owl::TFrameWindow(0, name) {}

private:
    void CmOutputPDF();
    
    DECLARE_RESPONSE_TABLE(TMyFrame);
};

DEFINE_RESPONSE_TABLE1(TMyFrame, owl::TFrameWindow)
    EV_COMMAND(CM_OUTPUTPDF, CmOutputPDF),
END_RESPONSE_TABLE;

void TMyFrame::CmOutputPDF()
{
    try {
        std::string driver = "winspool";
        std::string device = "Microsoft Print to PDF";
        std::string port = "C:\\WORK\\PDFOWL.PDF"; // ### 出力PDFファイル名
        //
        owl::TPrinter printer;
        printer.GetData()->SetDevNames(driver.c_str(), device.c_str(), port.c_str());
        TMyPrintOut printOut("PDF出力テスト");
        printer.Print(this, printOut, false);
    }
    catch (const std::exception& x) {
        MessageBox(x.what(), "エラー", MB_OK | MB_ICONERROR);
    }
    catch (...) {
        MessageBox("想定外の例外です。", "エラー", MB_OK | MB_ICONERROR);
    }
}

class TMyApp : public owl::TApplication {
public:
    TMyApp(const char* title) : owl::TApplication(title), Title(title) {}

private:
    void InitMainWindow() {
        std::unique_ptr<owl::TFrameWindow> mainWindow(new TMyFrame(Title.c_str()));
        mainWindow->SetMenuDescr(owl::TMenuDescr(MAIN_MENU, 1, 0, 0, 0, 0, 0));
        SetMainWindow(mainWindow.release());
    }

    std::string Title;

};

int OwlMain(int /*argc*/, char** /*argv*/)
{
    TMyApp("PDF出力テスト").Run();
    return 0;
}
// <pdf.rh>

#define MAIN_MENU       10000
#define CM_OUTPUTPDF    10100
// <pdf.rc>

#include "pdf.rh"

#include <owl/window.rh>
#include <owl/except.rc>
#include <owl/printer.rc>

MAIN_MENU MENU {
          POPUP "ファイル(&F)"
          {
                MENUITEM "PDF出力(&P)", CM_OUTPUTPDF
                MENUITEM "終了(&X)", CM_EXIT
          }
}

CMakeLists.txt は https://perce-neige.hateblo.jp/entry/2018/10/25/214023 にあります。