尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Delphi老系统源码恢复:编译环境、数据库连接与二次开发实战

Delphi老系统源码恢复:编译环境、数据库连接与二次开发实战 简介这是一份“海信商业管理系统”的完整Delphi源码压缩包适合具备一定编程基础、想深入学习桌面商业管理系统开发的Delphi开发者或软件架构学习者。整个压缩包内含4846个文件大小约103MB除大量pas源文件、dfm窗体设计文件、dcu编译单元和exe可执行程序外还包含报表、位图、动态链接库及数据库脚本等辅助资源能清晰看出系统从界面到数据层的完整结构。源码中既有类、继承、多态等面向对象特性的典型运用也有基于VCL的可视化界面设计通过逐行研读可掌握ADO/BDE数据库连接与查询、多线程并发控制、库存与价格等业务算法、权限管理、报表打印及异常日志记录等企业级功能的具体实现。对于想转型企业管理软件开发或提升Delphi实战能力的读者这份源码提供了真实商业项目的完整参考。目前已有321人学习下载。1. 从 zip 到可运行的 Delphi 老系统认清这类源码包的本来面目标题里的“海信商业管理系统”指零售行业铺量很大的那套收银与进销存软件前台 POS 卖货收款后台做商品档案、采购、库存、会员。所谓“完整 Delphi 源码版下载.zip”拿到的不是安装包而是开发维护人员留在项目现场的一堆 Delphi 工程——.dpr、.pas、.dfm外加数据库脚本与配置文件。这种包最典型的问题是“工程完整、路径不完整”源码里写死的服务器名、绝对目录、组件版本、注册表项和当前电脑全对不上。直接双击 .dpr 编译通常会连续报错连接数据库也大概率失败。所以拿到 zip 的正确顺序不是先装 IDE而是先从工程文件判断代码世代再补编译环境、接数据库最后才谈改动。下面按这个顺序走。2. 拆包与工程识别先从 .dpr 和 .dproj 判断版本2.1 解压后第一步先看文件清单而不是打开工程很多人习惯解压后直接双击里面的 .dpr 或者 .dproj这在组件版本普通但项目文件损坏时会浪费大量时间。我比较推荐先做一次文件统计看看这个包到底带了哪些文件和编译中间产物。在 Git Bash 或类 Unix 环境可以这么干find /d/src -type f | sed s/.*\.// | sort | uniq -c | sort -rn | head -20这段命令先把所有文件的后缀名提取出来再按出现次数从高到低排前 20 位。uniq -c给出每种后缀的文件数量sort -rn按数字倒序head -20只留出现最多的 20 类。Windows 自带 PowerShell 也能做同样的事Get-ChildItem D:\src -Recurse -File | Group-Object Extension | Sort-Object Count -Descending | Select-Object -First 20 Count, NameGroup-Object Extension按扩展名分组Sort-Object Count -Descending降序排列Select-Object -First 20截取前 20 条。看统计结果时关心的不是 .pas 多不多而是有没有四类文件.dcu 说明打包时连编译中间结果一起放进来了.bpl 是运行时包可能和本地 Delphi 版本冲突.exe 是编译好的成品可以当参照物.dproj 是工程文件本体。如果 .dcu 很多而 .pas 很少说明源码不完整后面编译要靠 .dcu 硬顶如果 .pas 很全但 .dcu 也在编译前最好删掉旧 .dcu否则 Delphi 会直接引用 .dcu 而不会重新编译改动过的 .pas。2.2 用工程文件扩展名锁定 Delphi 世代Delphi 二十多年里工程文件的命名规则换过好几轮看扩展名就能把版本锁定到大致年代这是判断这类源码包最省钱的方法。工程文件对应 Delphi 版本段说明.dpr .dofDelphi 47老牌项目常见编译选项写在 .dof 里.bdsprojDelphi 2005 / BDS 2006过渡期格式.dprojDelphi 2007 / 2009XE11现在还在用的主格式.groupproj与 .dproj 配套的项目组多工程打包时才出现在 Windows 命令行下直接看工程目录比一页页翻文件管理器快dir /b *.dpr *.dproj *.bdsproj *.groupproj 2nul/b表示只列出文件名不显示大小和日期2nul把“找不到文件”的错误输出丢弃避免满屏红色报错。定位到 .dproj 后用记事本打开搜索ProjectVersion字段这个字段里的数字和 IDE 世代一一对应字段值决定了你最好装对应世代的 IDE否则打开会警告项目版本过新或过旧编译期还会出现一系列莫名其妙的兼容性报错。有些 zip 包还会附一个已经编译好的 exe这时候可以用“Delphi 程序定位 OEP”里常用的思路确认编译代次把这个 exe 丢进十六进制编辑器搜索 Borland、CodeGear、Embarcadero 这几个字符串搜到哪个就是哪个时代的编译器生成的。这招比看关于对话框更可靠因为关于对话框的内容有时会被改掉。2.3 从 uses 子句判断代码的 Unicode 世代打开最顶层的 .dpr只看首屏的 uses 子句就能判断代码是 Unicode 还是 Ansi 时代。老写法是这样program POSMain; uses Forms, SysUtils, Classes, Windows, POS_Login in POS_Login.pas {frmLogin}, POS_Main in POS_Main.pas {frmMain};POS_Login in POS_Login.pas里in表示该单元属于当前工程花括号里的frmLogin是窗体标识编译后用于和 .dfm 里的窗体关联。而 Delphi 2009 以后的新工程uses 里会出现System.SysUtils、System.Classes这种带 System 前缀的写法。搜一下就知道grep -n System.SysUtils POSMain.dpr | head -5grep -n把匹配行号和内容一起打出来head -5只要前 5 行。这一步的意义不只是看着玩老代码用String在 Delphi 2009 之后默认变成UnicodeString凡是和第三方组件用PAnsiChar对接的地方会直接编译报错。遇到混编工程我一般先用 Unicode 版本编译如果报错超过五十处且集中在字符串处理就别硬撑了回到 Delphi 2007 把项目先跑通再说。提示工程管理器的版本和组件版本要一起判断。组件库装不上dpr 层面的一切分析都是空的。3. 编译环境与组件还原Delphi 工程重新跑起来的最小路径3.1 先解决第三方组件再谈编译商业管理系统和练习项目最大的区别在于组件引用。海信这类系统的界面是在第三方面板组件基础上搭的常见引用如下表这些组件在 IDE 自带安装包里基本找不到。组件名常见用途包名关键词DevExpress QuantumGrid表格、树形列表dxPackagesEhlibDBGridEh 增强数据集网格ehlibTeeChart销售与毛利图表TeeChartFastReport报表设计器FastReportGraphicEx扩展图像格式支持GraphicEx.dpk装组件时注意两点。第一组件包版本必须和 IDE 世代匹配DevExpress 有独立的“for Delphi XE2”这类安装包不能拿 2009 的包硬塞给 XE2第二装完以后要把组件包的 bpl 路径加进 IDE 的 Library 路径否则编译时提示Unit xxx not found而源码明明就在磁盘上。路径配置在Tools Options Library把组件解压根目录提上去保存后重启 IDE 再编译。这一步做完大多数“缺单元”的报错会消掉一大半。3.2 最小抢救顺序从 .dpr 移除失联单元组件都齐了依然编译不过多半是失联单元或条件编译符号的问题。我会先把 .dpr 复制一份改名 POSMain_backup.dpr然后一行一行过。典型开头长这样uses Forms, SysUtils, Classes, Windows, POS_Login in D:\Hisense\POS_Login.pas {frmLogin}, POS_Main in D:\Hisense\POS_Main.pas {frmMain}, POS_DataModule in D:\Hisense\POS_DataModule.pas {dmMain};报错时凡是Cannot open file xxx.dcu的单元直接在 uses 里注释掉行首加//把工程缩到最小可编译状态以后再逐个恢复。这一步有两个作用一是把第三方引用和业务代码解耦二是从使用关系上看到系统的模块划分——登录窗体、主界面、数据模块在 dpr 里的排列顺序其实就是系统初始化的先后顺序先创建的窗体不能放到后面去。3.3 用脚本处理路径写死和编码乱码老工程最磨人的是路径写死和文件编码两个问题。dpr 里in D:\Hisense\POS_Login.pas的绝对路径和本机目录不一致编译必然失败。批量替换用 Python 比记事本高效得多也不会漏文件from pathlib import Path root Path(rD:\src\HisensePOS) # 1. 将 GBK 编码的 .pas/.dfm/.dpr 转成 UTF-8 with BOM for ext in (*.pas, *.dfm, *.dpr): for f in root.rglob(ext): data f.read_bytes() if data[:3] ! b\xef\xbb\xbf: f.write_bytes(b\xef\xbb\xbf data.decode(gbk, errorsreplace).encode(utf-8)) # 2. 把所有绝对路径替换为当前工程根 for f in root.rglob(*.dproj): text f.read_text(encodingutf-8-sig, errorsignore) text text.replace(rD:\Hisense\POS, str(root)) f.write_text(text, encodingutf-8-sig)第一段对 GBK 编码的源码先转成 Unicode加上 BOM 头b\xef\xbb\xbf是为了让部分老版 Delphi 能正确识别 UTF-8 文件——它们不认无 BOM 的 UTF-8。data.decode(gbk, errorsreplace)里的errorsreplace表示遇到无法映射的字符就用替换符顶掉宁可丢个别字符也不让整个脚本崩掉。第二段str(root)是 Path 对象转字符串replace把旧路径改成当前目录。如果工程里还有.bdsproj或.dof把rglob(*.dproj)换成rglob(*)再在循环里判断后缀更稳妥。注意全局转码前做好备份转码过程不可逆。特别是 .dfm 里有图片资源时二进制资源段被误转码后会直接打不开窗体。4. 数据库连接与业务数据初始化直连、ODBC 与脚本顺序4.1 连接串通常不在界面里先找三处这类老商业系统的数据库连接串不会像新项目一样集中放在 appsettings 里常见三处工程目录下的 ini 或 config.txt、Windows 注册表 HKCU 下的一级目录、数据模块单元里的初始化代码。拿 ADO 方式举例数据模块里通常会看到类似代码procedure TdmMain.InitConnection; begin ADOConnection1.LoginPrompt : False; ADOConnection1.ConnectionString : ProviderSQLOLEDB.1;Password Trim(GetIni(DB, Password, hisense)) ;Persist Security InfoTrue;User ID Trim(GetIni(DB, User, sa)) ;Initial CatalogHisensePOS;Data Source Trim(GetIni(DB, Server, 127.0.0.1)); end;GetIni是读 ini 的函数三个字符串拼接分别对应数据库口令、用户名、数据库名、服务器地址。Initial Catalog指定要连的库名Data Source是服务器 IP 与端口。如果它用的是 BDE 方式配置点反而在 IDE 外面控制面板里的 BDE Administrator别名 AliasName 指向数据库路径。这个思路和“看连接串先看 ini 里的 DB 段”一样别上来就在代码里翻 Provider。4.2 执行建库脚本的顺序不能乱很多完整的源码包会把数据库脚本放在 Data 或 DB 文件夹下但顺次执行未必能成功顺序乱了最常见的报错是“表或对象已存在”以及外键引用失败。参考顺序如下序号脚本内容依赖1建库脚本Create Database无2操作员表、权限表、系统参数表建库完成3商品档案、供应商表参数表4库存表、变动流水表商品表5销售、采购单据与明细表库存表6初始化字典与演示数据全部表用 SQLCMD 执行批处理脚本时可以这样写sqlcmd -S 127.0.0.1 -U sa -P hisense123 -d master -i C:\DB\00_CreateDb.sql sqlcmd -S 127.0.0.1 -U sa -P hisense123 -d HisensePOS -i C:\DB\01_BaseData.sql sqlcmd -S 127.0.0.1 -U sa -P hisense123 -d HisensePOS -i C:\DB\02_Product.sql-S指定服务器-U和-P是登录账号密码-d指定数据库-i指定要执行的 SQL 脚本文件。如果脚本本身是 sqlcmd 语法写的文件里可以用:r指令 include 其他脚本类似这样:r C:\DB\01_BaseData.sql :r C:\DB\02_Product.sql:r这一行把另一个 sql 文件的内容原样塞进当前位置执行时顺序就有保证。另外要注意脚本里有没有USE HisensePOS和GO分批执行等事务提交完再跑下一步不然触发器依赖和自增 IDENTITY 可能错位。4.3 中文乱码从排序规则到字符串类型商业管理系统里中文最常在三个环节出乱码连接编码、SQL 排序规则、Delphi 侧的 AnsiString。数据表如果建在Chinese_PRC_CI_AS排序规则下和 Latin1 排序规则库互导时中文就变问号。最省事的排查方式是先查库默认排序规则SELECT DATABASEPROPERTYEX(HisensePOS, Collation) AS Collation;返回Chinese_PRC_CI_AS基本没问题返回SQL_Latin1_General_CP1_CI_AS就要在建库阶段改掉因为排序规则改起来费劲需要把表数据重建。网上常见的“Delphi sqlite 乱码”问题也是同一个思路先确认底层数据库的编码再回头改客户端代码里的字符集解析而不是对着 Len 和 Pos 瞎折腾。Delphi 侧如果代码是 XE 之前的 AnsiString 写的大量拼接在 XE 之后会变成 UnicodeString 拼接和PAnsiChar接口拼到一起时看似能编译运行时就会出现偶发乱码。排查这类问题我一般在连接建立成功后先执行一次SELECT SERVERPROPERTY(Collation)把数据库端字符集固定下来再谈改代码。5. 二次开发入口报表、授权与启动流程先看这几个位置5.1 从 Application.Initialize 往下跟启动顺序拿到能编译能连库的工程跟着启动流程看一遍就明白系统的初始化顺序。.dpr 里通常是这样begin Application.Initialize; Application.CreateForm(TdmMain, dmMain); Application.CreateForm(TfrmLogin, frmLogin); Application.Run; end;dmMain在所有窗体创建之前先创建意味着数据连接、权限读取都在登录界面弹出前完成frmLogin登录通过后才在主窗体里继续加载菜单权限。调试时先把断点下在Application.CreateForm(TdmMain, dmMain)这一行然后在TdmMain.FormCreate里 F8 跟一遍能看清楚连接串是从 ini 还是注册表读的。这个顺序理解以后想改“登录前的参数预检”或“登录后的首屏报表”都知道往哪里放代码。5.2 授权与加密狗代码优先找 Dog 和 Licence 单元商业源码里免不了加密狗或者注册码判断。找判断逻辑的位置基本固定名字里带 Dog、Key、Licence、Auth 的 .pas 文件。先全局搜一遍grep -ril ReadDog\|CheckLicence\|Auth *.pas | head -10-r表示递归-i忽略大小写-l只输出文件名head -10限定 10 个文件。找到后往下看代码常见写法是函数返回一个整型状态码0 表示授权有效其他值是不同错误等级。在调用处下断点调试会话里把返回值改成 0 或把布尔结果改成 True就能跳过授权直接看业务主流程。我的态度是这种改动只适合本地学习调试不要做成正式交付内容和商品化软件的正版授权不是一回事。5.3 报表与皮肤是改动最频繁的两处改完做最小回归实际改这类系统最多的功能是报表尤其是 FastReport 做的销售日报。FastReport 的模板在工程里一般是 .fr3 文件很多源码会把模板嵌入 exe这时改动方式是打开 FastReport 设计器修改后重新关联 .fr3。皮肤切换比报表好找通常在系统参数表里有一个 SkinName 字段改字段值就能换主题不用动窗体代码。每次改完一个模块最有效的验证是走一遍最小闭环新建一张销售单过账查库存打印一张销售小票。四步走完报表和单据两头的改动有没有互相影响一眼就清楚包括金额格式、小数位、负库存提示这类细节都在这个流程里暴露。本文还有配套的精品资源点击获取
返回列表