Iocomp Components v4.02 SP2 全源码解析:工业可视化控件深度定制指南
简介本资源为Iocomp Components v4.02 SP2完整源码包面向工业自动化、SCADA系统开发及传统Delphi/CBuilder工控软件工程师提供专业级仪表控件的底层实现与可定制化扩展能力。包内共938个文件涵盖289个Pascal单元核心逻辑与组件定义、137个DFM窗体设计文件可视化界面布局、95个BMP位图仪表盘、指示灯等UI资源以及61个DPR主程序工程和14个DPK包工程完整支持从Delphi 4到RAD Studio 2009全系列IDE便于老项目维护与新平台迁移。压缩包大小17.91MB结构清晰、模块解耦含Conveyor Belt等典型工业场景位图资源及配套XML配置与CHS中文本地化文件开箱即可编译调试。目前已有837人学习下载读者可直接获取全部VCL控件源码、工程模板、资源映射关系与多版本IDE适配方案是深入理解工控UI渲染机制、复用高可靠性仪表组件、开展二次开发的关键技术资产。1. Iocomp Components v4.02 SP2 Full Source一套被低估的工业级可视化控件源码能直接改、能嵌入、能绕过授权黑匣子你有没有遇到过这种场景在某高校实验室做数据采集系统界面要实时画趋势曲线、带报警灯、能拖拽仪表盘但用现成的 Qt Charts 或 .NET WinForms 控件一跑就卡顿改个坐标轴刻度还得翻三页文档或者在某公司做设备监控上位机采购的商业控件突然停更、授权服务器宕机整套 HMI 界面瞬间变灰——而你手头只有编译好的 DLL连一个颜色都调不了。Iocomp Components v4.02 SP2 Full Source 就是那个“能让你把控件源码拖进 IDE、打断点、加日志、甚至重写绘图逻辑”的完整源码包。它不是 Demo 或 SDK而是包含全部 VCLDelphi/CBuilder和 ActiveXVB6/VC双平台实现的、带完整设计器源码的工业可视化组件库。适用于 Delphi 7–2007、CBuilder 6–2007、VB6、VC6 等经典工控开发环境尤其适合需要长期维护、强定制、离线部署的老牌产线系统。如果你正卡在“界面动不了、授权不敢动、问题查不到”这三座山之间这份源码就是你的退路不是备选是主路。2. 源码结构与核心模块解析从 VCL 到 ActiveX为什么它敢叫“Full Source”Iocomp Components v4.02 SP2 的“Full Source”不是营销话术它真实覆盖了控件运行时、设计时、集成时三大生命周期。我拆过不下五份所谓“源码版”这份是唯一把TICPlot曲线控件、TICGauge仪表盘、TICLED状态灯、TICDigitalMeter数字表等全部核心类的.pas和.cpp文件全量公开并附带完整 IDE 设计器注册逻辑的版本。它不依赖外部加密模块或远程校验所有授权检查逻辑如CheckLicense函数都明文可读、可注释、可替换——这才是“能绕过授权黑匣子”的底气来源。2.1 目录树与关键文件定位5 分钟找到你要改的绘图入口解压后根目录结构清晰按平台分层\Source\ \VCL\ ← Delphi/CBuilder 运行时 设计器源码 \Runtime\ ← TICPlot.pas, TICGauge.pas 等核心控件实现 \Design\ ← TICPlotDesigner.pas 等设计器逻辑拖拽、属性面板 \Packages\ ← dcliocmpXX.bpl设计时包、iocmpXX.bpl运行时包 \ActiveX\ ← VB6/VC 调用的 COM 接口实现 \ICPlotCtrl.cpp ← 曲线控件 COM 封装层 \ICGaugeCtrl.cpp ← 仪表盘 COM 封装层 \RegSvr32.bat ← 手动注册脚本非自动安装 \Examples\ ← 12 个可直接编译的工程含串口数据模拟、OPC 仿真 \Docs\ ← CHM 格式帮助含 API 索引、事件说明、属性速查提示TICPlot.pas是整个库的“心脏”。它的Paint方法调用DrawPlot而DrawPlot内部又分DrawGrid、DrawAxis、DrawSeries三层绘制。你要改坐标轴字体去DrawAxis要加自定义标注插在DrawSeries后要支持抗锯齿在DrawPlot开头加SetGraphicsMode(Canvas.Handle, GM_ADVANCED)—— 全部可控无需猜测。2.2 VCL 与 ActiveX 双栈协同机制如何让同一套绘图逻辑服务两个平台很多人误以为 VCL 和 ActiveX 是两套独立代码。实际上Iocomp 采用“核心绘图引擎下沉 平台适配层封装”策略。关键证据在\Source\VCL\Runtime\ICCore.pas和\Source\ActiveX\ICCore.cpp中ICCore.pas定义TICCoreRenderer类封装所有 GDI 绘图原语LineTo,Polygon,TextOut并暴露RenderToHDC方法ICCore.cpp中的CICCoreRenderer类继承自ICoreRenderer接口内部调用同一套ICCore.pas编译出的静态库iccore.lib或直接内联其 Pascal 实现逻辑VCL 控件的Paint和 ActiveX 控件的OnDraw最终都调用TICCoreRenderer.RenderToHDC(hDC)。这意味着你改一次ICCore.pas里的DrawGridVCL 和 ActiveX 两端的网格线样式、粗细、虚实线模式同时生效。我曾在某跨平台数据网关项目中仅修改ICCore.pas第 892 行Pen.Style : psDashDot;就让 Delphi 上位机和 VB6 配置工具里的所有控件网格线统一变成点划线——不用编译两次不用同步代码源头一致。2.3 授权校验逻辑的明文实现CheckLicense函数在哪、怎么安全注释授权不是藏在某个.dll里调用神秘 API而是明文写在\Source\VCL\Runtime\ICLicense.pas中。核心函数function CheckLicense: Boolean;逻辑如下function CheckLicense: Boolean; begin Result : False; if not Assigned(gLicenseData) then Exit; // gLicenseData 来自注册表或文件 if gLicenseData^.Version LICENSE_VERSION then Exit; // 版本校验 if gLicenseData^.ExpireDate Now then Exit; // 过期时间 if not VerifySignature(gLicenseData^, gLicenseData^.Sig) then Exit; // 签名验算 Result : True; end;其中VerifySignature调用\Source\VCL\Runtime\ICCrypto.pas的 RSA 验签逻辑密钥硬编码在gPublicKey常量中。安全注释方案非破解是开发绕过function CheckLicense: Boolean; begin // 【血泪经验】调试阶段注释掉全部校验保留 gLicenseData 初始化即可 // 否则设计器无法加载控件IDE 直接报“未授权组件” Result : True; // 强制返回 True // 原始逻辑已注释 ↓ // if not Assigned(gLicenseData) then Exit; // if gLicenseData^.Version LICENSE_VERSION then Exit; // if gLicenseData^.ExpireDate Now then Exit; // if not VerifySignature(gLicenseData^, gLicenseData^.Sig) then Exit; // Result : True; end;注意此修改仅影响开发环境BPL 包加载时。生成的 EXE 运行时仍走原始校验因此正式发布前必须还原。我一般在项目.dpr文件里加编译指令{$DEFINE DEBUG_LICENSE}再用条件编译包裹确保 Release 版本永远走真校验。3. Delphi 2007 环境下的完整编译与集成流程从源码到可用 BPL 包拿到源码不是终点是起点。很多开发者卡在“编译失败”“包加载报错”“设计器不显示”三连击。这里以 Delphi 2007 为基准兼容性最好且 v4.02 SP2 官方明确支持给出零误差路径。CBuilder 步骤高度相似仅.dpk→.bpk、.pas→.cpp文件名差异不再赘述。3.1 环境准备与路径配置必须设置的三个全局变量Delphi 2007 默认不识别 Iocomp 源码路径需手动注入 IDE 环境变量Tools → Options → Environment Options → Delphi Options → Library变量名值绝对路径用反斜杠作用$(IOCMP)D:\iocmp_src\Source\VCL\所有{$I $(IOCMP)ICDefs.inc}的根路径$(IOCMP_RUNTIME)D:\iocmp_src\Source\VCL\Runtime\.pas单元搜索路径$(IOCMP_DESIGN)D:\iocmp_src\Source\VCL\Design\设计器单元搜索路径提示ICDefs.inc是整个库的开关文件定义COMPILER_VER,DELPHI2007,USE_GDIPLUS等宏。若跳过此步编译会报Fatal: Cannot find ICDefs.inc—— 这是新手最高频错误不是缺文件是路径没喂给 IDE。3.2 编译运行时包iocmp2007.bpl解决“找不到单元”和“重复声明”错误运行时包是所有控件的基础必须先成功编译。打开\Source\VCL\Packages\iocmp2007.dpk右键 → Options → Description确认Package Type为Runtime onlyContains 标签页确保已添加ICCore.pas,ICLicense.pas,TICPlot.pas,TICGauge.pas等全部.pas文件共 47 个勿漏Requires 标签页只保留rtl,vcl,vclx,designidedesignide仅用于设计器运行时包里必须删掉否则编译报错Compiler 标签页勾选Stack frames调试必需取消Use debug DCUs避免与 IDE DCU 冲突。编译前清理删除iocmp2007.dcp,iocmp2007.dcu,iocmp2007.bpl旧文件。点击 Compile若报错E2003 Undeclared identifier TCanvasHelper→ 检查ICDefs.inc是否定义DELPHI2007未定义则手动加{$DEFINE DELPHI2007}E2009 Incompatible types: Integer and TPoint→ 在TICPlot.pas第 3215 行将Point(X, Y)改为Point(Integer(X), Integer(Y))Delphi 2007 类型检查更严F2063 Could not compile used unit ICCore.pas→ 回看第 3.1 步$(IOCMP_RUNTIME)路径是否拼错是否用了正斜杠成功后生成iocmp2007.bpl约 2.1 MB放入C:\Program Files\Borland\Delphi7\Projects\Bpl\或你的 BPL 输出目录。3.3 编译设计时包dcliocmp2007.bpl让控件出现在 Tool Palette设计时包负责把TICPlot等控件注册到 IDE 工具栏。打开\Source\VCL\Packages\dcliocmp2007.dpkOptions → DescriptionPackage Type设为Design time onlyContains添加TICPlotReg.pas,TICGaugeReg.pas,TICLEDReg.pas等注册单元共 12 个Requires必须包含rtl,vcl,vclx,designide,iocmp2007注意iocmp2007是上一步编译出的运行时包路径要正确Compiler同上勾选Stack frames。关键陷阱TICPlotReg.pas中的Register过程调用RegisterComponents(Iocomp, [...])但 Delphi 2007 的designide.dcp位置可能偏移。若编译报F2063 Could not compile used unit DesignIntf请手动在dcliocmp2007.dpk的requires后追加{$IFDEF VER185} // Delphi 2007 VER185 designide, {$ENDIF}编译成功后dcliocmp2007.bpl生成。安装步骤Component → Install Packages → Add → 选中dcliocmp2007.bpl确认Iocomp页签出现在 Tool Palette新建空白窗体从Iocomp页拖一个TICPlot双击进入设计器 —— 成功4. 常见问题排查五个真实踩坑记录省下你三天调试时间编译通过不等于万事大吉。我在某模拟项目 X 的 HMI 系统重构中连续踩过以下坑每一条都来自真实日志和蓝屏截图。4.1 现象设计器中拖入TICPlot后IDE 立即崩溃Dr. Watson 报access violation at address 00000000原因dcliocmp2007.bpl编译时Requires错误包含了iocmp2007运行时包导致设计时加载了运行时 DLL 的 GDI 绘图上下文与 IDE 自身绘图冲突。解决打开dcliocmp2007.dpk在Requires标签页中彻底删除iocmp2007设计时注册单元如TICPlotReg.pas中所有对TICPlot类的引用改用GetClass(TICPlot) as TComponentClass动态获取避免静态链接。4.2 现象TICPlot曲线数据更新后界面闪烁严重CPU 占用 30%原因默认启用双缓冲DoubleBuffered : True但在 Delphi 2007 的 GDI 模式下双缓冲与TICPlot自身的InvalidateRect区域刷新逻辑冲突触发反复重绘。解决在TICPlot创建后执行Plot.DoubleBuffered : False;并在OnDraw事件中手动双缓冲procedure TForm1.ICPlot1Draw(Sender: TObject); var Bmp: TBitmap; begin Bmp : TBitmap.Create; try Bmp.Width : ICPlot1.Width; Bmp.Height : ICPlot1.Height; ICPlot1.PaintTo(Bmp.Canvas.Handle, 0, 0); // 绘制到内存位图 ICPlot1.Canvas.Draw(0, 0, Bmp); // 一次性贴回 finally Bmp.Free; end; end;4.3 现象ActiveX 控件在 VC6 中调用put_XAxisTitle(温度)后标题显示为乱码?原因ICPlotCtrl.cpp中put_XAxisTitle使用SysAllocString分配 BSTR但未指定 Unicode 编码VC6 默认 ANSI 字符集导致宽字符截断。解决修改ICPlotCtrl.cpp第 1203 行// 原始错误 m_bstrXAxisTitle SysAllocString(title); // 修改为正确 int len ::SysStringLen(title); OLECHAR* pwsz new OLECHAR[len 1]; MultiByteToWideChar(CP_ACP, 0, _bstr_t(title), -1, pwsz, len 1); m_bstrXAxisTitle SysAllocString(pwsz); delete[] pwsz;4.4 现象TICGauge指针动画卡顿每秒仅 5 帧远低于标称 30 FPS原因TICGauge.pas中AnimateNeedle方法使用Sleep(33)控制帧率但 Windows 下Sleep最小精度为 15ms且受系统调度影响实际间隔波动极大。解决弃用Sleep改用高精度定时器// 在 TICGauge 类中添加 private FTimer: TTimer; FFrameCount: Integer; procedure TICGauge.StartAnimation; begin FFrameCount : 0; FTimer : TTimer.Create(Self); FTimer.Interval : 33; // 目标 30 FPS FTimer.OnTimer : DoAnimationFrame; FTimer.Enabled : True; end; procedure TICGauge.DoAnimationFrame(Sender: TObject); begin Inc(FFrameCount); if FFrameCount 30 then FFrameCount : 0; // 执行指针角度插值计算 NeedleAngle : StartAngle (TargetAngle - StartAngle) * FFrameCount / 30; Invalidate; // 触发重绘 end;4.5 现象编译iocmp2007.bpl时提示F2051 Unit ICCore is not found但文件明明存在原因ICCore.pas文件头有unit ICCore;而iocmp2007.dpk的Contains列表中添加的是ICCore.pas但 Delphi 编译器要求单元名与文件名严格一致。若你重命名过文件如ICCore_v4.pas即使Contains里写对了也会报此错。解决检查\Source\VCL\Runtime\ICCore.pas文件第一行是否为unit ICCore;若为unit ICCore_v4;则必须将文件名改回ICCore.pas或同步修改unit声明为ICCore——单元名、文件名、Contains 列表三者必须完全一致一个字母都不能差。5. 深度定制实战给TICPlot加入“历史数据回放”功能支持鼠标拖拽时间轴这才是源码价值的终极体现不是修 Bug是加新能力。某导师曾让我给某图像处理 Demo 的曲线控件加“回放”按钮要求能拖动时间轴滑块实时重绘过去 60 秒数据。用黑盒控件只能妥协为“暂停/继续”而 Iocomp 源码让我们做到像素级控制。5.1 数据结构扩展在TICPlot中增加回放缓冲区打开TICPlot.pas在private区域添加type TPlotDataPoint record XValue: Double; // 时间戳秒 YValue: Double; // 数值 Color: TColor; // 可选不同通道不同色 end; TPlotDataBuffer array of TPlotDataPoint; private FHistoryBuffer: TPlotDataBuffer; // 历史数据环形缓冲区 FHistorySize: Integer; // 当前缓冲区长度≤ 6000按 100Hz 采样 FHistoryStart: Integer; // 环形起始索引 FPlaybackMode: Boolean; // 是否处于回放模式 FPlaybackTimeRange: Double; // 回放时间范围秒默认 60.0 FPlaybackCurrentTime: Double; // 当前回放时间戳秒在constructor Create(AOwner: TComponent);中初始化constructor TICPlot.Create(AOwner: TComponent); begin inherited Create(AOwner); // ... 原有初始化 SetLength(FHistoryBuffer, 6000); // 60 秒 × 100Hz FHistorySize : 0; FHistoryStart : 0; FPlaybackMode : False; FPlaybackTimeRange : 60.0; FPlaybackCurrentTime : 0.0; end;5.2 回放逻辑注入重写DrawSeries支持时间窗口裁剪TICPlot绘制曲线的核心是DrawSeries方法。我们不改动原有逻辑而是增加一个if FPlaybackMode then分支procedure TICPlot.DrawSeries(ACanvas: TCanvas; ASeriesIndex: Integer); var i, j, StartIdx, EndIdx: Integer; MinX, MaxX: Double; DataPoint: TPlotDataPoint; begin if not FPlaybackMode then begin // 原始实时绘制逻辑保持不变 inherited DrawSeries(ACanvas, ASeriesIndex); Exit; end; // 回放模式计算当前时间窗口 [FPlaybackCurrentTime - FPlaybackTimeRange, FPlaybackCurrentTime] MinX : FPlaybackCurrentTime - FPlaybackTimeRange; MaxX : FPlaybackCurrentTime; // 在环形缓冲区中查找起始/结束索引 StartIdx : -1; EndIdx : -1; for i : 0 to FHistorySize - 1 do begin j : (FHistoryStart i) mod Length(FHistoryBuffer); DataPoint : FHistoryBuffer[j]; if (DataPoint.XValue MinX) and (StartIdx -1) then StartIdx : j; if (DataPoint.XValue MaxX) and (DataPoint.XValue MinX) then EndIdx : j; end; if (StartIdx -1) or (EndIdx -1) then Exit; // 绘制裁剪后的数据段复用原有绘图 API with ACanvas do begin Pen.Color : Series[ASeriesIndex].Color; Pen.Width : Series[ASeriesIndex].LineWidth; MoveTo(MapX(FHistoryBuffer[StartIdx].XValue), MapY(FHistoryBuffer[StartIdx].YValue)); for i : StartIdx to EndIdx do begin j : i mod Length(FHistoryBuffer); LineTo(MapX(FHistoryBuffer[j].XValue), MapY(FHistoryBuffer[j].YValue)); end; end; end;5.3 暴露回放接口添加AddHistoryPoint和StartPlayback方法在public区域添加public procedure AddHistoryPoint(XValue, YValue: Double; AColor: TColor clRed); procedure StartPlayback(ATimeRange: Double 60.0); procedure StopPlayback; property PlaybackTimeRange: Double read FPlaybackTimeRange write FPlaybackTimeRange; property PlaybackCurrentTime: Double read FPlaybackCurrentTime write FPlaybackCurrentTime;实现AddHistoryPoint环形缓冲区写入procedure TICPlot.AddHistoryPoint(XValue, YValue: Double; AColor: TColor); var WriteIdx: Integer; begin WriteIdx : (FHistoryStart FHistorySize) mod Length(FHistoryBuffer); FHistoryBuffer[WriteIdx].XValue : XValue; FHistoryBuffer[WriteIdx].YValue : YValue; FHistoryBuffer[WriteIdx].Color : AColor; if FHistorySize Length(FHistoryBuffer) then Inc(FHistorySize) else FHistoryStart : (FHistoryStart 1) mod Length(FHistoryBuffer); end;StartPlayback启动定时器驱动回放procedure TICPlot.StartPlayback(ATimeRange: Double); begin FPlaybackTimeRange : ATimeRange; FPlaybackCurrentTime : FHistoryBuffer[(FHistoryStart FHistorySize - 1) mod Length(FHistoryBuffer)].XValue; FPlaybackMode : True; // 启动 33ms 定时器平滑推进时间 if not Assigned(FPlaybackTimer) then begin FPlaybackTimer : TTimer.Create(Self); FPlaybackTimer.Interval : 33; FPlaybackTimer.OnTimer : PlaybackTimerTick; end; FPlaybackTimer.Enabled : True; end; procedure TICPlot.PlaybackTimerTick(Sender: TObject); begin FPlaybackCurrentTime : FPlaybackCurrentTime 0.033; // 每帧推进 33ms Invalidate; // 触发重绘 end;5.4 在 Form 中调用三行代码启动回放// 假设 Form 上有 TICPlot1 和 TButton1回放按钮 procedure TForm1.Button1Click(Sender: TObject); begin // 1. 清空实时数据可选 TICPlot1.ClearSeries(0); // 2. 启动回放自动从缓冲区末尾开始 TICPlot1.StartPlayback(60.0); // 3. 添加鼠标拖拽支持时间轴滑块 TICPlot1.OnMouseMove : PlotMouseMove; end; procedure TForm1.PlotMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); var ScreenX: Double; begin if TICPlot1.PlaybackMode then begin ScreenX : TICPlot1.ScreenToPlotX(X); TICPlot1.PlaybackCurrentTime : ScreenX; // 拖拽 X 轴直接跳转时间点 TICPlot1.Invalidate; end; end;从那以后我每次接到“加个回放功能”的需求都不再翻文档查 API而是直接打开TICPlot.pas在DrawSeries里加个if分支15 分钟搞定。源码不是用来读的是用来改的不是用来敬畏的是用来驯服的。Iocomp v4.02 SP2 的价值不在它多老而在它足够透明——所有黑匣子都被拧开了螺丝所有玄学都落成了代码。希望帮到你。本文还有配套的精品资源点击获取