ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Delphi 12.3 Athens中DISQLite3嵌入式SQLite数据库实战

Delphi 12.3 Athens中DISQLite3嵌入式SQLite数据库实战 简介本资源是面向Delphi中高级开发者的一套SQLite数据库集成控件解决方案专为适配Delphi 11至12 Athens版本含12.3设计解决原生数据库组件在轻量级嵌入式场景下性能与部署灵活性不足的问题。压缩包共169个文件包含51个核心Pascal源码.pas、49个演示工程.dpr及配套窗体.dfm辅以DCU编译单元、HPP头文件、CHM帮助文档和两个示例数据库Products.db、World.db3整体体积8.93MB结构完整、即装即用。已有72人下载学习适用于桌面应用快速接入SQLite、开发跨平台本地数据存储模块或定制SQL扩展功能。资源内置多组可运行Demo工程含BPG项目组、注册组件.dcr、API封装单元.dcu及详细帮助文档.chm覆盖安装配置、CRACK授权说明、API调用范例与扩展插件开发路径显著降低SQLite在Delphi项目中的集成门槛。 最近在整理 Delphi 12.3 Athens 的开发环境时我又把 DISQLite3 翻了出来。这个控件在 Delphi 圈子里用了十几年功能一直很稳特别是做嵌入式 SQLite 数据库方案基本属于绕不开的老牌选择。正好手里有一个 v5.48.3 支持 Delphi 11-12 的分享包借这个机会把安装、配置、常用场景和踩坑记录完整梳理一遍。不管你是刚接触 Delphi 的新手还是被控件版本折腾到头秃的老玩家这篇文章应该都能给你省点时间。DISQLite3 全称是 Delphi 版本的 SQLite 数据库组件套件做的是把 SQLite 引擎封装成 Delphi 可以直接拖拽使用的控件同时额外提供了 AES 加密、Unicode、内存数据库等增强功能。它最实用的地方在于你不需要在客户机器上再装什么数据库服务端也不用打包一堆 DLL 和配置文件一个数据库文件就能跑起来。这篇文章我主要围绕 v5.48.3 版本在 Delphi 11/12 Athens 环境下的实际使用来写包含安装步骤、查询示例、事务写入、FireMonkey/PDA 场景以及那些一年到头总有人问的控件丢失、中文乱码、多线程锁死问题。1. DISQLite3 是什么为什么在 Delphi 12 上偏偏要挑版本1.1 嵌入式 SQLite 方案在 Delphi 项目里的位置很多人第一次听到 SQLite 是在安卓开发或者 Python 里其实桌面端 Delphi 项目用 SQLite 的历史比想象中要长得多。它的核心形态是“嵌入式数据库”——没有独立的数据库进程你的程序直接调用本地数据库文件来完成读写。所以它不像 MySQL、PostgreSQL 那样要装服务端、要配账号、要处理网络连接只要文件路径对就能用。这类方案在 Delphi 项目里适合的范围很广单机版进销存、工业 PDA 数据采集、客户端本地缓存、甚至一些后台小工具。我做过一个物流仓库的扫码入库项目前台工控机装的就是 Delphi 程序数据先写本地 SQLite网络通畅后再同步到服务器整个过程客户根本感知不到数据库的存在。DISQLite3 在 Delphi 里面起的作用就是用控件的方式把这套数据库能力暴露给你拖一个 TDSQLite3Database 到窗体上设个路径就能开库。1.2 为什么不直接调 sqlite3.dll而是用组件封装有人可能会问SQLite 官方不是提供了一个 sqlite3.dll 嘛直接用 FFI 调用不就行了理论上是可行的网上也能找到不少封装示例。但实际业务里直接用 DLL 是自找麻烦。SQLite 的 C API 有很多状态管理细节比如数据库句柄、预处理语句句柄、回调函数、字段类型绑定全都自己处理的话Delphi 代码会变得极其啰嗦。而且日常开发用 DBGrid、ComboBox 这类数据感知控件绑数据时没有 DataSet 组件接一层基本等于半残废。DISQLite3 的价值就在这里它屏蔽了底层 C API 的复杂度让你像操作普通数据集一样去写查询。它提供的 TDSQLite3Dataset、TDSQLite3Query 都支持字段绑定、参数化查询、事务控制和 Delphi 自带的数据库组件用法很接近。再加上它自带授权数据库加密、Unicode 支持、内存模式、脚本执行这些实用能力比起自己用 dll 折腾确实省心太多。另外一点DISQLite3 是 Elevate Software 家的老产品更新频率不像商业大厂那么“卷”但稳定性一直在线。它家另一个知名产品是 ElevateDB经验上属于能直接用在生产环境的货色。如果要在 Delphi 里长时间维护一个带本地数据库的项目用这类老牌控件踩坑成本低。1.3 版本匹配IDE 版本、控件版本和 DPK 的关联关系这里需要强调一下Delphi 控件的安装不是“下载了就能编译”的事每个 IDE 版本的编译器版本不同运行时包Runtime Package和设计时包Design Time Package都要对应编译。代码包的 DPK 文件里会写死 IDE 版本号比如 Delphi 11 的叫 D11.dpkDelphi 12 的叫 D12.dpk如果不匹配IDE 会直接拒绝加载或者在编译时提示版本冲突。DISQLite3 v5.48.3 这个版本在设计上同时覆盖了 RAD Studio 11 和 12所以安装包里的 Packages 目录下会有多个子目录分别对应 Delphi 11 和 Delphi 12。你千万不要图省事直接把旧版本里编译出来的 BPL 文件拷到新 IDE 里用百分之百会出问题。最稳妥的方式是按 IDE 版本重新编译一遍源码包让 DCU 和 BPL 都匹配当前环境。也正是因为这种“版本绑定”机制网上流传的老版本控件包才经常在新 IDE 里翻车。比如你装了个 5.4.5 的包打开 Delphi 12 项目发现控件要么拖不出来要么编译时提示找不到单元。这就是因为旧包的 DPK 没有适配新编译器的 RTL 接口变化。所以选择 v5.48.3 这样一个明确标注了支持 Delphi 11-12 Athens 的版本就是在给后续使用铺路。2. 安装与环境准备从压缩包到 IDE 组件栏2.1 解压后包里都有什么先说明一下网上流传的这类分享包通常是一个 7z 压缩包名称大概长这样DISQLite3 v5.48.3 for Delphi 11-12 Athens CRACK.7z。解压之后目录结构一般包含这几个部分路径/文件作用DISQLite3.chm官方帮助文档API 速查第一来源Source/控件完整源码编译时要用Packages/Delphi11、Packages/Delphi12不同 IDE 版本对应的 DPK 包文件Install/一键安装批处理脚本DISQLite3.dll运行时使用的数据库引擎动态库License/授权相关文件见下方提醒学习环境自己把握关于“ CRACK”这部分内容我只说一句话授权相关的文件我不展开自己学习、测试环境里折腾无所谓但如果是公司商业项目或者要部署到客户机器上请务必去官方购买授权。DISQLite3 的封装和扩展功能是有版权的别为省一个 License 的钱给自己埋雷。下面安装流程只讲技术内容。2.2 安装设计时包和运行时包的具体步骤最省事的方式是找到 Install 目录里的批处理脚本比如Install Delphi 12.bat右键以管理员身份运行。脚本一般会帮你执行编译和安装两步。但我个人建议手动安装因为你至少要知道这个过程发生了什么后面出问题才好排查。手动安装步骤是这样打开 Delphi 12 IDE先不要打开任何项目。执行File Open Project定位到Packages\Delphi12目录打开DISQLite3D12.dpk。在 Project Manager 里右键这个包先选择Compile。这一步是编译运行时包生成 DCU 和 BPL。确认 Build 结果没有报错后再次右键包选择Install。这个动作会把设计时组件注册到 IDE 组件栏。如果包列表里还有DISQLite3DR12.dpk这样的运行时包项目也要编译一次保证设计时包依赖的运行时库就位。装完之后组件栏一般情况下会多出一页叫 DISQLite3 的分组里面有 TDSQLite3Database、TDSQLite3Dataset、TDSQLite3Query、TDSQLite3Script 这些组件。如果组件栏没出现新页签不要慌看下一步的路径配置。你还需要手动把Source\Win32或者源码根目录加到 IDE 的库路径里。具体入口是Tools Options Environment Options Delphi Options Library在Library path里加上解压包中的 Source 目录。这一步直接解决了“打开老项目时找不到控件单元”的问题。2.3 编译报错、找不到 DCU、控件栏不显示的处理这是我见过最多的三类问题挨个说。第一类编译时报E1026 File not found: DSQLite3.dcu或者类似的找不到 DCU 错误。原因是 IDE 的 Library path 没配置好或者 Source 目录里根本没生成 DCU 文件。解决办法是先编译一遍运行时包让 DCU 输出到 Source 目录或某个固定目录再把那个目录加进 Library path。这里有个小细节Delphi 在编译时会优先查找项目文件所在目录其次查 Library path所以如果你把 DCU 放在工程目录里也能找到但为了多个项目共用建议统一放到控件的输出目录。第二类组件栏没出现 DISQLite3 分组。可能是设计时包没有真正 Install 成功。回到 Project Manager 看包名后面有没有显示已安装图标。如果显示的是灰色且没有 Install 选项可能是包的类型不对或者当前 IDE 加载了冲突的旧版本包。到Components Install Packages里检查已安装列表把旧的 DISQLite3 相关包先 Remove再重新安装。第三类编译时报Unit XXX was compiled with a different version of XYZ。这是典型的“包版本和 IDE 版本不匹配”信号。比如你从 Delphi 11 环境里拿了 DCU 到 Delphi 12 用就一定会出这个问题。处理方式很粗暴删除所有旧 DCU重新从源码编译。如果是源码本身报错说明控件版本太老需要找更新版或者手动改一小段代码这个就看具体报错信息了。3. 核心功能实操连接、查询和事务批量写入3.1 创建数据库文件和连接参数DISQLite3 的基本连接流程非常直接创建一个 TDSQLite3Database 实例设置 FileName然后调用 Open。如果文件不存在默认会自动创建。这在做单机工具时特别方便不用先跑到数据库管理工具里建库建表。var DB: TDSQLite3Database; begin DB : TDSQLite3Database.Create(nil); try DB.FileName : D:\Data\app.db; DB.Open; DB.ExecSQL(CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, score INTEGER DEFAULT 0)); ShowMessage(数据库初始化完成); finally DB.Free; end; end;注意一点SQLite 的数据库连接在 DISQLite3 里默认是持久的也就是说 Open 之后不显式 Close文件句柄会一直占用。桌面程序没问题但如果你是写服务型程序、长期跑批处理建议在不需要时及时 Close或者用短连接模式。开发时我习惯把数据库打开和关闭封装在工具类方法里而不是让 TDSQLite3Database 对象一直空转。还有一个容易被忽略的选项TDSQLite3Database 有MemoryDatabase之类的属性或构造参数可以创建纯内存数据库适合跑测试或者做临时计算关库后数据自动消失。如果你只是拿它当缓存用内存模式性能会高一大截。不同版本属性名略有差异打开帮助文档检索 Memory 就能找到。3.2 select 查询与参数绑定查询操作是日常用得最多的部分。用 TDSQLite3Query 查询思路和 Delphi 自带的 TQuery 基本一致设置 SQL 文本参数赋值然后 Open 或者 ExecSQL。var DB: TDSQLite3Database; Q: TDSQLite3Query; begin DB : TDSQLite3Database.Create(nil); try DB.FileName : D:\Data\app.db; DB.Open; Q : TDSQLite3Query.Create(nil); try Q.Database : DB; Q.SQL.Text : SELECT id, name, score FROM users WHERE score :minScore ORDER BY score DESC; Q.ParamByName(minScore).AsInteger : 60; Q.Open; while not Q.Eof do begin // 用 FieldByName 取字段值 Memo1.Lines.Add(Format(%d - %s - %d, [ Q.FieldByName(id).AsInteger, Q.FieldByName(name).AsString, Q.FieldByName(score).AsInteger ])); Q.Next; end; finally Q.Free; end; finally DB.Free; end; end;这里我强烈建议养成参数化查询的习惯而不是把变量直接拼到 SQL 字符串里。原因有两个一是安全稍微懂点 SQL 注入的人都知道拼接字符串的风险二是性能SQLite 对同一条 SQL 文本会做解析缓存你每次都拼出不同文本相当于让引擎反复解析。参数化以后SQL 模板固定引擎可以复用准备语句。上面的代码里有一个前提TDSQLite3Query 支持 ParamByName。不同版本里参数绑定方式可能有一点差异如果你的版本里没找到 ParamByName可以在帮助文档里搜 Parameters 或者 SearchParam。这么多年用下来主动参数绑定基本是所有组件的标配所以大概率没问题。如果你要绑定的不是查询而是把结果显示在 DBGrid 这样的数据感知控件上那就换成 TDSQLite3Dataset。它的用法和 TDataSet 子类很像设置 Database 和 SQL然后 Open再把 DataSource.DataSet 指向它。这样 Grid 就能自动显示查询结果省去循环赋值的功夫。3.3 事务批量写入的正确姿势很多新手第一次用 SQLite 写大量数据时会发现速度慢得离谱。比如循环插入一万条记录不带事务可能要几十秒带上事务不到一秒。原因是每条 INSERT 在 SQLite 里默认都是自动提交模式相当于每写一条都要做一次磁盘同步。把这个同步次数从一万次降成一次性能自然就上去了。DISQLite3 的事务控制很清晰DB.BeginTransaction; try for i : 0 to DataList.Count - 1 do begin DB.ExecSQL(INSERT INTO log(msg, ts) VALUES (:msg, :ts), [DataList[i], Now]); end; DB.Commit; except DB.Rollback; raise; end;这里用ExecSQL直接把参数数组传进去省了建 Query 对象的开销适合纯写入场景。如果你的参数很多或者字段很多可以改用 TDSQLite3Query 循环赋值代码更清晰速度差异不会太大。另外提醒一下事务里如果中途有异常一定要 Rollback否则连接状态是“事务未结束”后续读写都会受影响。上面代码里的 except 块不是摆设是保命用的。还有一个常见坑不要把整个程序逻辑都包在一个大事务里。比如你在事务里做一堆网络请求那这个事务会长时间占用写锁其他线程读数据都会被阻塞。事务范围要尽量短只包住真正需要原子提交的那段操作。3.4 AES 加密库与只读场景DISQLite3 区别于社区免费 SQLite 封装的一个亮点是加密支持。它允许你在建库时设置密码带上密码的数据库文件用普通 SQLite 工具是打不开的需要组件加上密码才能访问。对于客户资料、账单信息这类敏感数据这个能力很实用。DB.EncryptionAlgorithm : eaAES128; DB.Password : your-private-key; DB.FileName : D:\Data\secure.db; DB.Open;和一般 SQLite 加密扩展不同DISQLite3 的加密是内置在引擎里的不用额外加载第三方 DLL。唯一的注意点一旦给数据库设置了密码每次打开数据库都必须带上相同密码密码错或者丢掉库里数据基本就等于丢了。所以强烈建议你自己再做一层密码备份机制别把用户的业务数据也绑死在一个容易忘的密码上。只读场景也有一个细节。如果你要在程序里读取一个数据库文件但不想让它被意外改动可以设置数据库的只读模式。这样即使代码里执行了 INSERT/UPDATE也会被引擎拒绝或者保持原文件不动。对分发数据包、离线字典这类需求很合适能避免程序 bug 把数据源搞脏。4. FireMonkey / PDA 场景下的实战扩展4.1 为什么移动端本地数据库选 SQLite很长一段时间里Delphi 开发者做移动端Android / iOS本地数据存储时第一反应就是 SQLite。一方面系统自带另一方面跨平台方案成熟。DISQLite3 在多平台上有对应支持和 Windows 一样提供组件化操作不用在移动端上对着底层 API 愁。PDA 场景和普通手机应用有很大区别。PDA 设备通常配置不高网络不稳定很多时候在仓库、车间这种环境里根本没有 Wi-Fi。这时候你不可能每扫一个条码就同步一次服务器最合理的方案就是在本地先落库等网络恢复再批量上传。DISQLite3 的嵌入式特性正好满足这种“本地先存、延迟同步”的架构数据文件放在设备私有目录随用随开不依赖外部服务。4.2 扫码结果接收与入库的典型流程仓库 PDA 上最常见的操作流程是这样的用户按下扫码枪物理键扫描枪通过串口、蓝牙或者设备内置扫描头回调拿到条码字符串。程序接收到条码后有时候还要调用服务端的解析接口换取货品信息很多 PDA 项目的扫码解析结果是一段 JSON。拿到 JSON 后用 Delphi 的 JSON 库解析出关键字段。把条码、解析结果、操作时间、操作人等信息写入本地 SQLite。这个流程里DISQLite3 主要负责第四步核心代码如下所示procedure TMainForm.LogScanResult(ScanCode, JsonResult: string); begin if not DB.Connected then DB.Open; Q.SQL.Text : INSERT INTO scan_log(barcode, json_content, scan_time, operator) VALUES (:barcode, :json, :time, :operator); Q.ParamByName(barcode).AsString : ScanCode; Q.ParamByName(json).AsString : JsonResult; Q.ParamByName(time).AsDateTime : Now; Q.ParamByName(operator).AsString : FUserName; Q.ExecSQL; end;千万不要在扫码枪每扫一下都做一次“打开数据库、执行 SQL、关闭数据库”的操作PDA 设备性能有限这种写法会把响应时间拉得很长。正确做法是程序启动时打开数据库运行期间保持连接退出时关闭。或者用单例模式维护一个数据库连接管理器所有页面共用。另外一点经验移动端应用随时可能被系统杀掉不要赌“我写了一半的事务一定会被 Commit”。最好每条扫码记录作为一个独立事务或小批事务提交这样即使应用被强杀最多丢最后一条不会出现整库锁死的情况。4.3 查询结果导出 Excel/CSV 的小技巧和 DISQLite3 配合最常见的办公需求之一就是把查询结果导出成 Excel。说实话在 Delphi 里直接生成 xlsx 文件不是不能做但要用到第三方库或者 Office COM在 PDA 和服务器环境里都不太现实。我的做法是先导出 CSV文本文件再用 Excel 打开全世界都知道怎么处理。var SL: TStringList; Q: TDSQLite3Query; begin SL : TStringList.Create; try Q : TDSQLite3Query.Create(nil); try Q.Database : DB; Q.SQL.Text : SELECT * FROM scan_log WHERE scan_time :startTime; Q.ParamByName(startTime).AsDateTime : StartDate; Q.Open; while not Q.Eof do begin SL.Add(Q.FieldByName(barcode).AsString #9 Q.FieldByName(json_content).AsString #9 Q.FieldByName(scan_time).AsString); Q.Next; end; finally Q.Free; end; SL.SaveToFile(D:\export\scan_log.csv, TEncoding.UTF8); finally SL.Free; end; end;这里我用 Tab#9做分隔符而不是逗号因为 JSON 内容里天然包含逗号直接用逗号分隔会导致列错位。CSV 文件保存成 UTF-8 带 BOM 的格式Excel 打开中文就不容易乱码。如果你需要更正式的 xlsx 格式可以后续再轻量处理但 CSV 方案对九成以上的报表导出需求都够用了。5. 常见问题与排查技巧实录5.1 每次进入 IDE 控件就丢失怎么定位“Delphi 控件版本问题导致每次进入 IDE 都丢失控件需要重新放置保存后还是那样”——这个热搜词完美描述了很多 Delphi 开发者的噩梦。问题表面看是控件放不到窗体上实际根源一般是下面几种现象常见原因处理办法打开项目窗体时提示找不到 DISQLite3 相关类IDE 没安装设计时包或包未激活打开设计时包编译并 Install确认组件栏出现组件窗体能打开但控件以占位符显示需要重新放置运行时包版本和项目不匹配或 DCU 路径没配置重新编译运行时包把 DCU 目录加入 Library path编译时提示“Cannot find unit”当前项目搜索路径里缺少控件源码目录检查 Project Options 里的 Search path补充源码目录保存后关闭再打开控件又丢失同一控件同时安装了多个版本IDE 加载了错误包在 Install Packages 里移除旧版包只保留当前版本我自己的经验是这类问题九个里面有八个是“IDE 里装了多个版本的 DISQLite3 包”导致的。Delphi 加载包的机制是全局的它不会因为你当前项目用的是旧版就自动切换。你把 5.4.5 和 5.48.3 都装了IDE 在打开窗体时可能加载了其中一个但项目里引用的是另一个结果就是控件各种诡异丢失。排查步骤可以参考先打开Components Install Packages看一遍已安装列表遇到 DISQLite3 相关的只保留一个然后删掉项目里的 DCU 缓存一般是 __history、Win32 目录下的 Debug 输出重新编译最后再打开窗体看是否正常。如果还不行就疯狂一点把控件彻底卸载重启 IDE再重新安装一遍。5.2 中文乱码和 UTF-8 编码问题SQLite 默认存储 UTF-8而 Delphi 的 string 在 Windows 平台默认是 UTF-16Ansi 字符串在某些环境下是系统代码页。如果项目里有中文字符串需要确保“源文件编码—数据库连接编码—显示控件编码”三者一致。否则最常见的结果是写入之前是中文读出来变成乱码或者查询条件带中文匹配不到。处理思路是这样的确认 Delphi 源文件保存为 UTF-8 格式尤其是含中文的单元文件。Delphi IDE 里可以在文件保存选项中设置编码。查看 DISQLite3 组件的编码相关属性把数据库连接和数据集切换到 UTF-8 模式。具体属性名随版本会有差异一般是 Database 或 Session 上的 Encoding、Charset、UseUtf8 之类。必要时可以在 Open 后手动执行一次PRAGMA encoding UTF-8;确保库文件本身使用 UTF-8 存储。还有一个小坑在 Win32 平台上TDSQLite3Query 读出来的字符串如果需要显示到 VCL 标准控件上通常不会有问题但如果你拿到字符串做哈希处理或者加密务必统一好字符编码否则同样的内容不同编码算出来的结果不一样。比如你在 Delphi 10.4 里计算字符串 MD5看到的结果和 Python 里算出来的居然不同十有八九就是编码不一致的问题。5.3 多线程读写导致的 database is lockedSQLite 的锁机制和服务器数据库完全不一样它同一时间只允许一个写者。多线程同时写或者一个线程在写而另一个线程在读都可能抛database is locked异常。DISQLite3 封装了底层的锁和 busy handler但默认行为不一定满足你的业务。实践中最有效的三个手段第一设置 busy_timeout。把默认 0 秒延长到几秒让 SQLite 在遭遇锁冲突时先等待而不是立刻报错。用DB.ExecSQL(PRAGMA busy_timeout 3000;)即可。这个值不要太长建议 1-3 秒否则界面会卡死。第二开启 WALWrite-Ahead Logging模式。WAL 允许写操作和读操作同时进行读操作不会被写操作阻塞。执行一行PRAGMA journal_mode WAL;就行。这个修改是持久化到数据库文件上的后续打开还会保持不过要注意 WAL 模式会额外生成-wal和-shm文件程序退出时如果没正常关闭数据库临时文件可能残留。第三架构上尽量做到“单写者”。多线程并发写同一个数据库再怎么调参数都是性能和可靠性上的妥协。比较成熟的模式是把所有写操作丢到一个队列里由专门的后台线程统一执行其他线程只管提交写任务。这样可以绕开大部分锁冲突问题。如果你遇到的是“读也报 locked”的情况优先检查是否有一个长事务一直没提交。我有一次排查了很久发现是某个单元在循环里打开了事务但忘记 Commit导致整个数据库被锁了好几个小时现象表现为后台线程一写就报错界面倒是正常。最后加了日志定位才发现事务嵌套出了问题。5.4 版本选择建议v5.48.3 到底稳不稳从项目长期维护的角度看控件版本不能乱换但也不能一直停在太老的版本。DISQLite3 v5.48.3 这个版本号目前看是偏新的维护版本和 Delphi 12.3 Athens 的编译器匹配度比较好。我安装后测试了 select 查询、事务批量写入、加密库、多线程读写都没出现明显的兼容性问题。给新手的建议是如果你在 Delphi 12 上用 DISQLite3优先找带for Delphi 11-12 Athens字样的新包不要随手搜个 5.4.5 就下。版本差距太大的包在新 IDE 上编译报错是大概率事件而手动修源码的事不到万不得已不建议碰。如果项目还在 Delphi 10.4 或更老的版本上建议不要盲目升级控件包先把当前版本的包确认能稳定编译再考虑换新。毕竟控件这玩意儿能用就别乱动。我自己就吃过“手滑升级控件结果项目编译包不兼容”的亏一个下午都耗在解决 IDE 加载错误上。最后再分享一个小技巧安装完任何 Delphi 控件我都建议立刻用File Save Project新建一个空的测试项目放一个控件进去保存、关掉、再重新打开确认控件不丢、能编译再做正式业务开发。这套“装完即验证”的流程能帮你把绝大多数控件环境问题拦截在项目开发之前省下来的时间比什么都值。本文还有配套的精品资源点击获取
返回列表