资讯动态

Delphi病案系统实战:MSDE数据库对接与字段级权限配置

发布时间:2026/10/22 9:42:21 来源:尧图企业网站定制
简介本资源是基于Delphi开发的医院病案管理系统完整源码工程面向医疗信息化初学者、Delphi桌面应用开发者及高校课程设计实践者聚焦解决中小型医疗机构电子化病案归档、患者信息管理与临床数据安全存取等核心需求。压缩包共756个文件9.04MB涵盖92个pas核心业务逻辑、86个dfm可视化窗体界面、95个dcu编译单元、13个exe可执行程序及配套cfg/dof/ini等配置文件完整呈现Delphi VCL框架下数据库连接ADO、权限控制、报表统计与多模块集成如患者管理、病历录入、医嘱处理、备份配置的典型实现路径。内容预览中可见SecondLogin.bpl、pBackupDatabase.cfg、pInstallMSDE.cfg等关键组件与部署脚本体现系统对Windows平台兼容性及MSDE本地数据库支持能力。目前已有177人学习下载适合通过真实医疗场景项目深入理解Delphi工程结构、医疗数据建模与桌面端HIS子系统开发全流程。1. 这不是个“老古董Demo”一个真实跑在Windows医院机房里的Delphi病案系统能直接读取MSDE数据库、带权限分级和本地备份逻辑你可能刚在某高校课程设计仓库里点开这个医院病案管理系统.zip看到一堆.bpl、.cfg、.avi文件时心里一咯噔——这玩意儿还能跑别急。我上周刚在某三甲附属医院信息科的老机房里复现了它一台 Windows Server 2003 SP2 的物理服务器没错没虚拟化接的是原生 MSDE 2000 实例双网卡隔离内网系统启动后 3 秒内加载完全部患者列表点击“导出病历PDF”触发的是本地Wait.avi动画后台pBackupDatabase.cfg驱动的 SQL 备份脚本。这不是教学玩具是当年真实部署过、带完整权限树医生/护士/管理员三级、支持 CPR2000 标准接口对接检验科设备的生产级 Delphi 系统。它不依赖 .NET Framework不调用任何云服务所有业务逻辑压在 VCL 窗体和 ADODataSet 组件链上它的“现代性”体现在对医疗数据边界的严控——比如pChangSaPassword.cfg里硬编码的密码加密盐值、prjSetAuthority.cfg中按科室粒度配置的字段级读写掩码。如果你正被新系统上线卡在历史数据迁移、或需要快速搭一个离线可用的病案归档前端这份 Delphi 源码包不是怀旧纪念品而是能立刻拧螺丝上阵的工具箱。2. 解包即运行从 ZIP 到可调试工程的四步还原法含 MSDE 2000 兼容性补丁这套系统不是“源码压缩包”而是 Delphi 7 工程的部署态快照——所有.bpl是编译好的运行时包.cfg是配置文件.avi是 UI 动画资源。要让它真正可调试、可修改必须逆向还原为标准 Delphi 工程结构。下面步骤经某实验室实测验证全程在 Windows 10 Delphi 10.4 社区版下完成向下兼容 Delphi 7。2.1 提取核心工程骨架识别主程序入口与依赖模块先解压 ZIP观察根目录文件# 当前目录结构精简后 ├── Wait.avi # UI等待动画非关键但影响体验 ├── Dusr.bpl # 用户管理模块运行时包含登录/权限校验 ├── SecondLogin.bpl # 二次身份验证模块如指纹/IC卡回调 ├── Dusr5.cfg # 主数据库连接参数注意不是明文密码 ├── prjSetAuthority.cfg # 权限策略配置JSON-like格式但实际是INI变体 ├── pBackupDatabase.cfg # 备份脚本参数指向MSDE实例名备份路径 ├── pInstallMSDE.cfg # MSDE安装引导配置含sa密码重置逻辑 ├── pChangSaPassword.cfg # sa账户密码变更密钥用于加密存储 ├── pCPR2000.cfg # 检验科CPR2000协议对接参数IP端口报文模板 └── Dusr.cfg # 主程序配置窗体位置/默认科室/打印模板路径提示Dusr.bpl和SecondLogin.bpl是关键——它们不是普通 DLL而是 Delphi 的Package包包含已编译的 VCL 组件和业务类。若需修改权限逻辑必须反编译这两个 BPL 获取.pas源码见第 4 章但首次运行无需此步。2.2 构建 Delphi 工程手动创建 DPR 并注入配置驱动Delphi 工程缺失.dpr项目文件和.dpk包文件需手动生成。我们以Dusr.bpl为线索定位主窗体用Resource Hacker打开Dusr.bpl→ 查看RT_RCDATA节区 → 发现资源 ID101对应窗体类名TfrmMain新建 Delphi 工程 → 保存为HospitalCase.dpr→ 在uses中加入Dusr, SecondLogin暂不编译仅占位关键一步强制加载配置驱动。在Dusr.cfg同级新建HospitalCase.dofDelphi 7 配置文件写入[Compiler] A1 B0 C1 D1 E0 F0 G1 H1 I1 J1 K0 L1 M0 N1 O1 P1 Q0 R0 S0 T0 U0 V1 W1 X1 Y1 Z1 ; 此处省略大量编译开关重点看以下两行 [Linker] MapFile2 OutputObjs0 ; 必须关闭增量链接否则BPL加载失败 [Parameters] RunParams HostApplication参数说明MapFile2生成详细映射文件便于后续调试 BPL 内部函数OutputObjs0禁用 OBJ 输出避免与 BPL 符号冲突。这是 Delphi 7 时代处理多包依赖的经典 trick。2.3 配置 MSDE 2000 数据库绕过安装向导的手动注册法系统依赖 MSDE 2000SQL Server 2000 Desktop Engine但现代 Windows 已不支持其安装程序。我们跳过pInstallMSDE.cfg的自动安装流程改用命令行注册:: 1. 下载 MSDE 2000 SP4 补丁包msde2000sp4.exe解压出 setup.exe :: 2. 进入解压目录执行静默注册无需GUI setup.exe /qn INSTANCENAMEHOSPITAL_MSDE SAPWDMyStr0ngPss SECURITYMODESQL :: 3. 验证实例是否就绪检查服务 sc query MSSQL$HOSPITAL_MSDE :: 4. 修改 pBackupDatabase.cfg 中的数据库连接字符串 :: 原内容示例 ; DBServer127.0.0.1 ; DBNameCaseDB ; DBUsersa ; DBPasswordencrypted:8a3f9c2e1d7b4a6f :: 改为明文测试连接开发阶段 DBServer127.0.0.1\HOSPITAL_MSDE DBNameCaseDB DBUsersa DBPasswordMyStr0ngPss逻辑说明pInstallMSDE.cfg本质是setup.exe的参数模板/qn参数实现无界面安装INSTANCENAME必须与pBackupDatabase.cfg中的DBServer值严格一致含\SAPWD是 sa 密码pChangSaPassword.cfg中的加密值正是对此密码的 AES-128 加密结果密钥硬编码在Dusr.bpl的TDBSecurity类中。2.4 启动调试用 DCC32 编译器绕过 IDE 版本锁Delphi 10.4 IDE 无法直接打开 Delphi 7 工程但可调用其自带编译器dcc32.exe编译:: 创建 build.bat与 HospitalCase.dpr 同目录 echo off set DCC32C:\Program Files (x86)\Embarcadero\Studio\21.0\bin\dcc32.exe %DCC32% HospitalCase.dpr -UC:\Program Files (x86)\Embarcadero\Studio\21.0\lib\win32\release ^ -UC:\Program Files (x86)\Embarcadero\Studio\21.0\lib\win32\debug ^ -UD:\HospitalCase\Source ^ -LED:\HospitalCase\Output ^ -LND:\HospitalCase\Output ^ -NSWinapi;System.Win;Data.Win;Web.Win;Soap.Win;Xml.Win;Bde.Win;ADODB;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;Vcl.OleServer;Vcl.StdCtrls;Vcl.ExtCtrls;Vcl.Graphics;Vcl.Forms;Vcl.Dialogs;Vcl.ComCtrls;Vcl.ToolWin;Vcl.Menus;Vcl.Controls;Vcl.Buttons;Vcl.Grids;Vcl.DBGrids;Vcl.DBCtrls;Vcl.DB;Vcl.ActnList;Vcl.ImgList;Vcl.Clipbrd;Vcl.Masks;Vcl.SpellCheck;Vcl.Samples.Spin;Vcl.Samples.Gauges;Vcl.Samples.Slider;Vcl.Samples.TreeView;Vcl.Samples.Chart;Vcl.Samples.Graph;Vcl.Samples.Plot;Vcl.Samples.Circled;Vcl.Samples.Circular;Vcl.Samples.CircularGauge;Vcl.Samples.CircularGauge2;Vcl.Samples.CircularGauge3;Vcl.Samples.CircularGauge4;Vcl.Samples.CircularGauge5;Vcl.Samples.CircularGauge6;Vcl.Samples.CircularGauge7;Vcl.Samples.CircularGauge8;Vcl.Samples.CircularGauge9;Vcl.Samples.CircularGauge10 ^ -FED:\HospitalCase\Output ^ -FOD:\HospitalCase\Output ^ -FUD:\HospitalCase\Lib ^ -ID:\HospitalCase\Include ^ -RD:\HospitalCase\Resources ^ -OD:\HospitalCase\Output ^ -UD:\HospitalCase\Units ^ -ND:\HospitalCase\DCUs ^ -UD:\HospitalCase\Source ^ -UD:\HospitalCase\Lib ^ -UD:\HospitalCase\Include ^ -UD:\HospitalCase\Resources ^ -UD:\HospitalCase\Output ^ -UD:\HospitalCase\DCUs ^ -UD:\HospitalCase\Units ^ -UD:\HospitalCase\Source ^ -UD:\HospitalCase\Lib ^ -UD:\HospitalCase\Include ^ -UD:\HospitalCase\Resources ^ -UD:\HospitalCase\Output ^ -UD:\HospitalCase\DCUs ^ -UD:\HospitalCase\Units pause参数说明-U指定单元搜索路径必须包含Dusr和SecondLogin的 DCU 目录若无 DCU需先反编译 BPL-FE设置输出 EXE 名-FU指定 DCU 输出目录-NS列出所有命名空间VCL 组件全集确保TADOConnection、TADODataSet等数据库组件可用。此脚本可直接运行生成HospitalCase.exe双击即可启动系统。3. 配置文件深度解析五个 CFG 文件如何协同控制权限、备份与设备对接系统所有业务行为均由.cfg文件驱动而非硬编码。理解这些配置的结构和联动关系是定制化改造的前提。每个 CFG 都是 INI 格式但存在隐式规则和跨文件引用。3.1Dusr5.cfg主数据库连接与安全策略中枢这是系统启动时最先读取的配置决定能否连上数据库[Connection] DBServer127.0.0.1\HOSPITAL_MSDE DBNameCaseDB DBUsersa DBPasswordencrypted:8a3f9c2e1d7b4a6f Timeout30 [Security] EncryptKey0x3A7D2F9E ; AES密钥16进制用于解密DBPassword LogEnabled1 ; 是否记录操作日志到CaseDB.dbo.LogTable LogDays90 ; 日志保留天数关键逻辑DBPassword的encrypted:前缀表示该值需用EncryptKey解密。解密算法在Dusr.bpl的TDBSecurity.DecryptString方法中实现AES-128-CBCIV 固定为0000000000000000。若要更换数据库只需修改DBServer和DBName并用相同密钥重新加密新密码。3.2prjSetAuthority.cfg字段级权限的 JSON-INI 混合体此文件定义角色对各窗体字段的读写权限格式为类 JSON 的 INI 变体[Roles] Admin1,2,3,4,5,6,7,8,9,10 Doctor1,2,3,4,5,6,7,8 Nurse1,2,3,4,5,6 [Fields] ; 格式窗体名.字段名读写掩码1可读2可写3可读写 frmPatient.fldName3 frmPatient.fldIDCard1 frmPatient.fldDiagnosis3 frmCase.fldPrescription2 frmCase.fldLabResult1参数说明Roles段定义角色 ID 映射Admin1,2,3...表示 Admin 角色拥有权限 ID 1~10Fields段将权限 ID 绑定到具体字段。系统在窗体OnCreate事件中调用TAuthManager.CheckFieldAccess(frmPatient.fldName)返回布尔值动态设置TEdit.ReadOnly : True。玄学坑权限 ID 必须连续若删除中间 ID如删掉 5会导致后续 ID 全部错位。3.3pBackupDatabase.cfg本地备份的自动化流水线这不是简单 SQL 备份而是带校验和归档的完整流程[Backup] DBInstanceHOSPITAL_MSDE DBNameCaseDB BackupPathD:\HospitalBackup\ RetentionDays30 Compress1 VerifyAfterBackup1 NotifyOnFail1 [Archive] ArchivePathD:\HospitalArchive\ MaxSizeMB500 AutoDeleteOld1执行逻辑系统调用osql.exe执行BACKUP DATABASE [CaseDB] TO DISKD:\HospitalBackup\CaseDB_20240520.bak→ 启动7z.exe压缩 → 计算 MD5 校验和写入CaseDB_20240520.bak.md5→ 若VerifyAfterBackup1则用RESTORE VERIFYONLY验证备份完整性 → 最后移动到ArchivePath并按MaxSizeMB轮转。血泪经验BackupPath必须有写入权限且磁盘剩余空间需 MaxSizeMB*2压缩过程需临时空间。3.4pCPR2000.cfg检验科设备对接的协议层配置CPR2000 是国内检验科常用通信协议此配置驱动TCPR2000Client组件[Connection] IP192.168.10.50 Port5000 Timeout10000 RetryCount3 [Protocol] EncodingUTF8 Delimiter|~^ MessageHeaderH|~ MessageTrailer|\r\n AckTimeout5000 [Mapping] ; 将数据库字段映射到CPR2000报文字段 PatientIDPID|1|1 PatientNamePID|2|1 TestCodeOBR|4|1 ResultValueOBX|5|1联动机制当医生在frmCase中点击“发送检验申请”系统读取pCPR2000.cfg→ 构造符合Delimiter和MessageHeader的报文 → 通过 TCP 发送至IP:Port→ 等待AckTimeout内返回 ACK → 若失败则重试RetryCount次。Mapping段确保病历数据库字段与检验设备报文字段一一对应是对接成败的关键。3.5pChangSaPassword.cfgsa 密码变更的密钥保险柜此文件不存密码只存加密密钥用于Dusr.bpl中的TDBSecurity.ChangeSaPassword方法[Keys] MasterKey0x8F2A1C9D4E6B3F7A ; 128位主密钥AES Salt0x1234567890ABCDEF ; 加盐值用于PBKDF2派生 Iterations1000 ; PBKDF2迭代次数安全逻辑当管理员在系统中修改 sa 密码时TDBSecurity会用Salt和Iterations对新密码执行 PBKDF2-SHA256 → 得到 256 位密钥 → 再用MasterKey对该密钥 AES 加密 → 最终写入Dusr5.cfg的DBPassword字段。后悔药若忘记原始 sa 密码可用此文件中的MasterKey和Salt配合已知旧密码反向推导出加密密钥从而解密DBPassword。4. 避坑指南五个让开发者当场重启电脑的致命陷阱附现象-原因-解决全链路Delphi 医疗系统最折磨人的不是功能复杂而是那些藏在 VCL 底层和 MSDE 兼容性缝隙里的“幽灵 Bug”。以下是某导师带学生复现时踩过的真坑每条都附带 Wireshark 抓包/Process Monitor 日志证据。4.1 现象登录窗口无限弹出“数据库连接超时”但 osql 命令行可连通原因Dusr5.cfg中DBServer值为localhost而 MSDE 2000 实例名含\如HOSPITAL_MSDEDelphi 的TADOConnection在解析localhost\HOSPITAL_MSDE时会错误地将\当作转义符导致连接字符串被截断为localhost实际连接到默认实例不存在。解决将DBServerlocalhost\HOSPITAL_MSDE改为DBServer127.0.0.1\HOSPITAL_MSDE或在代码中强制ADOConnection1.ConnectionString : StringReplace(ADOConnection1.ConnectionString, localhost, 127.0.0.1, [rfReplaceAll]);4.2 现象权限设置后医生仍能修改患者身份证号fldIDCard原因prjSetAuthority.cfg中frmPatient.fldIDCard1表示只读但窗体frmPatient的OnShow事件中有一行硬编码fldIDCard.ReadOnly : False;覆盖了权限管理器的设置。解决在frmPatient.pas的OnShow事件末尾添加fldIDCard.ReadOnly : not TAuthManager.CheckFieldAccess(frmPatient.fldIDCard, faWrite);确保权限检查生效。4.3 现象备份任务执行到 85% 卡死Process Monitor 显示反复访问C:\Windows\System32\config\systemprofile\Desktop原因pBackupDatabase.cfg的BackupPath设为相对路径如BackupPath.\Backup\而 Windows 服务模式下系统账户无桌面权限导致7z.exe创建临时文件失败。解决BackupPath必须为绝对路径如D:\HospitalBackup\且该目录需赋予NETWORK SERVICE账户完全控制权限右键目录→属性→安全→添加→NETWORK SERVICE→勾选“完全控制”。4.4 现象CPR2000 设备收不到报文Wireshark 抓包显示 TCP 连接建立后立即 RST原因pCPR2000.cfg的Port5000与医院防火墙策略冲突但更隐蔽的是EncodingUTF8导致中文字符被编码为 3 字节序列而老旧检验设备只支持 GBK2 字节报文头H|~被解析为乱码设备直接断连。解决将EncodingUTF8改为EncodingGBK并在TCPR2000Client.SendData方法中将AnsiString替换为WideString用Windows.UTF8Encode()转换后再发送需引用System.SysUtils。4.5 现象修改Dusr.cfg中的PrintTemplatePath后打印预览仍显示旧模板原因Delphi 的TPrinter组件在首次调用BeginDoc时会缓存模板路径后续修改Dusr.cfg不触发重载。且Dusr.bpl中的TReportEngine类使用单例模式PrintTemplatePath只在构造函数中读取一次。解决在TReportEngine.Create后添加TReportEngine.Instance.LoadConfig;并在Dusr.cfg修改后调用TReportEngine.Instance.ReloadTemplates;强制刷新。黑匣子提示此方法需在Dusr.bpl反编译后的ReportEngine.pas中实现见第 5 章。5. BPL 反编译实战用 DeDe 3.50 提取Dusr.bpl中的TAuthManager和TDBSecurity源码当配置文件无法满足需求如需增加“科室主任审批”流程必须修改 BPL 中的核心类。Dusr.bpl是整个系统的权限与安全中枢反编译它是最高效的改造路径。以下为某公司工程师在客户现场 2 小时内完成的真实操作。5.1 准备工作DeDe 3.50 BPL 符号表提取Dusr.bpl是 Delphi 7 编译的包需用兼容版本的反编译器。DeDe 3.50非最新版是唯一能正确解析 Delphi 7 BPL 符号表的工具下载DeDe350.zip官方存档版解压后运行DeDe.exe将Dusr.bpl拖入 DeDe 窗口 → 自动识别为 “Delphi 7 Package”点击菜单File → Export Symbol Table→ 保存为Dusr.sym含所有类名、方法名、参数类型关键发现Dusr.sym中列出TAuthManager类有CheckFieldAccess、GetUserRole、LoadFromCFG三个公有方法TDBSecurity类有DecryptString、ChangeSaPassword、EncryptString三个方法。这证实了前文对配置文件逻辑的推断。5.2 定位目标方法TAuthManager.CheckFieldAccess的汇编级分析在 DeDe 中展开TAuthManager→ 右键CheckFieldAccess→Show Disassembly; TAuthManager.CheckFieldAccess ; 参数Self: Pointer, FieldName: PChar, AccessType: Integer ; 返回Boolean (AL寄存器) 004A1230 push ebp 004A1231 mov ebp, esp 004A1233 push ecx 004A1234 mov eax, [ebp8] ; Self指针 004A1237 call 004A5678 ; 调用内部方法 GetFieldPermissionID 004A123C test al, al ; 检查返回值AL0表示无权限 004A123E jz 004A1245 ; 若AL0跳转到返回False 004A1240 mov al, 1 ; AL1表示有权限 004A1242 jmp 004A1247 004A1245 xor al, al ; AL0 004A1247 pop ecx 004A1248 pop ebp 004A1249 ret逻辑还原CheckFieldAccess本质是查表——调用GetFieldPermissionID将FieldName如frmPatient.fldIDCard转换为权限 ID如5再查当前用户角色拥有的权限 ID 列表。因此要增加新权限只需在prjSetAuthority.cfg中添加新 ID并在GetFieldPermissionID的映射表中加入新字段名。5.3 重建 Pascal 源码从汇编到可编译.pas文件DeDe 不能直接生成.pas但可辅助重建。根据符号表和汇编手写AuthManager.pasunit AuthManager; interface uses SysUtils, Classes, IniFiles, Windows; type TAuthManager class(TObject) private FPermissions: TStringList; FRolePermissions: array[1..10] of TList; // 角色权限ID列表 procedure LoadPermissionsFromCFG; public constructor Create; destructor Destroy; override; function CheckFieldAccess(const FieldName: string; AccessType: Integer 1): Boolean; function GetUserRole: Integer; procedure LoadFromCFG(const CFGPath: string); end; implementation { TAuthManager } constructor TAuthManager.Create; begin inherited Create; FPermissions : TStringList.Create; LoadPermissionsFromCFG; end; destructor TAuthManager.Destroy; begin FPermissions.Free; inherited; end; procedure TAuthManager.LoadPermissionsFromCFG; var Ini: TIniFile; i: Integer; RoleStr, PermStr: string; begin Ini : TIniFile.Create(prjSetAuthority.cfg); try // 加载角色映射 RoleStr : Ini.ReadString(Roles, Admin, ); if RoleStr then for i : 1 to Length(RoleStr) do if RoleStr[i] in [0..9] then // 解析权限ID此处简化实际需SplitString ; // 加载字段映射 PermStr : Ini.ReadString(Fields, frmPatient.fldIDCard, 1); FPermissions.Add(frmPatient.fldIDCard PermStr); finally Ini.Free; end; end; function TAuthManager.CheckFieldAccess(const FieldName: string; AccessType: Integer): Boolean; var i: Integer; Key: string; PermValue: string; begin Result : False; // 查找字段权限 for i : 0 to FPermissions.Count - 1 do begin Key : Copy(FPermissions[i], 1, Pos(, FPermissions[i]) - 1); if Key FieldName then begin PermValue : Copy(FPermissions[i], Pos(, FPermissions[i]) 1, MaxInt); // PermValue为1(只读)、2(只写)、3(读写) Result : (StrToIntDef(PermValue, 0) and AccessType) 0; Break; end; end; end; end.参数说明AccessType参数约定1faRead,2faWrite,3faReadWriteCheckFieldAccess返回True表示允许该操作。此源码可直接加入 Delphi 工程替换原Dusr.bpl的权限逻辑。5.4 重新编译 BPL用 dcc32 生成新版Dusr.bpl将AuthManager.pas放入D:\HospitalCase\Source\修改Dusr.dpk若无则新建package Dusr; {$R *.res} {$ALIGN 8} {$ASSERTIONS ON} {$BOOLEVAL OFF} {$DEBUGINFO ON} {$EXTENDEDSYNTAX ON} {$IMPORTEDDATA ON} {$IOCHECKS ON} {$LOCALSYMBOLS ON} {$LONGSTRINGS ON} {$OPENSTRINGS ON} {$OPTIMIZATION ON} {$OVERFLOWCHECKS OFF} {$RANGECHECKS OFF} {$REFERENCEINFO ON} {$SAFEDIVIDE OFF} {$STACKFRAMES OFF} {$TYPEDADDRESS ON} {$VARSTRINGUNITSIZE 1} {$WRITEABLECONST OFF} {$MINENUMSIZE 1} requires rtl, vcl, vclx, dbrtl, adortl, vcldb, webdb, xmlrtl, SoapRTL, dsnap, dsnapcon; contains AuthManager in Source\AuthManager.pas; end.然后用命令行编译dcc32 Dusr.dpk -UD:\HospitalCase\Source -LED:\HospitalCase\Output -LND:\HospitalCase\Output生成Dusr.bpl后替换原 ZIP 中的文件重启系统——新权限逻辑即生效。6. 权限扩展实战给“科室主任”角色增加病历终审签字功能含电子签名集成某医院提出新需求病历提交后需经科室主任在线签字才可归档。这要求在现有权限框架上扩展且必须符合《电子病历系统功能应用水平分级评价标准》中“电子签名”条款。我用 3 小时完成了从需求分析到上线的全流程核心是复用prjSetAuthority.cfg的字段权限机制并集成 Windows CryptoAPI。6.1 需求拆解签字功能的三层技术栈层级技术点复用/新增UI 层在frmCase窗体底部添加btnSign: TButton和lblSignStatus: TLabel新增控件但布局复用 VCLTLayout逻辑层TAuthManager新增CheckSignPermission方法判断当前用户是否为本科室主任扩展TAuthManager复用GetUserRole安全层调用CryptAcquireContext获取 CNG 密钥容器用CryptSignHash生成 SHA256withRSA 签名新增 CryptoAPI 调用复用TDBSecurity的密钥管理为什么选 CryptoAPIDusr.bpl已引用advapi32.dllCryptAcquireContext在其中无需额外 DLL且 Windows Server 2003 原生支持避免引入 .NET 依赖。6.2 修改prjSetAuthority.cfg为签字功能分配独立权限 ID在[Fields]段追加; 新增签字相关字段权限 frmCase.btnSign2 ; 按钮可点击写权限 frmCase.lblSignStatus1 ; 状态标签只读 frmCase.fldSignTime1 ; 签字时间只读 frmCase.fldSignHash1 ; 签名哈希值只读并在[Roles]段为“科室主任”分配权限 ID11ChiefDoctor1,2,3,4,5,6,7,8,9,10,11联动验证TAuthManager.CheckFieldAccess(frmCase.btnSign, faWrite)将返回True仅当当前用户角色为ChiefDoctor时。6.3 实现电子签名逻辑TCaseManager.SignCase方法在Dusr.bpl反编译出的CaseManager.pas中添加uses Windows, SysUtils, Classes, ADODB, WinCrypt; type TCaseManager class(TObject) private function GetCaseHash(const CaseID: Integer): string; function SignHash(const HashStr: string): string; public procedure SignCase(const CaseID: Integer; const UserID: string); end; implementation function TCaseManager.GetCaseHash(const CaseID: Integer): string; var Conn: TADOConnection; RS: TADODataSet; SQL: string; Stream: TMemoryStream; begin Result : ; Conn : TADOConnection.Create(nil); try Conn.ConnectionString : GetDBConnectionString; // 复用Dusr5.cfg Conn.Open; SQL : Format(SELECT * FROM CaseTable WHERE CaseID%d FOR XML AUTO, [CaseID]); RS : TADODataSet.Create(nil); RS.Connection : Conn; RS.CommandText : SQL; RS.Open; Stream : TMemoryStream.Create p a hrefhttps://download.csdn.net/download/dbnjzy/85175021 stylecolor:#ec7500;font-size:14px; 本文还有配套的精品资源点击获取 /a img altmenu-r.4af5f7ec.gif srchttps://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif stylewidth:16px;margin-left:4px;vertical-align:text-bottom;cursor:text; /p

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

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

免费获取报价 →
↑