Articol tehnic

Crearea și eliberarea dinamică a componentei HotPDF în C++Builder

A plasa un THotPDF pe un formular în timpul proiectării este suficient de bun pentru un prototip rapid, dar leagă componenta de durata de viață a formularului, ceea ce rareori este ceea ce își dorește codul de producție. Un generator de rapoarte care rulează o dată la fiecare clic de buton, un fir de execuție de serviciu care grupează exporturi peste noapte, o clasă auxiliară care nu are niciun formular: în fiecare dintre aceste situații doriți ca componenta să existe exact pe durata unei singure lucrări PDF și apoi să dispară. Asta înseamnă alocare la runtime și schimbă două lucruri pe care merită să le înțelegeți înainte de a scrie prima linie: cine deține obiectul și cum rulează curățarea atunci când ceva merge greșit

Semantica proprietarului în VCL

Fiecare constructor de componentă VCL primește un parametru Owner de tipul TComponent*. Transmiterea lui this (formularul) înregistrează noul obiect în lista de componente deținute a formularului, astfel încât, dacă formularul este distrus în timp ce componenta este încă activă, VCL o eliberează automat. Transmiterea lui nullptr înseamnă fără proprietar: vă asumați singura responsabilitate pentru pointer, iar nimic nu îl va curăța pentru dumneavoastră dacă o excepție derulează stiva înainte de delete-ul dumneavoastră explicit

Pentru un export unic care se finalizează într-o singură funcție, ambele variante funcționează, dar cele două au moduri de eșec diferite. Cu this ca proprietar, o scurgere de memorie este imposibilă atât timp cât formularul se închide în cele din urmă; cu nullptr, pointerul trebuie să ajungă într-un bloc __finally. În practică, modelul nullptr plus __finally este ușor mai curat pentru obiectele cu durată scurtă de viață, deoarece face vizibilă dintr-o privire granița duratei de viață și evită acumularea de către formular a unor obiecte deținute care erau menite să fie temporare

Cronologia unui ciclu de viață THotPDF dinamic în C++Builder, de la new THotPDF(nullptr) prin setarea opțiunilor, BeginDoc și EndDoc, până la delete Pdf într-un bloc __finally care rulează chiar și când o excepție e ridicată
Un THotPDF de runtime trăiește exact un singur job PDF: stabilește opțiunile înainte de BeginDoc, desenează, închide, și lasă blocul __finally să emită delete indiferent de excepții

Structură sigură la excepții

Generarea PDF-ului poate eșua din motive care nu au nimic de-a face cu API-ul: directorul de ieșire este doar pentru citire, lipsește un fișier de font, un flux se golește prematur sau datele furnizate de apelant ating o limită de lungime. Indiferent de cauză, calea de curățare trebuie să ruleze. Modul idiomatic din C++Builder de a garanta acest lucru este try/__finally:

#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
#pragma package(smart_init)
#pragma link "HPDFDoc"
#pragma resource "*.dfm"

TForm1 *Form1;

__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
}

void __fastcall TForm1::Button1Click(TObject *Sender)
{
    THotPDF* Pdf = new THotPDF(nullptr);
    try
    {
        Pdf->FileName = "output.pdf";
        Pdf->Compression = cmFlateDecode;
        Pdf->FontEmbedding = true;
        Pdf->BeginDoc();
        Pdf->CurrentPage->SetFont("Arial", TFontStyles(), 12);
        Pdf->CurrentPage->TextOut(72, 720, 0, L"Hello from C++Builder");
        Pdf->EndDoc();
    }
    __finally
    {
        delete Pdf;
    }
}

Câteva lucruri din acest listing merită menționate. Proprietarul este nullptr, ceea ce face explicită durata de viață. Compression și FontEmbedding sunt setate înainte de BeginDoc: ambele sunt opțiuni la nivel de document pe care HotPDF le confirmă atunci când documentul se deschide, iar atribuirea lor ulterioară nu are niciun efect. TextOut primește coordonate în puncte măsurate din colțul din stânga jos al paginii, cu Y crescând în sus; perechea 72, 720 plasează textul lângă colțul din stânga sus al unei pagini de format letter, cu o margine stângă de un inch. delete Pdf din blocul __finally rulează indiferent dacă BeginDoc, desenarea sau EndDoc a generat o excepție sau nu

Evitați să apelați orice metodă pe Pdf după delete. Dacă pointerul este stocat într-o variabilă membru, setați-l la nullptr imediat după eliminare, astfel încât orice acces accidental ulterior să producă o cădere curată în loc de o corupere silențioasă

Configurarea proiectului

C++Builder localizează THotPDF printr-o combinație de căi de includere, căi de bibliotecă și o directivă pragma. Header-ul generat se află alături de HPDFDoc.pas în directorul sursă HotPDF; adăugați acel director la Project > Options > C++ Compiler > Include path. Directiva #pragma link "HPDFDoc" îi spune linker-ului să includă unitatea compilată fără a o lista manual în fișierul proiectului. Dacă folosiți pachetul runtime în loc de linkare statică, instalați mai întâi pachetele de proiectare și runtime HotPDF; pragma se aplică în continuare

Păstrați neschimbat numele unității HPDFDoc. C++Builder derivă numele header-ului din numele unității Pascal, astfel încât redenumirea fișierului sau folosirea unui alias de cale în pragma întrerupe silențios căutarea

Domeniul de vizibilitate și lucrările cu mai multe documente

Pentru un singur export declanșat de o acțiune a utilizatorului, o variabilă locală cu domeniul de vizibilitate limitat la handler-ul de buton este răspunsul corect: este creată, folosită și distrusă în cadrul unui singur cadru de apel, iar intenția este evidentă pentru oricine citește codul mai târziu. Alternativa la momentul proiectării este justificată atunci când același formular conduce un flux de lucru continuu, cum ar fi un panou de previzualizare a tipăririi care reconstruiește documentul de fiecare dată când utilizatorul modifică o setare; în acest caz, menținerea componentei active și apelarea repetată a BeginDoc/EndDoc este mai puțin disruptivă decât alocarea și eliberarea repetată a unor obiecte pe heap

Pentru lucrările în lot care produc multe documente în succesiune, limitarea unui THotPDF la un singur document merită costul suplimentar de alocare. Starea nu se transmite între documente dacă nu există niciun obiect care să o transporte, iar aceasta este o clasă de bug intermitent pe care nu va trebui niciodată să o depanați. Alocați, generați, ștergeți, repetați

O proprietate care apare în mai multe demo-uri HotPDF este AutoLaunch, care deschide fișierul generat în vizualizatorul PDF implicit al sistemului imediat după EndDoc. Este utilă în timp ce scrieți prima variantă a unui layout. În producție, evitați-o: deschideți explicit calea de ieșire, verificați că fișierul există și are o dimensiune diferită de zero, înregistrați rezultatul în jurnal și lăsați fluxul de lucru apelant să decidă dacă un vizualizator este relevant. Într-o lucrare în lot, AutoLaunch lansează câte o fereastră de vizualizator pentru fiecare document și va bloca procesul pe unele sisteme, așteptând închiderea vizualizatorului

Diagramă a buclei de lot care alocă un THotPDF nou per document și îl eliberează după EndDoc, astfel încât nicio stare să nu se scurgă între treceri, cu un avertissem împotriva activării AutoLaunch în exporturile PDF de producție
Delimitează un THotPDF proaspăt per document din batch, astfel încât nicio stare să nu se scurgă între treceri, și lasă AutoLaunch dezactivat, astfel încât ferestrele de viewer rătăcite să nu blocheze jobul

Componenta THotPDF și toate apelurile de desenare prezentate aici fac parte din HotPDF Delphi Component pentru Delphi și C++Builder