Examples / hello_world / activex_cpp

Hello World

A complete, runnable activex_cpp program — 116 lines, shipped in your download.

Demonstrates Getting Started

Same example, other languages: c cpp cpp_linux csharp delphi php python vbnet

examples/activex_cpp/hello_world/hello_world.cpp 116 lines
// ============================================================================
//  hello_world -- C++ ActiveX (COM) port.
//
//  WHY THIS TREE EXISTS, AND WHY IT IS NOT examples/activex/
//  ---------------------------------------------------------
//  examples/activex/ already holds this example as VBScript. Both the .vbs and
//  a .cpp beside it would write the SAME out.pdf in the SAME directory, so
//  whichever ran last would be the one graded, and the comparer -- which keys
//  on the path relative to its language root -- could not tell the two ports
//  apart. A sibling language tree is what tools/compare_lang_outputs.py already
//  understands: it walks examples/<lang>/** and diffs each artifact against
//  examples/delphi/**, so this tree is gradeable with no change to that script.
//
//  WHAT IS DIFFERENT FROM examples/cpp/
//  ------------------------------------
//  examples/cpp/hello_world calls the FLAT C API: pdfSetFontA(pdf, ...) with an
//  explicit PPDF handle. This calls the SAME engine through the COM object the
//  VBScript port uses -- CreateObject("LumasPdf.PDF") -- so the handle becomes
//  the `this` of an interface pointer and the pdf* prefix disappears. That is
//  the whole point: it exercises wrappers/activex, which until now no compiled
//  language exercised at all.
//
//  #import, NOT late-bound IDispatch. The type library is right there
//  (wrappers/activex/LumasPdfAX.tlb) and #import turns it into typed smart
//  pointers, so a wrong argument count or type is a COMPILE error. A late-bound
//  Invoke() harness would push every one of those into a runtime E_INVALIDARG
//  with no indication of which call was wrong -- across 80+ generated files
//  that is the difference between a build log and an afternoon.
//
//  NOTE ON RaiseExceptions: the .vbs sets it so engine errors surface as script
//  errors. #import's generated wrappers already throw _com_error on a failed
//  HRESULT, so the same setting gives the same behaviour here, and the catch
//  below reports it rather than letting a failure look like a clean exit.
// ============================================================================
#import "..\\..\\..\\wrappers\\activex\\LumasPdfAX.tlb" no_namespace named_guids

#include <windows.h>
#include <cstdio>
#include <string>
#include <ctime>

// The output goes beside the EXECUTABLE, exactly as every other port does it,
// so the comparer finds it at the same relative path as delphi's.
static std::string exeDir() {
    char buf[MAX_PATH];
    GetModuleFileNameA(nullptr, buf, MAX_PATH);
    std::string s(buf);
    size_t p = s.find_last_of("\\/");
    return p == std::string::npos ? std::string(".") : s.substr(0, p);
}

// The Delphi original writes DateTimeToStr(Date + Time) and the .vbs writes
// CStr(Now); both render with the SYSTEM short-date + long-time patterns. Read
// the same two locale values rather than inventing a strftime layout -- the
// comparer diffs whitespace-separated word counts, so dropping the AM/PM
// designator would report CONTENT_DIFF for a reason unrelated to the engine.
// (Same finding, same fix, as examples/cpp/hello_world/hello_world.cpp.)
static std::string nowLikeDelphi() {
    char d[128] = {0}, t[128] = {0};
    GetDateFormatA(LOCALE_USER_DEFAULT, DATE_SHORTDATE, nullptr, nullptr, d, sizeof(d));
    GetTimeFormatA(LOCALE_USER_DEFAULT, 0, nullptr, nullptr, t, sizeof(t));
    return std::string(d) + " " + t;
}

int main() {
    HRESULT hr = CoInitialize(nullptr);
    if (FAILED(hr)) { std::fprintf(stderr, "CoInitialize failed: 0x%08lx\n", (unsigned long)hr); return 1; }
    int rc = 0;
    try {
        const std::string outFile = exeDir() + "\\out.pdf";

        // ILumasPDF, not IPDF: the type library names the default interface
        // ILumasPDF (see the generated LumasPdfAX.tlh), and #import derives the
        // smart pointer name from that. The .tlh also declares
        // ILumasPDFContentParser / ILumasPDFReport / ILumasPDFReportJob -- so
        // the psr* and rpt* examples are NOT unportable as a first scan of the
        // .ridl suggested; they simply hang off secondary interfaces rather
        // than off this one.
        ILumasPDFPtr pdf;
        hr = pdf.CreateInstance(L"LumasPdf.PDF");
        if (FAILED(hr)) _com_issue_error(hr);

        pdf->RaiseExceptions = VARIANT_TRUE;

        pdf->CreateNewPDFA("");                              // output opened later
        pdf->SetDocInfoA(1, "Delphi Example project");       // diCreator
        pdf->SetDocInfoA(5, "My first PDF output");          // diTitle

        pdf->Append();
        pdf->SetFontA("Arial", 1, 30, VARIANT_TRUE, 2);      // fsItalic, cp1252
        const std::string txt = "My first PDF output...\r\r" + nowLikeDelphi();
        pdf->WriteFTextA(1, txt.c_str());                    // taCenter
        pdf->EndPage();

        if (pdf->HaveOpenDoc() != 0)
            pdf->OpenOutputFileA(outFile.c_str());
        if (pdf->CloseFile() != 0)
            std::printf("OK: %s\n", outFile.c_str());
    } catch (const _com_error& e) {
        // ErrorMessage() returns TCHAR*, which is char* in this (non-UNICODE)
        // build -- mixing it with %ws is a type error, not a formatting nicety.
        // Take the BSTR description when there is one and fall back to the
        // narrow system message otherwise.
        const _bstr_t d = e.Description();
        if (d.length())
            std::fprintf(stderr, "COM error 0x%08lx: %ws\n",
                         (unsigned long)e.Error(), (const wchar_t*)d);
        else
            std::fprintf(stderr, "COM error 0x%08lx: %s\n",
                         (unsigned long)e.Error(), e.ErrorMessage());
        rc = 1;
    }
    CoUninitialize();
    return rc;
}

This file is in the SDK at examples/activex_cpp/hello_world/hello_world.cpp. The build fails if this page and that file ever differ.