技术文章

在 C++Builder 中动态创建并释放 HotPDF 组件

· PDF 编程

很多 HotPDF 示例会在设计期把组件放到窗体上。这样写演示很方便,但生产代码有时只需要在某次导出操作中临时创建 PDF 组件。动态创建适合服务模块、报表辅助类、批处理任务,以及组件不应跟随窗体整个生命周期存在的场景。

基本模式是分配一个 THotPDF 实例,设置输出文件和文档选项,生成 PDF,然后在操作结束时释放组件。下面的示例展示了按钮事件中的最小流程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include
#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* HotPDF1 = new THotPDF(this);
    HotPDF1->FileName = "HelloWorld.pdf";
    HotPDF1->AutoLaunch = true;      
    HotPDF1->BeginDoc(false);
    HotPDF1->CurrentPage->PrintText( 10, 10, 0, "Hello World!" );
    HotPDF1->EndDoc();
    HotPDF1->Free();
}

所有权和清理

构造函数传入 this 作为 owner,因此如果组件仍然存活,窗体可以负责清理。在这个短示例中,对象也会在 EndDoc 之后显式释放。实际代码应让清理路径具备异常安全性:如果 PDF 生成可能失败,请用 try/__finally 或小型 RAII 辅助对象确保 Free() 仍会执行。

调用 Free() 后不要再访问组件。除非窗体其他部分确实需要管理组件生命周期,否则不要把指针保存到更大的作用域里。对于一次性 PDF 导出,局部变量通常最简单。

项目配置注意事项

项目必须能找到 HotPDF 的头文件、库文件以及设计期或运行期包。把 HotPDF include 目录加入 C++ include path,把库目录加入 linker search path。如果项目链接 HPDFDoc,保持包名和单元名不变,让 C++Builder 能解析生成的头文件。

异常安全结构

示例代码保持简洁,但真实导出例程要处理分配和清理之间的异常。PDF 生成可能因为输出目录只读、字体无法解析、流提前关闭或用户数据异常而失败。清理路径不应依赖每个绘制调用都成功。

一个实用做法是在导出前立即创建组件,并在保证执行的清理块中释放。这样组件生命周期只绑定到一次 PDF 任务。如果应用连续生成多份文档,也能降低一个文档状态泄漏到下一个文档的风险。

设计期组件与运行期组件

如果一个窗体拥有一个固定导出流程,设计期组件更简单。如果应用需要多个临时导出器、动态选择设置,或从辅助类运行导出代码,运行期组件更合适。API 调用相同,区别只在于谁拥有对象以及对象存活多久。

  • 为输出文件使用绝对路径或经过验证的路径。
  • 保持 BeginDocEndDoc 成对出现。
  • 不要在批处理或服务式代码中依赖 AutoLaunch
  • 记录生成错误,便于区分路径、权限、字体和渲染问题。