简介:本资源为Delphi 7环境下经典Unicode增强控件库TNT Controls 2.3正式版完整安装包,面向使用老旧Delphi平台开发多语言桌面应用的中高级开发者,尤其适用于需兼容中文、日文、阿拉伯文等复杂Unicode字符集的遗留系统维护与升级项目。压缩包共159个文件,含46个Pascal源码(.pas)、45个编译单元(.dcu)、9个Borland包工程(.dpk/.bpk)、12个资源文件(.res)及配套设计时组件注册文件(.dcr)、配置文件(.cfg)和帮助文档(.rtf/.txt),总大小835KB,结构完整覆盖源码、设计时支持、运行时库与示例工程。已有516人学习下载,资源内含TntUnicodeVcl系列多版本工程(支持D7/D9/BDS等),可直接导入Delphi 7 IDE完成组件安装,并立即在窗体中拖放使用TNTGrid、TNTTreeView等增强控件,获得原生不支持的Unicode文本渲染、双向文字排版及高定制化界面能力。
1. 项目背景与核心价值解析
如果你是一位在Windows XP时代就开始用Delphi 7做开发的“老炮”,那么对“TNT Unicode Controls”这个组件包一定不会陌生。最近我在整理一个老项目的源码时,又翻出了那个经典的tnt2.3.rar压缩包,里面包含了TNT Controls的源码、安装包和一堆示例。这个项目标题看起来像是一串混乱的关键词堆砌,但它精准地指向了一个在Delphi 7时代至关重要的技术痛点:原生VCL控件对Unicode支持的缺失,以及TNT控件作为当时最主流解决方案的完整生态。简单来说,TNT2.3就是那个能让你的Delphi 7程序轻松显示中文、日文、阿拉伯文,而不会变成一堆乱码的“救星包”。
为什么今天还要聊这个“古董”?原因很现实。大量的遗留系统、工业控制软件(正如“TNT CONTROLS”可能暗示的工控领域)、甚至一些特定行业的桌面应用,至今仍运行在Delphi 7构建的框架上。这些系统稳定,但面临现代化改造的难题,其中首要的就是国际化支持。直接升级到高版本的Delphi(如Delphi 2009及以后,它们原生支持Unicode)成本高昂,风险巨大。因此,为现有的Delphi 7程序打上TNT这个“Unicode补丁”,就成了最具性价比的平滑升级方案。此外,网络热词“delphi7 idftp put 无反应”也侧面反映了老技术栈在当下网络环境中遇到的新问题,而TNT控件中恰好包含了对Indy等网络组件的Unicode增强,这其中的关联我们后面会详细拆解。
所以,这篇文章不仅仅是怀旧。我将结合自己十多年维护和改造Delphi 7项目的经验,为你彻底拆解tnt2.3.rar这个资源包,从安装部署、核心控件解析、到解决实际开发中的乱码难题,特别是如何应对像FTP上传无反应这类新时代的兼容性问题。无论你是需要维护旧系统的开发者,还是对这段技术历史感兴趣的学习者,都能从这里获得可直接复现的实操指南和避坑经验。
2. TNT2.3组件包全貌与安装部署指南
2.1 组件包结构与核心文件解读
拿到tnt2.3.rar,解压后你会发现它远不止一个简单的BPL包。一个完整的TNT 2.3发行版通常包含以下核心部分,理解它们各自的作用是成功部署的关键:
源码目录 (
Source\): 这是最宝贵的部分。里面是所有TNT控件的Pascal源代码。主要子目录包括:TntControls: 核心UI控件源码,如TTntLabel,TTntEdit,TTntMemo,TTntStringGrid等。它们是StdCtrls和ExtCtrls中对应控件的Unicode版本。TntClasses: 基础类库,定义了WideString相关的列表、流等辅助类,是其他模块的基础。TntGraphics: 解决了TCanvas文本绘制函数的Unicode支持问题。TntForms: 提供了TTntForm基类,确保窗体标题、消息框等系统文本支持Unicode。TntSysUtils,TntWindows: 提供了替换RTL(运行时库)和Windows API中字符串函数的Unicode版本。Demos: 各种示例程序,是学习控件用法的绝佳资料。
设计期包 (
DesignTime): 包含用于在Delphi 7 IDE中安装的包文件(.dpk、.bpl等)。安装后,工具栏上会出现“Tnt Unicode”面板。运行时包 (
RunTime): 包含程序运行时必需的.bpl文件。如果你选择动态链接,部署程序时需要将这些BPL与主程序一同分发。第三方增强组件: 一些版本的TNT 2.3还包含了针对流行第三方组件的Unicode封装,例如
TntDB用于数据库控件,以及对我们解决“idftp put 无反应”问题至关重要的TntId目录,它提供了对Indy网络组件的Unicode支持。
注意:网络上流传的
tnt2.3.rar版本众多,完整性不一。最理想的版本应包含上述所有目录,特别是完整的Source和TntId。如果缺少,你可能需要寻找更完整的版本或自行补全。
2.2 在Delphi 7中的完整安装步骤与避坑要点
安装TNT控件不是简单地点“Install Package”就完事了。为了系统的稳定和后续开发的顺畅,我强烈推荐以下步骤:
步骤一:备份与准备
- 备份你的Delphi 7组件库配置。可以导出注册表项
HKEY_CURRENT_USER\Software\Borland\Delphi\7.0下的相关键值,或者直接复制整个Delphi 7安装目录下的Bin和Lib文件夹。 - 将解压后的TNT目录(例如
D:\Dev\TntUnicode\)放置在一个路径不含中文和空格的位置。这是避免编译诡异错误的第一步。
步骤二:编译并安装运行时包
- 打开Delphi 7,选择
File -> Open...,导航到TNT目录下的RunTime文件夹,打开TntRX.dpk(也可能是TntRunTime.dpk)。 - 在出现的包管理器窗口中,点击
Compile按钮。如果编译成功,你会看到TntRX.bpl(或类似名称)被生成。 - 关键避坑点:编译时最常见的错误是“File not found: ‘DesignEditors.dcu’”。这是因为TNT源码中某些设计期单元引用了仅在设计期包中存在的单元。解决方法是在
Project -> Options -> Directories/Conditionals中的Search Path里,添加你Delphi 7安装目录下的Lib路径(例如C:\Program Files\Borland\Delphi7\Lib)。确保路径正确,再次编译通常即可通过。
步骤三:编译并安装设计期包
- 关闭运行时包项目。现在打开
DesignTime文件夹下的TntD7.dpk(针对Delphi 7的设计期包)。 - 同样,点击
Compile编译,然后点击Install安装。 - 安装成功后,Delphi会提示“包已安装”,并且你会在组件面板上看到一个名为“Tnt Unicode”的新标签页,里面排列着所有带“Tnt”前缀的Unicode控件。
步骤四:配置库路径(至关重要)为了让Delphi在任何项目中都能找到TNT的源码进行编译(特别是当你使用“Build with Runtime Packages”选项时),必须将TNT的源码路径添加到全局库路径。
- 点击
Tools -> Environment Options。 - 选择
Library标签页。 - 在
Library Path编辑框中,添加TNT源码的根目录路径(例如D:\Dev\TntUnicode\Source)。你可以点击末尾的“...”按钮浏览添加。 - 实操心得:我习惯将
Source下的各个子目录(TntControls,TntClasses等)也一并添加进去,确保万无一失。添加后点击OK,并重启Delphi 7使配置生效。
至此,TNT Unicode Controls就已经成功集成到你的Delphi 7开发环境中了。你可以像使用标准Label、Edit一样,从“Tnt Unicode”面板拖拽TTntLabel、TTntEdit到窗体上,它们的Caption和Text属性将直接支持双字节字符。
3. 核心TNT控件深度解析与应用场景
3.1 基础UI控件的Unicode化迁移
安装完成后,最直观的变化就是多了一套和标准VCL控件一一对应的TNT版本。它们的用法几乎完全相同,但内核已替换为支持WideString。
TTntLabel/TTntEdit/TTntMemo/TTntButton: 这些是最常用的控件。将原有窗体上的TLabel替换为TTntLabel,其Caption属性就可以直接存储和显示如“中文标题”这样的Unicode字符串,而不会在非中文系统上显示为“???”。TTntEdit和TTntMemo的Text属性同理。TTntComboBox/TTntListBox: 它们的Items属性是TTntStrings类型,支持直接添加Unicode字符串项。这是实现多语言下拉列表的关键。TTntStringGrid: 网格控件是数据展示的重灾区。原生的TStringGrid的Cells属性存储AnsiString,显示中文经常出问题。TTntStringGrid完美解决了这个问题,并且与TTntStringList配合进行数据导入导出非常方便。
迁移策略与技巧: 对于已有项目,不建议手动逐个替换控件,工作量巨大且易错。推荐的方法是:
- 在DFM文件(窗体的二进制描述文件)级别进行替换。可以用文本编辑器(如Notepad++)打开
.dfm文件,将object Label1: TLabel替换为object Label1: TTntLabel。但要注意,TTntLabel的类定义必须在uses部分包含TntStdCtrls。 - 更安全的方法是编写一个小脚本,或者利用Delphi的“重命名引用”功能,但这需要更精细的操作。一个实用的土办法是:在窗体上放一个新的
TTntLabel,设置好属性,然后去DFM里复制这个对象的定义,替换掉旧Label的定义,并保留旧的Name和位置信息。 - 重要注意事项:替换控件后,一定要检查事件处理程序。例如,原
TEdit的OnChange事件处理器签名为procedure TForm1.Edit1Change(Sender: TObject);,它仍然可以挂接到TTntEdit上,因为事件类型兼容。但是,在代码中访问Text属性时,现在得到的是WideString,需要确保后续的字符串处理逻辑能兼容WideString(在Delphi 7中,WideString与AnsiString的赋值是自动转换的,但涉及指针操作或API调用时要格外小心)。
3.2 数据感知控件与数据库Unicode支持
对于数据库应用,乱码问题往往出现在“数据感知控件”和“SQL语句”两个层面。TNT提供了TntDB单元来解决前者。
TTntDBEdit/TTntDBMemo/TTntDBGrid: 这些是TDBEdit,TDBMemo,TDBGrid的Unicode版本。它们能够正确显示来自数据库的Unicode字符串字段内容。- 工作原理:
TTntDBGrid的核心在于重写了绘制单元格的方法。当数据库字段是WideString类型(例如,在ADO中对应ftWideString),TTntDBGrid会调用Unicode版本的文本绘制函数,确保正确渲染。
数据库连接与SQL语句处理: TNT控件主要解决的是显示层的问题。要确保数据从数据库到程序的整个链路都是Unicode,还需要:
- 数据库字段类型:确保表中存储文本的字段使用的是支持Unicode的类型,如SQL Server的
NVARCHAR、NTEXT,Oracle的NVARCHAR2,MySQL的UTF8MB4编码的VARCHAR。 - 连接组件设置:以ADO为例 (
TADOConnection,TADOQuery),需要将ConnectionString中的字符集或区域设置指定为支持Unicode的,例如加上Character Set=UTF8(取决于驱动)。对于TADOQuery,写入参数时,如果参数对应NVARCHAR字段,其数据类型应设置为ftWideString。 - SQL语句编写:在代码中拼接SQL时,直接使用Delphi的
String(在Delphi 7默认是AnsiString)可能会导致问题。一个良好的习惯是:所有SQL语句中的字符串常量,都使用N‘’前缀(对于SQL Server)或使用参数化查询。例如:ADOQuery1.SQL.Text := 'SELECT * FROM Users WHERE Name = N'’张三‘’;或者使用参数ADOQuery1.Parameters.ParamByName('Name').Value := '张三';(ADO会自动处理类型转换)。
3.3 系统对话框与文件操作的Unicode适配
除了可视化控件,程序与操作系统交互的许多环节也是乱码高发区。TNT通过TntSysUtils和TntWindows单元提供了大量替代函数。
- 消息框与对话框:原生的
ShowMessage、MessageDlg函数只支持AnsiString。TNT提供了WideShowMessage、WideMessageDlg等函数,接受WideString参数,可以正确显示中文提示。 - 文件与目录操作:
FindFirst、FindNext在遇到包含中文的文件名时会失败。应使用TntClasses单元中的WideFindFirst、WideFindNext以及TTntDirectory等相关类。 - INI文件读写:标准的
TIniFile不支持Unicode节名和键名。使用TTntIniFile可以完美解决。 - 注册表操作:同样,使用
TTntRegistry替代TRegistry。
实操心得:一个彻底的Unicode化改造,不仅仅是替换UI控件。我建议在项目初期就建立一个“单元替换清单”,在代码中全局搜索并替换这些常用的RTL函数为TNT的Wide版本。虽然工作量不小,但这是从根本上杜绝乱码的治本之策。例如,可以创建一个公共单元,定义诸如function MsgBox(const Msg: WideString): Integer;这样的包装函数,内部调用WideMessageDlg,然后在项目中统一使用自己的MsgBox。
4. 攻克网络组件Unicode难题:以“IDFTP Put无反应”为例
现在我们来深入探讨网络热词“delphi7 idftp put 无反应”背后的原因,以及如何利用TNT组件包解决它。这个问题非常典型,它不仅仅是TNT的问题,更是Delphi 7时代Ansi编码与当今UTF-8主导的网络世界之间的冲突。
4.1 问题根源深度剖析
TIdFTP是Indy组件包中的FTP客户端控件。在Delphi 7中,Indy的默认版本(通常是Indy 9)其内部字符串处理是基于AnsiString的。当你尝试使用Put方法上传一个包含非ASCII字符(如中文)的文件名时,问题就来了:
- 客户端发送:你的代码中,文件名是
WideString类型(比如从TTntEdit获得)。当你将其赋值给TIdFTP的某个属性或Put方法的参数时,Delphi会将其隐式转换为AnsiString。这个转换依赖于系统的默认代码页(在中文Windows上是GBK)。转换后,“中文.txt”可能变成了字节序列。 - FTP协议与服务器端:FTP协议在传输文件名时,理论上不关心编码,它只是传输字节流。然而,现代的FTP服务器(如FileZilla Server、vsftpd等)通常期望客户端使用UTF-8编码来传输文件名,以支持国际字符集。这是IETF在RFC 2640中推荐的。
- 编码不匹配:客户端用GBK编码发送了文件名字节流,而服务器端用UTF-8去解码,结果解码失败。服务器可能无法识别这个命令或文件名,从而导致连接挂起、无响应,或者返回一个模糊的错误。这就是“Put无反应”或上传失败的根源。
4.2 TntId组件的解决方案与配置
TNT 2.3包中的TntId目录正是为解决此类问题而生。它提供了一套TIdFTP的派生类TTntIdFTP,以及其他Indy组件的Unicode版本。
解决方案步骤:
引入TntId单元:首先,确保你的项目搜索路径包含了TNT源码下的
TntId目录。然后在需要使用的单元中,在uses部分添加TntIdFTP,并移除原有的IdFTP(避免冲突)。使用TTntIdFTP控件:在窗体上放置一个
TTntIdFTP控件(如果设计期包已正确安装,它应该在组件面板上),替代原来的TIdFTP。它的属性、方法和事件与TIdFTP几乎完全一致,但内部核心的字符串处理已升级为WideString。关键属性设置:
TTntIdFTP有一个至关重要的属性:UseUTF8。这个属性指示控件是否在FTP协议层面使用UTF-8编码发送文件名和目录名。- 对于大多数现代FTP服务器,你需要将
UseUTF8设置为True。 - 连接服务器后,
TTntIdFTP会自动发送OPTS UTF8 ON命令(如果服务器支持),协商使用UTF-8编码。
- 对于大多数现代FTP服务器,你需要将
代码迁移示例:
// 原来的代码 (可能出问题) uses IdFTP; ... var FTP: TIdFTP; begin FTP := TIdFTP.Create(nil); try FTP.Host := 'ftp.example.com'; FTP.Username := 'user'; FTP.Password := 'pass'; FTP.Connect; FTP.Put('C:\我的文件\中文文档.txt', '中文文档.txt'); // 这里可能无反应 FTP.Disconnect; finally FTP.Free; end; end; // 使用TTntIdFTP的代码 uses TntIdFTP; // 注意这里 ... var FTP: TTntIdFTP; // 类型变了 begin FTP := TTntIdFTP.Create(nil); try FTP.Host := 'ftp.example.com'; FTP.Username := 'user'; FTP.Password := 'pass'; FTP.UseUTF8 := True; // !!!关键设置!!! FTP.Connect; FTP.Put('C:\我的文件\中文文档.txt', '中文文档.txt'); // 现在应该可以正常工作了 FTP.Disconnect; finally FTP.Free; end; end;
4.3 扩展与兼容性处理
即使使用了TTntIdFTP和UseUTF8,在实际环境中仍可能遇到一些棘手的兼容性问题:
- 服务器不支持UTF8:一些老旧的FTP服务器可能不支持
OPTS UTF8命令。如果连接后上传依然失败,可以尝试将UseUTF8设为False。这时,TTntIdFTP会回退到使用系统本地编码(如GBK)。但这要求服务器端也使用相同的本地编码,否则依然会乱码。 - 列表解析问题:
List或DirectoryListing方法获取的文件列表,如果服务器返回的列表信息包含UTF-8编码的非ASCII字符,TTntIdFTP需要正确解析。TTntIdFTP在这方面做了增强,但并非所有服务器列表格式都完美支持。如果遇到列表乱码,可能需要自定义OnCreateFTPList事件来处理特殊的列表格式。 - 被动模式与防火墙:“无反应”有时也可能源于网络连接问题,如防火墙阻塞了FTP的数据连接通道。确保
Passive属性设置正确(对于大多数位于防火墙后的客户端,应设为True)。
排查“无反应”问题的通用思路:
- 开启Indy的调试信息。设置
FTP.Intercept为一个TIdLogDebug或TIdLogFile组件,将通信日志输出到文件或调试窗口。观察PUT命令发送后,服务器的响应是什么。如果服务器返回550错误(文件不可用),很可能是编码问题导致服务器找不到文件;如果连接直接超时,则可能是网络或防火墙问题。 - 先用一个纯英文文件名(如
test.txt)测试上传,如果成功,则基本确定是编码问题。 - 联系服务器管理员,确认服务器支持的FTP编码类型。
通过系统性地应用TNT控件,特别是TntId组件,你可以让基于Delphi 7的遗留网络应用重新焕发生机,顺畅地与现代化的服务器进行交互。
5. 高级技巧、疑难杂症与性能考量
5.1 字符串类型的混用与转换陷阱
在全面引入TNT的WideString世界后,你的代码库可能会处于AnsiString(原VCL、部分API)和WideString(TNT控件、Windows Unicode API)混用的状态。虽然Delphi 7编译器在它们之间进行赋值时会做自动转换,但以下几个陷阱需要警惕:
- 指针与缓冲区操作:绝对不要将
PAnsiChar指向一个WideString变量,反之亦然。例如,调用需要PChar的Windows API时,如果函数是Unicode版本(如MessageBoxW),应使用PWideChar(WideStringVar);如果是Ansi版本(如MessageBoxA),则使用PAnsiChar(AnsiStringVar)。TNT提供的TntSysUtils.WideStringToStr和StrToWideString函数可以用于可控的转换。 - 字符串连接性能:
WideString是COM BSTR类型,其内存分配策略与AnsiString不同。在循环中进行大量的WideString连接(使用+运算符)可能带来性能开销。对于高性能场景,可以考虑使用TntClasses.TWideStringBuilder(如果TNT版本提供)或手动管理WideString列表。 - 常量字符串:代码中的字符串常量,如
'你好',在Delphi 7中默认是AnsiString。如果你将其直接赋给一个WideString变量,会发生从系统默认代码页(如GBK)到Unicode的转换。为了清晰和避免歧义,可以使用WideString类型强转:WideString('你好'),或者使用#符号指定Unicode字符代码(不实用)。更好的做法是将所有UI相关的字符串资源外部化。
5.2 资源文件(RC)与多语言支持
真正的国际化应用离不开资源文件。TNT与标准VCL的资源文件机制兼容,但需要遵循Unicode规范。
创建Unicode RC文件:使用文本编辑器(如Notepad++,确保以UTF-8 with BOM格式保存)创建
.rc文件。字符串表定义如下:STRINGTABLE BEGIN 1001, "Hello, World!" 1002, "中文欢迎信息" END保存时编码必须是UTF-16 LE with BOM或UTF-8 with BOM,Delphi的资源编译器
BRCC32.exe才能正确识别其中的非ASCII字符。更推荐UTF-16 LE。编译与链接:在Delphi项目中,通过
{$R ‘myres.res’}指令包含编译好的资源文件。在TNT控件中使用:在代码中,使用
LoadString或LoadWideString(TNT提供)函数从资源加载字符串,然后赋给TNT控件的属性。uses TntSysUtils; ... var MyWideStr: WideString; begin LoadWideString(HInstance, 1002, MyWideStr); // 加载ID为1002的字符串 TntLabel1.Caption := MyWideStr; end;动态切换语言:你可以准备多个不同语言的
.res文件(如lang_zh-CN.res,lang_en-US.res)。在运行时,通过Windows APILoadLibraryEx加载特定的DLL资源模块,或者更复杂地切换主程序的资源实例,来实现语言的动态切换。TNT控件能自动显示当前资源中对应的WideString。
5.3 与第三方组件的兼容性问题
不是所有第三方Delphi组件都考虑到了与TNT的兼容。常见问题及应对策略:
- 属性编辑器不显示:某些第三方控件的属性编辑器(Object Inspector中)可能无法正确处理
WideString属性,导致显示为空白或乱码。这通常是组件设计期代码的问题,运行时一般正常。解决办法有限,可能需要在代码中直接设置属性值。 - 消息与事件:TNT控件会处理
WM_SETTEXT、WM_GETTEXT等消息的Unicode版本。大部分第三方控件如果只是简单继承自标准VCL控件,消息传递可能不会有问题。但如果第三方控件深度自绘或自定义了消息处理,可能需要测试其与TNT控件父子关系的兼容性。 - 数据绑定:如果第三方网格或列表控件支持数据感知,但它内部使用
AnsiString与数据集交互,那么绑定到TField类型为ftWideString的字段时可能会出问题。解决方案是寻找该控件的Unicode版本,或者在其OnGetText事件中手动进行WideString到AnsiString的转换(这会导致信息丢失,是下策)。
性能考量: 使用TNT控件会带来轻微的性能和内存开销,因为WideString每个字符占用2字节(UTF-16),而AnsiString只占1字节。在文本处理量极大的场景(如处理百万行的日志文件),需要留意内存消耗。然而,对于绝大多数桌面GUI应用,这点开销与获得的全球语言支持能力相比,是完全可以接受的。更关键的是,它避免了因编码错误导致的崩溃、数据损坏等严重问题,提升了程序的健壮性。
6. 从TNT到现代Delphi的迁移路径思考
虽然TNT 2.3让Delphi 7项目得以延续,但终究不是长久之计。Embarcadero的现代Delphi(如Delphi 11 Alexandria)已原生支持Unicode(字符串默认是UnicodeString,即UTF-16),并拥有更强大的IDE、更多的平台支持。因此,为旧项目规划迁移路径是必要的。
评估迁移必要性:如果项目稳定,仅需维护,且TNT方案运行良好,则不一定需要立即迁移。迁移是一项有风险的工作。
迁移的第一步:代码清理:
- 将项目中所有显式使用的
WideString替换为String(在Delphi 2009+中,String就是UnicodeString)。 - 将
TntStdCtrls等单元引用替换回StdCtrls、ExtCtrls等标准单元。 - 将
TTntLabel等控件类型替换回TLabel。这一步可以借助IDE的“重命名引用”功能批量进行,但必须仔细验证每个窗体。 - 将
WideShowMessage等TNT辅助函数调用,替换回标准的ShowMessage(现在已支持Unicode)。
- 将项目中所有显式使用的
处理特定API调用:在Delphi 7中,你可能调用了
MessageBoxA或使用了PAnsiChar。在Unicode Delphi中,应改为调用MessageBoxW或直接使用MessageBox(它现在是MessageBoxW的别名),并使用PChar(现在是PWideChar的别名)。第三方组件升级:最大的挑战往往是第三方组件。你需要为目标Delphi版本寻找对应支持的组件包。很多经典的组件如Indy、DevExpress、FastReport等都有新版本。这可能涉及许可和费用的更新。
渐进式迁移策略:对于大型项目,可以尝试“双模式”编译。即通过条件编译,让同一份代码库既能在Delphi 7+TNT下编译,也能在Unicode Delphi下编译。这需要精心设计
{$IFDEF UNICODE}等条件指令,初期工作量巨大,但为并行开发和测试提供了可能。
我个人经历过数次这样的迁移,我的体会是:迁移的核心不是语法转换,而是测试。尤其是涉及文件IO、网络通信、数据库交互和第三方组件行为的部分,必须进行全面的回归测试。TNT项目为你的代码提前引入了“Unicode意识”,这实际上让后续向现代Delphi的迁移变得相对平滑,因为你已经处理了最棘手的字符串编码思维转换。从这个角度看,在Delphi 7时代投资学习并使用TNT,是一笔非常划算的技术债偿还。
本文还有配套的精品资源,点击获取