Upuszczenie komponentu THotPDF na formularz w czasie projektowania jest w porządku dla szybkiego prototypu, ale wiąże komponent z cyklem życia formularza, co rzadko jest pożądane w kodzie produkcyjnym. Generator raportów uruchamiany jednym kliknięciem przycisku, wątek usługi wykonujący nocne eksporty partiami, klasa pomocnicza nieposiadająca w ogóle formularza: w każdej z tych sytuacji chcesz, aby komponent istniał dokładnie przez czas trwania jednego zadania PDF, a następnie zniknął. Oznacza to alokację w czasie wykonywania i zmienia to dwie rzeczy, które warto zrozumieć przed napisaniem pierwszej linijki kodu: kto jest właścicielem obiektu i jak przebiega czyszczenie w przypadku wystąpienia błędu
Semantyka właściciela w VCL
Każdy konstruktor komponentu VCL przyjmuje parametr Owner typu TComponent*. Przekazanie this (formularza) rejestruje nowy obiekt na liście komponentów posiadanych przez formularz, więc jeśli formularz zostanie zniszczony, podczas gdy komponent wciąż istnieje, VCL zwolni go automatycznie. Przekazanie nullptr oznacza brak właściciela: bierzesz wyłączną odpowiedzialność za wskaźnik i nic go za ciebie nie wyczyści, jeśli wyjątek zwinie stos przed twoim jawnym wywołaniem delete
W przypadku jednorazowego eksportu, który kończy się w ramach pojedynczej funkcji, obie opcje zadziałają, ale mają różne tryby awaryjne. Z this jako właścicielem wyciek pamięci jest niemożliwy, dopóki formularz w końcu się zamyka; z nullptr, wskaźnik musi dotrzeć do bloku __finally. W praktyce wzorzec nullptr w połączeniu z __finally jest nieco czystszy dla krótko żyjących obiektów, ponieważ czyni granicę cyklu życia widoczną na pierwszy rzut oka i zapobiega gromadzeniu przez formularz posiadanych obiektów, które miały być tymczasowe
Struktura bezpieczna dla wyjątków
Generowanie PDF może zawieść z powodów niezwiązanych z API: katalog wyjściowy jest tylko do odczytu, brakuje pliku czcionki, strumień przedwcześnie zrzuca dane lub dostarczone przez wywołującego dane osiągają limit długości. Niezależnie od przyczyny, ścieżka czyszczenia musi zostać wykonana. Idiomatycznym sposobem zagwarantowania tego w C++Builderze jest konstrukcja 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;
}
}
Warto zwrócić uwagę na kilka rzeczy w tym kodzie. Właścicielem jest nullptr, co czyni cykl życia jawnym. Właściwości Compression i FontEmbedding są ustawiane przed BeginDoc: obie są opcjami na poziomie dokumentu, które HotPDF zatwierdza w momencie otwarcia dokumentu, a przypisywanie ich później nie przynosi żadnego efektu. TextOut przyjmuje współrzędne w punktach mierzonych od lewego dolnego rogu strony, przy czym oś Y rośnie w górę; para 72, 720 umieszcza tekst w pobliżu lewego górnego rogu strony formatu Letter z jednocalowym lewym marginesem. Instrukcja delete Pdf w bloku __finally zostaje wykonana bez względu na to, czy BeginDoc, rysowanie, czy EndDoc wywoła wyjątek, czy nie
Unikaj wywoływania jakiejkolwiek metody na Pdf po wywołaniu delete. Jeśli wskaźnik jest przechowywany w zmiennej składowej, ustaw go na nullptr natychmiast po usunięciu, aby każdy przypadkowy późniejszy dostęp powodował czystą awarię (crash), a nie ciche uszkodzenie pamięci
Konfiguracja projektu
C++Builder lokalizuje THotPDF poprzez kombinację ścieżek dołączania, ścieżek bibliotek i dyrektywy pragma. Wygenerowany plik nagłówkowy znajduje się obok HPDFDoc.pas w katalogu źródłowym HotPDF; dodaj ten katalog do Project > Options > C++ Compiler > Include path. Dyrektywa #pragma link "HPDFDoc" informuje konsolidatora (linker), aby pobrał skompilowany moduł bez konieczności ręcznego wypisywania go w pliku projektu. Jeśli używasz pakietu wykonawczego zamiast statycznego konsolidowania, najpierw zainstaluj pakiety projektowe i wykonawcze HotPDF; dyrektywa pragma nadal ma zastosowanie
Zachowaj nazwę modułu HPDFDoc niezmienioną. C++Builder wyprowadza nazwę nagłówka z nazwy modułu w języku Pascal, więc zmiana nazwy pliku lub użycie aliasu ścieżki w pragmie po cichu psuje wyszukiwanie
Zasięg i zadania wielodokumentowe
W przypadku pojedynczego eksportu wyzwalanego akcją użytkownika, lokalna zmienna z ograniczeniem zasięgu do procedury obsługi przycisku jest właściwą odpowiedzią: jest tworzona, używana i niszczona w ramach jednej ramki wywołania, a jej intencja jest oczywista dla każdego, kto później czyta kod. Alternatywa w postaci użycia komponentu z czasu projektowania (design-time) jest uzasadniona, gdy ten sam formularz steruje ciągłym przepływem pracy, takim jak panel podglądu wydruku, który przebudowuje dokument, gdy użytkownik zmieni ustawienie; w tym przypadku utrzymywanie komponentu przy życiu i powtarzane wywoływanie BeginDoc/EndDoc jest mniej inwazyjne niż powtarzane alokowanie i zwalnianie obiektów na stercie
W przypadku zadań wsadowych, które sekwencyjnie generują wiele dokumentów, utrzymanie zasięgu jednego komponentu THotPDF na dokument jest warte narzutu związanego z alokacją. Stan nie przenosi się między dokumentami, jeśli nie ma obiektu, który by go przenosił, co eliminuje konieczność debugowania całej klasy sporadycznych błędów. Alokuj, generuj, usuń, powtórz
Jedną z właściwości pojawiających się w kilku wersjach demonstracyjnych HotPDF jest AutoLaunch, która otwiera wygenerowany plik w systemowej przeglądarce PDF natychmiast po EndDoc. Jest to użyteczne podczas pisania pierwszego szkicu układu dokumentu. W środowisku produkcyjnym zrezygnuj z tej opcji: jawnie otwórz ścieżkę wyjściową, zweryfikuj czy plik istnieje i ma niezerowy rozmiar, zapisz wynik w logach i pozwól wywołującemu mechanizmowi zdecydować, czy potrzebna jest przeglądarka. W zadaniu wsadowym AutoLaunch uruchamia jedno okno przeglądarki na każdy dokument i na niektórych systemach zablokuje proces w oczekiwaniu na zamknięcie przeglądarki
Komponent THotPDF oraz wszystkie opisane tutaj wywołania funkcji rysowania są częścią komponentu HotPDF dla Delphi i C++Builder