资讯动态

CrystalReport VCL水晶报表11在Delphi XE中的集成与导出实践

发布时间:2026/10/16 3:22:20 来源:尧图企业网站定制
简介水晶报表11 R2是面向Delphi/BCB XE开发者的VCL控件包用于在桌面应用中集成水晶报表设计、预览、打印与导出功能属于第三方基于11版修改的增强版本。压缩包共710个文件、约5.43MB包含127个pas源码与101个hpp头文件便于阅读和二次调整113个dcu及dpk/bpk/bpl组件包用于编译安装109个dfm保存窗口与报表设计布局176个bmp等图像资源负责工具栏和图标另有rpt报表模板、pdf/hlp帮助文档及dpr示例工程结构覆盖从设计期到运行期的完整链路可保留XE环境下的设计期能力。安装时需注意与Embarcadero Sample Components中的TCalendar控件冲突在Install Packages菜单中取消勾选Sample Components即可正常安装这一排错要点对同环境下复现问题的开发者很实用。目前已有416人学习/下载适合需要快速在VCL应用中引入水晶报表、并希望保留源码可维护性的Delphi/BCB中高级开发人员。1. 拿到 CrystalReport VCL 水晶报表 11 后先搞清它能帮你省下多少报表工时做 Delphi 或 CBuilder XE 的老人十有八九被报表折磨过。Grid 打出来歪歪扭扭、PDF 导出来中文变方块、客户临时要个合计字段就得改代码重新编译……而 CrystalReport VCL 水晶报表 11 这套组件就是把“报表设计”从代码里整个剥出去的存在。它让程序员只需要写数据加载代码剩下排版、分组、求和、条形码全部在 .rpt 设计器里点鼠标完成。适合三种人接手老项目被迫维护水晶报表的、新项目想快速交付复杂报表的、以及被 FastReport 或 RAVE 的布局折腾到想换工具的人。这套 VCL 版本直接嵌入 Delphi/BCB XE 的 IDE不需要单独起报表进程当年能在一夜之间把几十张报表的交付周期压缩一半。它的价值不在“能画画格子”而在“把设计权限交给业务人员代码只做搬运工”。2. 装进 IDEXE 环境下的安装步骤与版本匹配2.1 组件包结构先分清 RTL 与设计期包水晶报表 11 的 VCL 包解压后通常能看到几个关键文件CRViewer.dcu、CRRTLLib_TLB.pas、CRDesigner.dcu以及一堆.bpl和.bpi。第一次打开的人会懵到底要装哪几个我的习惯是先分两类——运行期包RTL和设计期包Designer。运行期包负责在程序里加载 rpt、连数据库、导出的核心逻辑设计期包是集成到 IDE 里让你在 Delphi 窗体上拖一个TCrViewer控件下来。两者缺一不可但设计期包装错版本IDE 启动直接报“包未找到”。看安装说明时重点找Crystal Reports 11 for Delphi或BCB字样的文件夹。里面通常有Delphi10、Delphi11、BCB等子目录对应不同 IDE 版本。注意这是“XE”时代的老组件它只认 Delphi 2007、2009、2010、XE 这类 32 位编译环境。如果你拿它硬塞给最新 RAD Studio 的 64 位编译器后面会遇到一连串接口映射问题这个后面专章说。2.2 安装流程注册路径与库文件我一般不走官方 Installer因为老组件在 XE 上经常检测不到注册表。手动装反而清晰先把运行期包里的.bpl复制到系统System32或 IDE 的bin目录然后在 IDE 里打开Component Install Packages点Add选择对应的.bpl文件。设计期包通常是dclcr11.bpl这类名字。装完工具栏会出现CrViewer、CrReport等图标。代码层面的依赖路径也要配好。在Project Options Delphi Compiler Search Path里把解压出来的Lib目录加进去否则编译时uses CRTTL_TLB会报找不到文件。我的血泪经验是一定把CRRTLLib_TLB.pas这个文件单独拿出来放到自己的源码目录别只依赖系统搜索路径。因为这个文件是类型库自动生成的不同机器上不同版本生成的 GUID 会有偏差一旦 IDE 自动帮你重新包装接口引用就会错乱。2.3 版本匹配Delphi 与 BCB 的 lib 差异Delphi 和 CBuilder XE 虽然共用 VCL 包机制但水晶报表 11 给两边提供的.lib和.bpi不互通。我见过有人用 Delphi 的.dcu扔给 BCB 编译直接报E2206 Illegal type in expression。正确做法是BCB 项目优先添加.bpiDelphi 项目添加.dcu。如果资源包里没有对应版本你可以用tlibimp.exeIDE 自带的类型库导入工具手动从crts32.dll重新生成导入单元。命令大致是tlibimp -C -P -X -D crts32.dll这个命令会在当前目录生成CRRTLLib_TLB.pas和CRRTLLib_TLB.hpp-C表示生成 C 头文件-P生成 Pascal 单元-X跳过已有文件-D指定源 DLL。我一般用-X防止把原厂文件覆盖但如果你之前生成错了就删掉旧文件重新跑。参数不复杂难的是得确定 DLL 里接口版本和你手上的 rpt 兼容——水晶报表 11 生成的文件拿新版运行时引擎打开时总会有一堆兼容性提醒这在老项目里是常态别慌。3. 第一条报表从设计器到代码基本调用流程3.1 设计报表文件rpt 的生成与数据源绑定水晶报表的核心是.rpt文件。它可以用独立的 Crystal Reports 设计器生成也可以在本组件里通过代码动态生成。但最常见、最省事的路径是先用设计器拉好版式保存成 rpt然后在 Delphi 里加载它。设计器里连数据库时要注意水晶报表 11 的数据库连接方式分三种——直接 ODBC、ADO、和“当前连接”。做桌面程序时我强烈建议选“ADOOLE DB”因为部署目标机器上不一定装了水晶报表的数据库驱动但 ODBC 驱动几乎都有。设计时先用自己的开发库连一次把表结构和字段定义固化进 rpt。但注意这种方式会把连接信息服务器名、用户名密码明文存进 rpt。如果程序要交给客户要在设计器中把“保存密码”取消运行时通过代码传连接参数覆盖。3.2 在代码中加载并显示报表在 Delphi 中调用最核心的步骤是创建CrReport对象设置ReportSource属性再让CrViewer打开。我通常这样写procedure TMainForm.ShowReport(const RptFile: string); var CR: TCrReport; begin CR : TCrReport.Create(nil); try CR.Load(RptFile); // 加载已设计好的 rpt CR.LogOnServer(ADO, ProviderSQLOLEDB.1;Data SourceMYPC;User IDsa;Passwordxxx, , ); CrViewer1.ReportSource : CR; CrViewer1.ViewReport; // 触发预览 finally // 注意: ReportSource 赋值后, 释放时要先置空 CrViewer1.ReportSource : nil; CR.Free; end; end;逻辑说明LogOnServer的参数依次是连接类型、连接字符串、日志和表前缀。对于 ADO 连接第二个参数就是完整的 ADO 连接串。这里有个重点ReportSource会持有CrReport对象的引用所以在释放前必须先将ReportSource置空否则访问违例。我一开始没写最后那行程序退出时随机崩溃排查了整整一个下午。3.3 常用参数设置打印边距、预览与静默导出预览界面有很多可调项但实用的是几个非 UI 属性。比如关闭预览窗口的工具栏直接用代码导CrViewer1.ShowToolbar : False; CrViewer1.ShowGroupTree : False; CR.PrintWindowTitle : 季度销售明细;ShowGroupTree默认 True会显示左侧的分组树。老客户往往不想要这个觉得碍眼。如果要打印用PrintReport(True)参数意思是显示打印设置对话框。如果想直接静默打印到默认打印机传False。还要注意边距设计器里默认边距是 2.5cm但很多针式打印机实际打不了那么宽你需要在代码里覆盖CR.SetMarginLeft(5); // 单位: 缇 (twips), 1cm 567 缇 CR.SetMarginRight(5); CR.SetMarginTop(5670); // 1cm单位很容易弄错。水晶报表的边距接口用的是缇不是像素。我见过同事按像素填结果打印出来左边距只有零点几毫米整张纸挤成一团。4. 把报表导出成 PDF / Excel参数与乱码处理4.1 导出格式的选择与导出参数报表除了预览更多时候客户要求自动生成 PDF 发邮件。水晶报表 11 的 VCL 导出函数是统一的ExportTo靠枚举指定格式。我常用的是CR.ExportTo(ExportOptions, c:\report.pdf);其中ExportOptions是一个CrExportOptions对象需要先创建并设置目标类型。常用格式有CrExportFormatPDF、CrExportFormatExcel、CrExportFormatRTF。每个格式有自己的子参数比如 Excel 导出时可以设置ExcelUseTabDelimited或ExcelConstantColumnWidth。我一般记不清全部选项都是先看类型库的注释。一个实用的技巧导出成功后用文件大小判断是否成功别只看返回值。因为老版本组件有时导出空 pdf 也返回成功。4.2 中文字体与乱码的坑这是水晶报表在国内项目里八成会踩的坑。现象预览正常导出 PDF 后中文全部变成方块或问号。原因有两个一是 rpt 中字段的字体没有选择中文字体。设计器里默认字体会落到Arial它没有中文字形PDF 引擎找不到对应字体就画方块。所以设计时必须把字体设为“宋体”或“微软雅黑”。二是导出时缺少字符映射。老外设计的水晶报表导出 PDF 默认不嵌入字体在无中文字体的机器上打开必乱。解决办法在 rpt 设计器里选中所有文字对象把字体改成“宋体”SimSun。另外可以在代码里用CR.Font.Name : SimSun强行覆盖字段字体但注意这只影响运行时rpt 里固定好的样式优先级更高。实在不行还有一个偏方导出 PDF 后再用开源工具做一次字体嵌入。但这会引入额外依赖不推荐。4.3 导出到流对象而不落盘的技巧有些场景不允许生成临时文件比如 Web 服务里导出 PDF 后直接输出到响应流。水晶报表 VCL 的导出目标除了文件路径还支持crExportOleStream。代码可以这样var MS: TMemoryStream; Opts: CrExportOptions; begin MS : TMemoryStream.Create; try Opts : CrExportOptions.Create; Opts.ExportFormat : CrExportFormatPDF; Opts.ExportFileName : ; // 留空, 配合流目标 Opts.ExportWithOLE : True; CR.ExportTo(Opts, MS); // 第二个参数可以是 TStream MS.Position : 0; // 这里把 MS 交给 TIdHTTP 或 TFileStream 发送 finally MS.Free; end; end;这里的关键是ExportWithOLE : True它让组件走 OLE 流通道而不是写临时文件。我有一段时间导出总是卡住原因是传给ExportTo的流对象没设置 Position组件从当前位置写数据导致文件头为空。做法是在导出前显式MS.Position : 0。另外如果 rpt 比较大超过几十 MB导出到内存流会很吃内存建议先导临时文件再删或者分页导出。5. 避坑水晶报表 VCL 常见的六大故障与排查5.1 运行时提示 “Cannot load Crystal Reports” 错误现象程序编译通过一打开带水晶报表的窗体就弹Cannot load Crystal Reports runtime。原因目标机器缺少水晶报表 11 的运行时 DLLcrts32.dll、u2d*.dll等。开发机上能跑是因为 IDE 装了完整包部署机只装了你的 exe。解决把资源包里Runtime目录下的所有文件复制到 exe 同目录或者用安装工具把它们注册到系统目录。别只复制主 DLLcrpe32.dll和crdb_ado.dll也必须带否则报错会在连接数据库时出现。5.2 设计器打开 rpt 时崩溃现象双击 rpt 文件IDE 崩溃或无响应。原因多数情况是 rpt 文件是用新版 Crystal Reports 设计器13 或 14保存的数据结构与 11 的 RDC 接口不兼容。解决先用原版设计器把 rpt 另存为 11 格式或者用纯文本方式打开 rpt 检查头部标记。如果客户坚持要新版设计那就别用 rpt 文件改为代码动态生成报表结构。这个工程量不小但能根治兼容性问题。5.3 导出 PDF 中文变方块现象预览正常导出的 PDF 中文乱码。原因rpt 内字段字体为非中文字体且 PDF 导出未嵌入字体。解决在 rpt 设计器中全选字段统一设置字体为中文字体宋体/黑体。同时确认导出参数里PDFEnableFontEmbedding为 True。这个参数在 CrExportOptions 的子属性里别漏了。如果还不行检查 Windows 系统字体目录是否有 SimSun没有则需要安装中文字体。5.4 与 FastReport 同项目冲突现象项目中同时使用了水晶报表 VCL 和 FastReport编译时提示TPersistent方法冲突或类名冲突。原因两个库都注册了名为TCustomReport或TfrxReport的类根类相同但接口实现不同。解决在 uses 子句中调整顺序把水晶报表相关单元放在 FastReport 之前。更彻底的做法是使用CRIntf前缀调用水晶报表接口避免类名直接引用。如果你用的是包安装还可以在 IDE 的包设置里禁掉其中一个库的自动加载——反正两个库混合使用在内存里很容易踩对象生命周期问题。5.5 64 位编译下接口失效现象项目设置了 64 位编译代码里调用LogOnServer直接报Access Violation。原因水晶报表 11 的 VCL 只生成了 32 位导入库64 位下类型库接口指针宽度不一样老式 COM 桥接失败。解决别挣扎把报表相关模块单独编译成 32 位 exe和主程序通过进程间通信。或者改用 64 位版水晶报表运行时Crystal Reports for Visual Studio 等但需要重写引用。这是老组件的硬伤我最后就是让报表模块独立进程跑主程序和报表进程用命名管道传递参数。5.6 部署到目标机器时缺少运行时现象程序在客户机器上报“动态链接库 crpe32.dll 找不到”。原因安装包没有包含水晶报表运行时开发机上能跑是因为 IDE 环境带了。解决制作安装包时把水晶报表的整个 Redistributable 目录打进去。注意别直接覆盖客户机器同名的新版 DLL最好使用私有目录加载方式在 exe 启动时先调用SetDllDirectory指向你自己带的 DLL 目录。我一般会在主窗体初始化里加一句SetDllDirectory(D:\App\CRRuntime);这样系统会优先去这个目录找水晶报表的组件避免和客户机器上其他版本打架。6. 进阶用数据字典动态绑定表让一条 rpt 走遍多张表水晶报表 11 最大的弱点就是 rpt 一旦设计了固定数据源换张表就得重新做。我后来摸索出一个做法用数据字典DataDictionary接口在运行时替换 rpt 的数据库连接和表名。核心思路是拿到CrReport对象的数据库控制器然后用Dictionary方法重新设置表位置。大概流程是先在 rpt 里定义一个逻辑表名比如ORDERS设计时用假表占位。运行时通过代码遍历 rpt 的数据库表列表把每张表的属性改为实际数据源。关键代码如下var Dic: CrDatabaseDictionary; Tbl: CrDatabaseTable; i: Integer; begin Dic : CR.Database.Dictionary; for i : 0 to Dic.TableCount - 1 do begin Tbl : Dic.Tables[i]; // 只替换逻辑名为 ORDERS 的表, 不碰其他子查询 if Tbl.Name ORDERS then begin Tbl.TableName : Northwind_Orders; // 实际物理表 Tbl.ConnectionInfo.Provider : ADO; Tbl.ConnectionInfo.ConnectionString : ProviderSQLOLEDB;Data SourceMYPC;Initial CatalogNorthwind;User IDsa; end; end; end;这么做的好处是一套报表模板只要改字段映射就能复用。但坑也不少表名替换后字段仍是按原表结构绑定如果物理表的列名不一样报表会显示空白。我的习惯是在设计时就给字段取别名让ORDERS.OrderID对应物理表的OrderID这样替换表名就不会丢列。另外Dictionary操作在每次ViewReport之前执行才有效否则已打开的报表缓存不会刷新。从那以后我每次做多表报表都强制走一遍“逻辑名 运行时替换”的流程再也没为重复设计画过一张 rpt。这套路的另一好处是客户中途要求换库连接只需要改一个配置项不用重新发布程序。希望这个用法帮你在老组件上多榨出一些生产力。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑