functionGetcolor:longint;stdcall;beginresult:=color1;end;
Delphi中Format字符串说明
delphi中国际化的几种方案及比较(转)
随着全球化程度加深,软件越来越像蒲公英,到处飘散、扎根。这其中要解决的是不同语言的显示问题。我们当然希望一套程序,可以不修改代码就可以支持不同的语言,不要去维护很多的版本。
首先要谈到的一个问题是乱码问题,因为delphiwin32到11.x版还是不支持unicode,所以一般使用Ansi码,有这样几种情况会显示乱码:
言归正传,首先,看看哪些地方的字串需要实现多语言,并来看看各种实现方法的优劣。1、界面上的元件,如TButton的Caption;2、主动弹出的消息,如ShowMessage('Areyousure'),RaiseException.Create('Error!');3、例外错误举发的报告信息,如f/0引起的exception;4、第3方元件包内部的上述字串;
实现多语言的方法很多,列举一二:1、delphi自带的Resource生成工具此工具把专案的dfm文件里的所有字串以及pas中定义为ResourceString的字串列举出来,按不同的语言编译成不同的Resource,专案编译前先选语言,每种语言编译成一个exe。
这个工具使用很不方便,不是一个完整的解决方案,跟Borland的Midas的demo一样(TClientDataSet通过ProviderName连接到RemoteDatamodule的TDataSetProvider,实际开发Erp系统时,谁会放100个TDataSetProvider连接到100个TDataSet?),只是一个原理尚通的示范。
首先,由于dfm本身也是资源文件的一部分,因此每次修改都要“UpdateResourceDLLs...”,如修改Button1为Button2,如果你忘了,运行时就会报“找不到资源Button1的错误”;提供的字典编辑画面中,出了字串,还有Left/Top等资料;字典不能重用,在一个模组翻译了,在第2个模组还要再翻译相同的词。
其次,每种语言一个exe/bpl,如果你的系统是Package切割,bpl也是每种语言一个,还要小心别把不同语言的bpl组合在了一起,到时候一个画面显示中文,一个显示德文(有一个可能是乱码)就惨了。
再次,在作bpl组装的系统时,第3方元件如果没有提供多语言的方案,你就需要修改第3方元件,但一般我们不这样干,因为第3方元件会随时更新,难道每次人家更新你也再更新人家。
因此,一般都没有人使用Delphi本身提供的这个方案(除了作demo)。
2、Resourcedll方式
用单独的ResourceDll,用LoadResString等函数获得翻译字串,但你要到处写这个函数来一一替换,特别是Form上的字串,噢,会累死人。字典可以重用。3、网上讨论很多的ini文件方式此方法是写个替换的引擎,在运行时从ini文件读取语言字串来替换画面元件的显示文字。这个方法比第一种进步很多,不需要每种语言编译一个exe了,只要提供不同的ini文件就好;画面修改时如果ini没有同步更新也不会出现致命错误,最多就是某个文字没有转换;引擎也提供了字串转换函数,因此也可以处理主动弹出的消息。这个方法在文件格式上有三种不同的实现:(1)、[编号]=[字串]每个字串从1开始编号,1,2,3,4......,很麻烦,代码要修改,当然运行时切换语言没问题。(2)、[元件.属性]=[字串]这种实现把元件instance一一对应,用RTTI来判断属性,替换很精确,也可以运行时切换语言。不足之处是,略显呆板,多个元件相同的字串会多次列出;没有扩展性,表现TListView的Columns等复杂元件时比较吃力。(3)、[旧字串]=[新字串]不管元件的instance,ini是纯粹的语言对照表,或者叫字典,扩展性、运行时切换语言可能在引擎里。不足之处是不能处理一词多义。
总的说来,这种方式有很大进步,但为了用ini文件,大家还要费力的破解64k的限制,更专业的方式是使用自定义的文件格式。在简单性方面,无疑是这种自定义的转换引擎,[旧字串]=[新字串]的文件格式来得方便,借助字典管理工具,字典文件可以重复使用,也可以提供给专业翻译公司翻译。那么剩下的问题在引擎上,如何方便,最好用户不写一行代码;如何扩展性强,支持任意的第三方元件;如何有弹性,同一个画面有多种语言的文字,同一个词可以转换成不同的意思......
4、给每个元件类继承一个子类,在子类的Loaded方法里转换文字。由于要处理的都是叶级元件(虽然TLabel、TPanel都是从TCustomControl来,但不能只处理TCustomControl),工作量比较大;对旧有程序除了换元件无能为力。
5、为每个元件类注册一个转换函数,引擎遍历Container,为每个元件找到血源最近的转换函数,调用这个函数转换这个元件的文字。这样可以不必处理叶结点,只需在恰当的元件层上注册函数;不必改动旧有程序。設計時Form上只需要放一個轉換元件,這個元件在Loaded后開始掃描Form上的元件,從forI:=0toComponentCount-1或從fori:=0toControlCount-1遞歸,找到一個元件就去查找其血緣最近的註冊函數,然後調用這個函數替換其文字。因爲註冊函數是額外加上去的,所以不會動到舊的代碼,對任意第方元件都可以擴展支持,且也不用去修改人家第3方元件的代碼。
最后,我们畅想一下,如果我是Borland,如何在Delphi里完整支援多语言。Delphi提供了一个区块定义的关键字“ResourceString”,在这个区块定义的字串常量,编译器会把它编译在exe文件的资源区,运行时用LoadStringA这个WindowsAPI来读取,因此有些外部转换工具可以直接从exe文件读取这些资源字串,再写入转换后的字串;内嵌的转换引擎也可以拦截这个API函数来转换文字。但是如果exe里的字串资源化不彻底,就无能为力,这个不彻底恰恰来自Delphi的DFM文件,Delphi把DFM文件整个作为一项资源放在exe里,其上的字串就没法决定是否要don`tresource(Delphi源码里很多常量字串都有这个提示)了。
如果除了string,widestring,ansistring等等这些数据类型,delphi增加一种数据类型multistring,然后修改vcl元件定义(拜托Borland连同Unicode一起解决了吧),像TLabel.Caption定义成MultiString,对MultiString类型,有一种专门的处理方法,如类似ResourceString用LoadStringAPI来处理,每次读取就转换一次,但应该比这个内容更多,比如要传出instance,然后提供一个全局的ApplicationMulti元件,类似ApplicationEvent,让外面能捕捉到。至于字典,只能外部用户提供(当然可以制定一个标准格式让delphi人都可以共享交换)。
此法看起来可行,但还有个效率问题要考虑,(1)每次读取都转换,对频繁draw的东西效率低;(2)比如一个ToolBar有好多的ToolButton,批次更新时一般都会用BeginUpdate/EndUpdate,vcl如何告知后代来提高这种效率。(补记:效率看起来不是问题,对多次字串更改导致频繁draw,其实元件自己已经会用beginupdate/endupdate处理,外部不会涉及)
ResourceString与国际化(转)
如果您写的软件需要考虑到转换成为不同语言,那么由BorlandC++BuilderEnterpriseEdition所提供给您的多国语言翻译环境将是极为有帮助的。他可以很快的帮助您将各国语言版本制作出来。并且也提供动态的方式让您可以轻易的制作出您所需要的版本。甚至可以让您在不用变动程序代码本身的状态下,将GUI的外观字型等进行适度的调整。除了多国语言接口的制作外,透过本文后面所叙述的技巧,将可以让您动态的切换不同的语系。这些BCB都已经帮您处理得很好了,不过有些小细节您仍然需要注意,小心的处理或是避开,否则很容易的做不出来您想要的结果。
首先,我们先来看一下在设计程序时要注意的东西。
第一个版本务必使用英文。这点非常重要,其理由有二。第一点是通常比较好的文字翻译最好是由该国自己翻译。举例来说,要制作日文版本,当然是请地道的日本人来翻译会好的多。那么你认为懂得英文的日本人多,还是懂得中文的日本人多?同理,换成韩文,法文与西班牙文也是一样。第二点是由于在处理上,BCB所提供的环境通常比较能够正确的支持英文及该平台的语系。也就是说,您可以很容易找到可同时显示及处理英文及日文的平台,或者是英文及法文的平台。因为,没有一个语系的平台不能处理英文。可是你比较难找到一个可同时处理中文及日文的平台,或是同时处理中文及法文的平台。例外,还有一个理由未列在上面的是,在大部分的状况下,英文的句子长度比较长。
写程序时,无论任何理由都不要让自己的程序绑死在某个语系上面。例如:你可能会想说要好看一点所以写成"92年2月2日"。可是,这样的东西在其它语言马上变成乱码。因此,基本的方式是使用国际通用的格式。当然最好的方式还是要能自动转换成为各国的常用表示方法。但是这个部分属于i10n的范畴,我还没研究过,有空再说啰。
现在,就让我们来看看这个Delphi语法的ResourceString要怎么用在C++Builder中。其实,在多国语言上,Borland已经准备好了一个范例,放在$(BCB)\ExamplesAppsRichEdit。这个范例很重要的,他里面有些程序是我们后面介绍动态变换语系时要借来用用的。不过,这边我们先借他的ReConst.pas这个档案来用一下。您会看到这个档案有下面的内容:
unitReConst;interfaceusesWindows;constENGLISH=(SUBLANG_ENGLISH_USshl10)orLANG_ENGLISH;GERMAN=(SUBLANG_GERMANshl10)orLANG_GERMAN;FRENCH=(SUBLANG_FRENCHshl10)orLANG_FRENCH;resourcestringSUntitled='Untitled';SPercent_s='%s-%s';SSaveChanges='SaveChanges';SConfirmation='Confirmation';SNumberbetween='Thenumbermustbebetween1and1638.';SLcid='1033';{US=1033;DE=1031;FR=1036}implementationend.看到那个resourcestring了没?没错,就是这样!只要把那些resourcestring中的字符串换成你自己的定义就可以啰。稍微要注意的是这个字符串是使用单引号来括住的,因为他是Pascal语法嘛!例如,您可定义自己的字符串为:
ShowMessage(MyConst_MyResString1);是的!在您要用的Resourcestring名称前面加个"MyConst_"的prefix。就可以了。当然,这个prefix完全是根据您Unit的定义而来。您可参考前面步骤所做出的include档案中,的namespace名称,就可看到啰。在了解了这个部分后,接下来就是要把你的程序写好啰。请注意,多国语言化的翻译务必在最后进行。而且是要在程序完成度已经相当高的时候才进行。否则,画面一下子变化太大的话,C++Builder的多国语言环境也吃不消的。现在,假设您的程序已经完成了,接下来要如何才能够进行多国语言化呢?首先,您需要开启ResourceDLLWizard来制作ResourceDLL。你可在主选单中依照New->Others->New->ResourceDLLWizard来开启它。接下来这个精灵程序会给您一些指示,并请您选择要制作的语系。
首先,您在精灵中遇到的第一个问题是精灵会询问你要对哪个Project制作多国语言版本。把你要制作的Project打勾。然后,继续后面的步骤。这边有一点要注意的是BCB多国语言环境似乎有点问题,如果您的Project放在一些中文目录中,可能会找不到,所以建议您的Project要放在纯英文的路径内。接着,就是要选择您要转换的语系了。C++Builder的精灵会列出所有的语系让您选择。您可以一次把要做的语系全部选出来。也可以一次只选一些,下次要加入其它语系时,请重复这个步骤就可以了。别担心旧的语系会被局戚AC++Builder已经帮您处理好了。选好你的语系后按下下一步,您就会看到这些语系所对的目录。例如:中文就是CHT,简体是CHS,日文是JPN等等。接着,下一个画面,BCB会问您有没有其它的要加入的处理的档案。如果要加入的应该都是dfm檔或是rc档案。在接着下一步就是要产生语系了。C++Builder最后还会再列出一次要进行的语系给你看。
没问题的话,就按Finish啰。在最后列出所有语系的地方,有一栏为UpdateMode。如果您是第一次对这个语系建立ResourceDLL,那么这个字段就是CreateNew。如果您之前已经建立过这个语系了,而且您又再次使用ResourceDLL来建立他。没问题,精灵会帮您处理好,所以您会看到UpdateMode中所显示的是Update。如果,您不想要只是Update而已,而是要重新建立新的,在Update上面点一下,一个combobox会出现让您选择。最后都没问题的话,您就会看到精灵跟您说他需要重新编译您的程序以方便建立各国语言。在此同时,精灵会要求您将Project存成ProjectGroup。这是因为每个语系都是自己一个Project。用成ProjectGroup才能统一管理。对于这个ProjectGroup的名称我的建议是在Project名称后面加上"_i18n"的字尾。
当然,或许您会习惯在前面加上ML的前置词,代表MultipleLanguage。不过,我还是觉得用i18n好。存盘成功且Project也重新Compile成功。这时,精灵会顺便帮您叫出翻译管理员(TranslationManager)。在这个TranslationManager中,您需要使用其中的Workspace画面来帮助您进行翻译的工作。TranslationManager的用法不难,大家看看就会了。重点是除了字符串外,您还可透过翻译管理员调整组件大小,避免不同语系下,字符串被组件破坏的窘境。您随时都可透过主选单的View->TranslationManager来叫出翻译管理员。只是每次翻译完,您都需要针对有修改过的语系重新编译。每个单独语系都会编译出自己的ResourceDLL。这些档案的名字就是Project的名字,加上以语系的名字作扩展名。所以,如果您有一个名为Project1.exe的档案。那么他对应的中文繁体语系档就是Project1.CHT。日文就是Project1.JPN。这些语系档案要与执行档放置于同一个目录下。至于我们前面所说的resourcestring,他会出现在各语系的Resourcescript中。您也是透过翻译管理员来编辑他们。
不过,在这里C++Builder一直到6.0update2都有这么个Bug。这个Bug会导致您的resourcestring在切换语系时,不会被载入。好的,要如何解决他呢?简单的很,请开启你的ProjectMananger,将每个语系的Project加入一个名为XXX_DRC.rc的档案。其中,XXX就是你的Project名称。这个档案在每个语系自己的目录下都有一个,您需要为每个语系的Project加入他。当然,别弄错目录,把不同的语系档案混在一起啰。如果您要设定这个Project在目前平台要使用何种语系,可利用Project->Languages->SetActive设定预设语系。实际上,您的程序是根据下面这个Registry的值来决定要使用何种语系ResourceDLL:
HKEY_CURRENT_USER\Software\Borland\Locales\[Execfilename]其中"Execfilename"就是你的执行文件名(含路径),这个Registry的值是您希望的语系的名称。例如,如果您需要这个软件启动时是中文版,那么这个Registry就应该填CHT。其它以此类推。由于,要显示哪个语系是透过上述的Registry所决定的,因此,您需要在安装程序中就正确的设定这个Registry的值。以便能够正确的显示正确的语系。或阴z要问,难道没有办法自动判断系统语系,然后进行切换吗?有的!这就是我们下面所要介绍的,不过我们要介绍的是远比自动判断语系更厉害的,也就是动态切换语系。让您的使用者,可以动态的切换所要显示的语系。嗯!厉害吧!好了,首先第一部要做的是把前面所提到C++Builder范例中的那个RichEdit目录找出来。接着,仔细找找,看下面是不是有个叫做reinit.pas的档案。有吧!没有的话,就去跟别人Copy吧。把这个档案放到你的Project里面来。然后在您要让使用者动态切换的地方加上由这个档案所产生的include档案。例如:
#include"reinit.hpp"OK!第一次,你的系统里面当然没有这个档案啰!请利用CompileUnit的方式产生他。接着,我们就是要利用这个档案所提供的函式来帮助我们达到动态切换语系的效果。首先,假设您希望的是能够自动判断系统语系然后进行切换,那么找个合适的地方(通常是mainfunction的开头。)加入下面的程序代码:
if(LoadNewResourceModule(GetSystemDefaultLCID())){ReinitializeForms();}这样,便可正确的显示语系啰。上面这几行,其实就是C++Builder自己偷偷在用的。所以,如果你有提供ResourceDLL,而且没有依照前面所说的在Registry中指定语系。那么你的程序就会自己依照抓取系统语系了。正因如此,我们只需要把GetSystemDefaultLCID()换掉,换成特定的语系就可以啰。所以,现在要做的就是如何制作出给特定语系LCID呢?首先,在C++Builder的include目录中,找到winnt.h这个档案。找到LANG_及SUBLANG_开头的定义宏。我们以法文为例。要做出法文的LCID,其作法如下:
LCIDlcid=(SUBLANG_FRENCH<<10)|LANG_FRENCH;这样就可以了,很明显LANG_是在定义国家的语言。那么SUBLANG_呢?指的是地方语系。比如说,同样是中文,在LANG_CHINESE之下,就有这些SUBLANG:
#defineSUBLANG_CHINESE_TRADITIONAL0x01//Chinese(Taiwan)#defineSUBLANG_CHINESE_SIMPLIFIED0x02//Chinese(PRChina)#defineSUBLANG_CHINESE_HONGKONG0x03//Chinese(HongKongS.A.R.,P.R.C.)#defineSUBLANG_CHINESE_SINGAPORE0x04//Chinese(Singapore)#defineSUBLANG_CHINESE_MACAU0x05//Chinese(MacauS.A.R.)全部使用上面的计算方式,就可组合出您要的LCID。然后再把他喂给LoadNewResourceModule(),就可加载正确的ResourceDLL了。前提是那个ResourceDLL档案要存在喔。到此为止,您应该已经能够掌握C++Builder中多国语言的作法啰。最后再提醒您一点,一旦您对于Form上面或是resourcestring有所新增,删除,或是其它外观上的变动。务必要用Project->Languages->UpdateResourceDLLs来更新您的ResourceDLL。并且重新编译出各语系的版本。千万不要让自己重复一直做这个工作,否则您会很累。
---------------------------------------------------------------------------------如何运行时修改假设在Project1.exe里有一個ID为100的ResouceString,Value为"abc",想将abc改为123,可以试如下操作procedureTForm1.Button2Click(Sender:TObject);varh:THandle;b:LongBool;s:String;s2:PWideChar;iMemAlloc:Integer;begins:=Edit2.text;iMemAlloc:=Length(s)*SizeOf(WideChar);s2:=AllocMem(iMemAlloc);StringToWideChar(s,S2,iMemAlloc);h:=BeginUpdateResource('Project1.exe',false);b:=UpdateResource(h,RT_STRING,MakeIntResource(100),LANG_NEUTRAL,s2,iMemAlloc);ifbthenShowMessage('a');EndUpdateResource(h,False);end;--------------------------------------------------------------------------
ThebetteralternativetoresourcestringInsteadofusingresourcestrings,thereisabetteralternative:ShowMessage(_('Actionaborted'));
The_()isapascalfunctioninsidegnugettext.pas,whichtranslatesthetext.Itreturnsawidestring,unlikeresourcestring,whichislimitedtoansistring.Youcanuse_()togetherwithansistringsasyoulike,becauseDelphiautomaticallyconvertswidestringstostringswhenneeded.Anotherbenefitofthisisthatyoucanwritecomments,thatthetranslatorcanusetomakebettertranslations:
//MessageshowntotheuseraftertheuserclicksAbortbuttonShowMessage(_('Actionaborted'));
Youcanalsowritethecommentinthesameline:
ShowMessage(_('Actionaborted'));//MessagetouserwhenclickingAbortbutton
Butonlythe//stylecommentissupported-youcannotuse{}or(**)commentsforthispurpose.
Goodcommentsnormallyleadtogoodtranslations.Ifthetranslatorhasacopyofthesourcecode,poeditandkbabelcanbothshowthelocationinthesourcecodetothetranslator.Thismakessensewith_(),becausethetranslatormightgetagoodidea,whatthisisabout,evenifthetranslatorisn'taprogrammer.
Inotherwords,therearemanyreasonstouse_()insteadofresourcestrings.Ifyoucreateanewapplication,don'teventhinkaboutresourcestrings-justgodirectlyforthe_()solution.
软件加密技术和注册机制加密基础
一个密码系统的安全性只在于密钥的保密性,而不在算法的保密性。
对纯数据的加密的确是这样。对于你不愿意让他看到这些数据(数据的明文)的人,用可靠的加密算法,只要破解者不知道被加密数据的密码,他就不可解读这些数据。
但是,软件的加密不同于数据的加密,它只能是“隐藏”。不管你愿意不愿意让他(合法用户,或Cracker)看见这些数据(软件的明文),软件最终总要在机器上运行,对机器,它就必须是明文。既然机器可以“看见”这些明文,那么Cracker,通过一些技术,也可以看到这些明文。
于是,从理论上,任何软件加密技术都可以破解。只是破解的难度不同而已。有的要让最高明的Cracker忙上几个月,有的可能不费吹灰之力,就被破解了。
所以,反盗版的任务(技术上的反盗版,而非行政上的反盗版)就是增加Cracker的破解难度。让他们花费在破解软件上的成本,比他破解这个软件的获利还要高。这样Cracker的破解变得毫无意义——谁会花比正版软件更多的钱去买盗版软件?
2、密码学简介
2.1概念
(1)发送者和接收者
假设发送者想发送消息给接收者,且想安全地发送信息:她想确信偷听者不能阅读发送的消息。
(2)消息和加密
消息被称为明文。用某种方法伪装消息以隐藏它的内容的过程称为加密,加了密的消息称为密文,而把密文转变为明文的过程称为解密。
明文用M(消息)或P(明文)表示,它可能是比特流(文本文件、位图、数字化的语音流或数字化的视频图像)。至于涉及到计算机,P是简单的二进制数据。明文可被传送或存储,无论在哪种情况,M指待加密的消息。
密文用C表示,它也是二进制数据,有时和M一样大,有时稍大(通过压缩和加密的结合,C有可能比P小些。然而,单单加密通常达不到这一点)。加密函数E作用于M得到密文C,用数学表示为:
E(M)=C.
相反地,解密函数D作用于C产生M
D(C)=M.
先加密后再解密消息,原始的明文将恢复出来,下面的等式必须成立:
D(E(M))=M
(3)鉴别、完整性和抗抵赖
除了提供机密性外,密码学通常有其它的作用:.
(a)鉴别
(b)完整性检验
消息的接收者应该能够验证在传送过程中消息没有被修改;入侵者不可能用假消息代替合法消息。
(c)抗抵赖
发送者事后不可能虚假地否认他发送的消息。
(4)算法和密钥
如果算法的保密性是基于保持算法的秘密,这种算法称为受限制的算法。受限制的算法具有历史意义,但按现在的标准,它们的保密性已远远不够。大的或经常变换的用户组织不能使用它们,因为每有一个用户离开这个组织,其它的用户就必须改换另外不同的算法。如果有人无意暴露了这个秘密,所有人都必须改变他们的算法。
更糟的是,受限制的密码算法不可能进行质量控制或标准化。每个用户组织必须有他们自己的唯一算法。这样的组织不可能采用流行的硬件或软件产品。但窃听者却可以买到这些流行产品并学习算法,于是用户不得不自己编写算法并予以实现,如果这个组织中没有好的密码学家,那么他们就无法知道他们是否拥有安全的算法。
尽管有这些主要缺陷,受限制的算法对低密级的应用来说还是很流行的,用户或者没有认识到或者不在乎他们系统中内在的问题。
现代密码学用密钥解决了这个问题,密钥用K表示。K可以是很多数值里的任意值。密钥K的可能值的范围叫做密钥空间。加密和解密运算都使用这个密钥(即运算都依赖于密钥,并用K作为下标表示),这样,加/解密函数现在变成:
EK(M)=C
DK(C)=M.
DK(EK(M))=M.
有些算法使用不同的加密密钥和解密密钥,也就是说加密密钥K1与相应的解密密钥K2不同,在这种情况下:
EK1(M)=C
DK2(C)=M
DK2(EK1(M))=M
所有这些算法的安全性都基于密钥的安全性;而不是基于算法的细节的安全性。这就意味着算法可以公开,也可以被分析,可以大量生产使用算法的产品,即使偷听者知道你的算法也没有关系;如果他不知道你使用的具体密钥,他就不可能阅读你的消息。
密码系统由算法、以及所有可能的明文、密文和密钥组成的。
基于密钥的算法通常有两类:对称算法和公开密钥算法。下面将分别介绍:
2.2对称密码算法
对称算法有时又叫传统密码算法,就是加密密钥能够从解密密钥中推算出来,反过来也成立。在大多数对称算法中,加/解密密钥是相同的。这些算法也叫秘密密钥算法或单密钥算法,它要求发送者和接收者在安全通信之前,商定一个密钥。对称算法的安全性依赖于密钥,泄漏密钥就意味着任何人都能对消息进行加/解密。只要通信需要保密,密钥就必须保密。
对称算法的加密和解密表示为:
DK(C)=M
对称算法可分为两类。一次只对明文中的单个比特(有时对字节)运算的算法称为序列算法或序列密码。另一类算法是对明文的一组比特亚行运算,这些比特组称为分组,相应的算法称为分组算法或分组密码。现代计算机密码算法的典型分组长度为64比特——这个长度大到足以防止分析破译,但又小到足以方便使用(在计算机出现前,算法普遍地每次只对明文的一个字符运算,可认为是序列密码对字符序列的运算)。
2.3公开密码算法
用公开密钥K加密表示为
EK(M)=C.
虽然公开密钥和私人密钥是不同的,但用相应的私人密钥解密可表示为:
有时消息用私人密钥加密而用公开密钥解密,这用于数字签名(后面将详细介绍),尽管可能产生混淆,但这些运算可分别表示为:
当前的公开密码算法的速度,比起对称密码算法,要慢的多,这使得公开密码算法在大数据量的加密中应用有限。
2.4单向散列函数
单向散列函数H(M)作用于一个任意长度的消息M,它返回一个固定长度的散列值h,其中h的长度为m.
输入为任意长度且输出为固定长度的函数有很多种,但单向散列函数还有使其单向的其它特性:
(1)给定M,很容易计算h;
(2)给定h,根据H(M)=h计算M很难;
(3)给定M,要找到另一个消息M‘并满足H(M)=H(M’)很难。
在许多应用中,仅有单向性是不够的,还需要称之为“抗碰撞”的条件:
要找出两个随机的消息M和M‘,使H(M)=H(M’)满足很难。
由于散列函数的这些特性,由于公开密码算法的计算速度往往很慢,所以,在一些密码协议中,它可以作为一个消息M的摘要,代替原始消息M,让发送者为H(M)签名而不是对M签名.
如SHA散列算法用于数字签名协议DSA中。
2.5数字签名
提到数字签名就离不开公开密码系统和散列技术。
有几种公钥算法能用作数字签名。在一些算法中,例如RSA,公钥或者私钥都可用作加密。用你的私钥加密文件,你就拥有安全的数字签名。在其它情况下,如DSA,算法便区分开来了??数字签名算法不能用于加密。这种思想首先由Diffie和Hellman提出.
基本协议是简单的:
(1)A用她的私钥对文件加密,从而对文件签名。
(2)A将签名的文件传给B.
(3)B用A的公钥解密文件,从而验证签名。
这个协议中,只需要证明A的公钥的确是她的。如果B不能完成第(3)步,那么他知道签名是无效的。
这个协议也满足以下特征:
(1)签名是可信的。当B用A的公钥验证信息时,他知道是由A签名的。
(2)签名是不可伪造的。只有A知道她的私钥。
(3)签名是不可重用的。签名是文件的函数,并且不可能转换成另外的文件。
(4)被签名的文件是不可改变的。如果文件有任何改变,文件就不可能用A的公钥验证。
(5)签名是不可抵赖的。B不用A的帮助就能验证A的签名。
在实际应用中,因为公共密码算法的速度太慢,签名者往往是对消息的散列签名而不是对消息本身签名。这样做并不会降低签名的可信性。
3当前流行的一些软件保护技术
3.1序列号保护
数学算法一项都是密码加密的核心,但在一般的软件加密中,它似乎并不太为人们关心,因为大多数时候软件加密本身实现的都是一种编程的技巧。但近几年来随着序列号加密程序的普及,数学算法在软件加密中的比重似乎是越来越大了。
软件验证序列号的合法性过程,其实就是验证用户名和序列号之间的换算关系是否正确的过程。其验证最基本的有两种,一种是按用户输入的姓名来生成注册码,再同用户输入的注册码比较,公式表示如下:
序列号=F(用户名)
但这种方法等于在用户软件中再现了软件公司生成注册码的过程,实际上是非常不安全的,不论其换算过程多么复杂,解密者只需把你的换算过程从程序中提取出来就可以编制一个通用的注册程序。
另外一种是通过注册码来验证用户名的正确性,公式表示如下:
用户名称=F逆(序列号)(如ACDSEE)
这其实是软件公司注册码计算过程的反算法,如果正向算法与反向算法不是对称算法的话,对于解密者来说,的确有些困难,但这种算法相当不好设计。
于是有人考虑到以下的算法:
F1(用户名称)=F2(序列号)
F1、F2是两种完全不同的的算法,但用户名通过F1算法计算出的特征字等于序列号通过F2算法计算出的特征字,这种算法在设计上比较简单,保密性相对以上两种算法也要好的多。如果能够把F1、F2算法设计成不可逆算法的话,保密性相当的好;可一旦解密者找到其中之一的反算法的话,这种算法就不安全了。一元算法的设计看来再如何努力也很难有太大的突破,那么二元呢?
特定值=F(用户名,序列号)
这个算法看上去相当不错,用户名称与序列号之间的关系不再那么清晰了,但同时也失去了用户名于序列号的一一对应关系,软件开发者必须自己维护用户名称与序列号之间的唯一性,但这似乎不是难以办到的事,建个数据库就可以了。当然也可以把用户名称和序列号分为几个部分来构造多元的算法。
特定值=F(用户名1,用户名2,...序列号1,序列号2...)
现有的序列号加密算法大多是软件开发者自行设计的,大部分相当简单。而且有些算法作者虽然下了很大的功夫,效果却往往得不到它所希望的结果。
这种方法使用的较少。
3.3KeyFile保护
KeyFile(注册文件)是一种利用文件来注册软件的保护方式。KeyFile一般是一个小文件,可以是纯文本文件,也可以是包含不可显示字符的二进制文件,其内容是一些加密过或未加密的数据,其中可能有用户名、注册码等信息。文件格式则由软件作者自己定义。试用版软件没有注册文件,当用户向作者付费注册之后,会收到作者寄来的注册文件,其中可能包含用户的个人信息。用户只要将该文件放入指定的目录,就可以让软件成为正式版。该文件一般是放在软件的安装目录中或系统目录下。软件每次启动时,从该文件中读取数据,然后利用某种算法进行处理,根据处理的结果判断是否为正确的注册文件,如果正确则以注册版模式来运行。
这种保护方法使用也不多。
3.4CD-check
即光盘保护技术。程序在启动时判断光驱中的光盘上是否存在特定的文件,如果不存在则认为用户没有正版光盘,拒绝运行。在程序运行的过程当中一般不再检查光盘的存在与否。Windows下的具体实现一般是这样的:先用GetLogicalDriveStrings()或GetLogicalDrives()得到系统中安装的所有驱动器的列表,然后再用GetDriveType()检查每一个驱动器,如果是光驱则用CreateFileA()或FindFirstFileA()等函数检查特定的文件存在与否,并可能进一步地检查文件的属性、大小、内容等。
3.5软件狗
软件狗是一种智能型加密工具。它是一个安装在并口、串口等接口上的硬件电路,同时有一套使用于各种语言的接口软件和工具软件。当被狗保护的软件运行时,程序向插在计算机上的软件狗发出查询命令,软件狗迅速计算查询并给出响应,正确的响应保证软件继续运行。如果没有软件狗,程序将不能运行,复杂的软硬件技术结合在一起防止软件盗版。真正有商业价值得软件一般都用软件狗来保护。
平时常见的狗主要有“洋狗”(国外狗)和“土狗”(国产狗)。这里“洋狗”主要指美国的彩虹和以色列的HASP,“土狗”主要有金天地(现在与美国彩虹合资,叫“彩虹天地”)、深思、尖石。总的说来,“洋狗”在软件接口、加壳、反跟踪等“软”方面没有“土狗”好,但在硬件上破解难度非常大;而“土狗”在软的方面做的很好,但在硬件上不如“洋狗”,稍有单片机功力的人,都可以复制。
3.6软盘加密
通过在软盘上格式化一些非标准磁道,在这些磁道上写入一些数据,如软件的解密密钥等等。这种软盘成为“钥匙盘”。软件运行时用户将软盘插入,软件读取这些磁道中的数据,判断是否合法的“钥匙盘”。
软盘加密还有其它一些技术,如弱位加密等等。
随着近年来软盘的没落,这种方法基本上退出了历史舞台。
3.7将软件与机器硬件信息结合
此种加密算法的优点
·不同机器注册码不同。用户获得一个密码只能在一台机器上注册使用软件。不同于目前大多软件采用的注册方法,即只要知道注册码,可在任何机器上安装注册。
·不需要任何硬件或软盘
·采用特别技术,解密者很难找到产生注册号码的规律
·在使用注册号产生软件(注册机)时可采用使用密码、密钥盘、总次数限制等方法
·方便易用,价格低廉。
这种加密还有以下特点
1、注册加密的软件,只能在一台机器上安装使用。把软件拷贝到其它机器上不能运行。
2、若用户想在另一机器上安装运行,必须把软件在这一机器上运行时的序列号,寄给软件出版商换取注册密码。当然应再交一份软件费用。
3、此加密方法特别适应在因特网上发布的软件及用光盘发布的软件。
注释:
1、“加密技术概述”部分内容参考了大学教材“密码学基础”。
2、“当前流行的一些软件保护技术”部分内容参考了“加密与解密--软件保护技术及完全解决方案”一文。
Forexample,anycodethatmanipulatesordoespointeroperationsonstringsshouldbeexaminedforUnicodecompatibility.Morespecifically,anycodethat:
shouldbereviewedtoensurethatthoseassumptionsarenotpersistedincode.Codethatwritestoorreadsfrompersistentstorageneedstoensurethatthecorrectnumberofbytesarebeingreadorwritten,asasinglebytenolongerrepresentsasinglecharacter.
Generally,anyneededcodechangesshouldbestraightforwardandcanbedonewithaminimalamountofeffort.
Thissectiondiscussesareaofcodethatshouldcontinuetowork,andshouldnotrequireanychangestoworkproperlywiththenewUnicodeString.AlloftheVCLandRTLhavebeenupdatedtoworkasexpectedinDelphi2009,andwithvery,veryfewexceptions,suchwillbethecase.Forinstance,TStringListisnowcompletelyUnicode-aware,andallexistingTStringListcodeshouldworkasbefore.However,TStringListhasbeenenhancedtoworkspecificallywithUnicode,soifyouwanttotakeadvantageofthatnewfunctionality,youcan,butyouneednotifyoudon’twantto.
Ingeneral,codethatusesthestringtypeshouldworkasbefore.Thereisnoneedtore-declarestringvariablesasAnsiStringtypes,exceptasdiscussedbelow.StringdeclarationsshouldonlybechangedtobeAnsiStringwhendealingwithstoragebuffersorothertypesofcodethatusesthestringasadatabuffer.
Thatarticledoesn’tmentionanewunitaddedtotheRTL–AnsiString.pas.ThisunitexistsforbackwardscompatibilitywithcodethatchoosestouseorrequirestheuseofAnsiStringwithinit.
Runtimelibrarycoderunsasexpectedandingeneralrequiresnochange.Theareasthatdoneedchangearedescribedbelow.
TheentireVCLisUnicodeaware.AllexistingVCLcomponentsworkrightoutoftheboxjustastheyalwayshave.ThevastmajorityofyourcodeusingtheVCLshouldcontinuetoworkasnormal.We’vedonealotofworktoensurethattheVCLisbothUnicodereadyandbackwardscompatible.NormalVCLcodethatdoesn’tdoanyspecificstringmanipulationwillworkasbefore.
StringIndexingworksexactlyasbefore,andcodethatindexesintostringsdoesn’tneedtobechanged:
varS:string;C:Char;beginS:=‘Thisisastring’;C:=S[1];//Cwillhold‘T’,butofcourseCisaWideCharend;Length/Copy/Delete/SizeOfwithStringsCopywillstillworkasbeforewithoutchange.SowillDeleteandalltheSysUtils-basedstringmanipulationroutines.
CallstoLength(SomeString)will,asalways,returnthenumberofelementsinthepassedstring.
CallstoSizeOfonanystringidentifierwillreturn4,asallstringdeclarationsarereferencesandthesizeofapointeris4.
CallstoLengthonanystringwillreturnthenumberofelementsinthestring.
Considerthefollowingcode:
varS:string;beginS:='abcdefghijklmnopqrstuvwxyz';WriteLn('Length=',Length(S));WriteLn('SizeOf=',SizeOf(S));WriteLn('TotalBytes=',Length(S)*SizeOf(S[1]));ReadLn;end.Theoutputoftheaboveisasfollows:
PointerarithmeticonPCharshouldcontinuetoworkasbefore.ThecompilerknowsthesizeofPChar,socodelikethefollowingwillcontinuetoworkasexpected:
varp:PChar;MyString:string;begin...p:=@MyString[1];Inc(p);...end;ThiscodewillworkexactlythesameaswithpreviousversionsofDelphi–butofcoursethetypesaredifferent:PCharisnowaPWideCharandMyStringisnowaUnicodeString.
ShortStringremainsunchangedinbothfunctionalityanddeclaration,andwillworkjustasbefore.
ShortStringdeclarationsallocateabufferforaspecificnumberofAnsiChars.Considerthefollowingcode:
varS:string[26];beginS:='abcdefghijklmnopqrstuvwxyz';WriteLn('Length=',Length(S));WriteLn('SizeOf=',SizeOf(S));WriteLn('TotalBytes=',Length(S)*SizeOf(S[1]));ReadLn;end.Ithasthefollowingoutput:
Notethatthetotalbytesofthealphabetis26–showingthatthevariableisholdingAnsiChars.
Inaddition,considerthefollowingcode:
typeTMyRecord=recordString1:string[20];String2:string[15];end;Thisrecordwillbelaidoutinmemoryexactlyasbefore–itwillbearecordoftwoAnsiStringswithAnsiCharsinthem.Ifyou’vegotaFileofRecofarecordwithshortstrings,thentheabovecodewillworkasbefore,andanycodereadingandwritingsucharecordwillworkasbeforewithnochanges.
However,rememberthatCharisnowaWideChar,soifyouhavesomecodethatgrabsthoserecordsoutofafileandthencallssomethinglike:
varMyRec:TMyRecord;SomeChar:Char;begin//GrabMyRecfromafile...SomeChar:=MyRec.String1[3];...end;thenyouneedtorememberthatSomeCharwillconverttheAnsiCharinString1[3]toaWideChar.Ifyouwantthiscodetoworkasbefore,changethedeclarationofSomeChar:
SaveToFileandLoadFromFilecallscouldverywellgounderthe“JustWorks”sectionabove,asthesecallswillreadandwritejustastheydidbefore.However,youmaywanttoconsiderusingthenewoverloadedversionsofthesecallsifyouaregoingtobedealingwithUnicodedatawhenusingthem.
Forinstance,TStringsnowincludesthefollowingsetofoverloadedmethods:
However,ifyouputsomeUnicodestringdataintothetexttobewrittenout,youwillneedtousethesecondoverload,passingaspecificTEncodingtype.Ifyoudonot,thestringswillbewrittenoutasANSIdata,anddatalosswilllikelyresult.
Therefore,thebestideaherewouldbetoreviewyourSaveToFileandLoadFromFilecalls,andaddasecondparametertothemtoindicatehowyou’dlikeyourdatasaved.Ifyoudon’tthinkyou’lleverbeaddingorusingUnicodestrings,though,youcanleavethingsastheyare.
ExistingcodethatneedstocreateaCharfromanintegervaluemaymakeuseoftheChrfunction.CertainusesoftheChrfunctionmayresultinthefollowingerror:
[DCCError]PasParser.pas(169):E2010Incompatibletypes:'AnsiChar'and'Char'IfcodeusingtheChrfunctionisassigningtheresulttoanAnsiChar,thenthiserrorcaneasilyberemovedbyreplacingtheChrfunctionwithacasttoAnsiChar.
So,thiscode
MyChar:=chr(i);Canbechangedto
MyChar:=AnsiChar(i);SetsofCharactersProbablythemostcommoncodeidiomthatwilldrawtheattentionofthecompileristheuseofcharactersinsets.Inthepast,acharacterwasonebyte,soholdingcharactersinasetwasnoproblem.Butnow,CharisdeclaredasaWideChar,andthuscannotbeheldinasetanylonger.So,ifyouhavesomecodethatlookslikethis:
procedureTDemoForm.Button1Click(Sender:TObject);varC:Char;beginC:=Edit1.Text[1];ifCin['a'..'z','A'..'Z']thenbeginLabel1.Caption:='Itisthere';end;end;andyoucompileit,you’llgetawarningthatlookssomethinglikethis:
[DCCWarning]Unit1.pas(40):W1050WideCharreducedtobytecharinsetexpressions.Considerusing'CharInSet'functionin'SysUtils'unit.Youcan,ifyoulike,leavethecodethatway–thecompilerwill“know”whatyouaretryingtodoandgeneratethecorrectcode.However,ifyouwanttogetridofthewarning,youcanusethenewCharInSetfunction:
ifCharInSet(C,['a'..'z','A'..'Z'])thenbeginLabel1.Caption:='Itisthere';end;TheCharInSetfunctionwillreturnaBooleanvalue,andcompilewithoutthecompilerwarning.
Acommonidiomistouseastringasadatabuffer.It’scommonbecauseit’sbeeneasy--manipulatingstringsisgenerallyprettystraightforward.However,existingcodethatdoesthiswillalmostcertainlyneedtobeadjustedgiventhefactthatstringnowisaUnicodeString.
Thereareacoupleofwaystodealwithcodethatusesastringasadatabuffer.ThefirstistosimplydeclarethevariablebeingusedasadatabufferasanAnsiStringinsteadofstring.IfthecodeusesChartomanipulatebytesinthebuffer,declarethosevariablesasAnsiChar.Ifyouchoosethisroute,allyourcodewillworkasbefore,butyoudoneedtobecarefulthatyou’veexplicitlydeclaredallvariablesaccessingthestringbuffertobeANSItypes.
Thesecondandpreferredwaydealingwiththissituationistoconvertyourbufferfromastringtypetoanarrayofbytes,orTBytes.TBytesisdesignedspecificallyforthispurpose,andworksasyoulikelywereusingthestringtypepreviously.
CallstoSizeOfwhenusedwithcharacterarraysshouldbereviewedforcorrectness.Considerthefollowingcode:
procedureTDemoForm.Button1Click(Sender:TObject);varvarP:array[0..16]ofChar;beginStrPCopy(P,'Thisisastring');Memo1.Lines.Add('LengthofPis'+IntToStr(Length(P)));Memo1.Lines.Add('SizeofPis'+IntToStr(SizeOf(P)));end;ThiscodewilldisplaythefollowinginMemo1:
LengthofPis17SizeofPis34Intheabovecode,Lengthwillreturnthenumberofcharactersinthegivenstring(plusthenullterminationcharacter),butSizeOfwillreturnthetotalnumberofBytesusedbythearray,inthiscase34,i.e.twobytespercharacter.Inpreviousversions,thiscodewouldhavereturned17forboth.
CallstoFillCharneedtobereviewedwhenusedinconjunctionwithstringsoracharacter.Considerthefollowingcode:
varCount:Integer;Buffer:array[0..255]ofChar;begin//Existingcode-incorrectwhenstring=UnicodeStringCount:=Length(Buffer);FillChar(Buffer,Count,0);//CorrectforUnicode–eitheronewillbecorrectCount:=SizeOf(Buffer);//<<--SpecifybuffersizeinbytesCount:=Length(Buffer)*SizeOf(Char);//<<--SpecifybuffersizeinbytesFillChar(Buffer,Count,0);end;LengthreturnsthesizeincharactersbutFillCharexpectsCounttobeinbytes.Inthiscase,SizeOfshouldbeusedinsteadofLength(orLengthneedstobemultipliedbythesizeofChar).
Inaddition,becausethedefaultsizeofaCharis2,FillCharwillfillastringwithbytes,notCharaspreviously
Example:
varBuf:array[0..32]ofChar;beginFillChar(Buf,Length(Buf),#9);end;Thisdoesn’tfillthearraywithcodepoint$09butcodepoint$0909.Inordertogettheexpectedresultthecodeneedstobechangedto:
ifEdit1.Text[1]=#128thenwillrecognizetheEurosymbolandthusevaluatetoTrueinmostANSIcodepages.However,itwillevaluatetoFalseinDelphi2009becausewhile#128istheeurosigninmostANSIcodepages,itisacontrolcharacterinUnicode.InUnicode,Eurosymbolis#$20AC.
Developersshouldreplaceanycharacters#128-#255withliterals,whenconvertingtoDelphi2009,since:
ifEdit1.Text[1]='€'thenwillworkthesameas#128inANSI,butalsowork(i.e.,recognizetheEuro)inDelphi2009(where'€'is#$20AC)
CallstoMoveneedtobereviewedwhenstringsorcharacterarraysareused.Considerthefollowingcode:
varCount:Integer;Buf1,Buf2:array[0..255]ofChar;begin//Existingcode-incorrectwhenstring=UnicodeStringCount:=Length(Buf1);Move(Buf1,Buf2,Count);//CorrectforUnicodeCount:=SizeOf(Buf1);//<<--SpecifybuffersizeinbytesCount:=Length(Buf1)*SizeOf(Char);//<<--SpecifybuffersizeinbytesMove(Buf1,Buf2,Count);end;LengthreturnsthesizeincharactersbutMoveexpectsCounttobeinbytes.Inthiscase,SizeOfshouldbeusedinsteadofLength(orLengthneedstobemultipliedbythesizeofChar).
CallstoTStream.Read/ReadBufferneedtobereviewedwhenstringsorcharacterarraysareused.Considerthefollowingcode:
varS:string;L:Integer;Stream:TStream;Temp:AnsiString;begin//Existingcode-incorrectwhenstring=UnicodeStringStream.Read(L,SizeOf(Integer));SetLength(S,L);Stream.Read(Pointer(S)^,L);//CorrectforUnicodestringdataStream.Read(L,SizeOf(Integer));SetLength(S,L);Stream.Read(Pointer(S)^,L*SizeOf(Char));//<<--Specifybuffersizeinbytes//CorrectforAnsistringdataStream.Read(L,SizeOf(Integer));SetLength(Temp,L);//<<--UsetemporaryAnsiStringStream.Read(Pointer(Temp)^,L*SizeOf(AnsiChar));//<<--SpecifybuffersizeinbytesS:=Temp;//<<--WidenstringtoUnicodeend;Note:Thesolutiondependsontheformatofthedatabeingread.SeethenewTEncodingclassdescribedabovetoassistinproperlyencodingthetextinthestream.
AswithRead/ReadBuffer,callstoTStream.Write/WriteBufferneedtobereviewedwhenstringsorcharacterarraysareused.Considerthefollowingcode:
varS:string;Stream:TStream;Temp:AnsiString;begin//Existingcode-incorrectwhenstring=UnicodeStringStream.Write(Pointer(S)^,Length(S));//CorrectforUnciodedataStream.Write(Pointer(S)^,Length(S)*SizeOf(Char));//<<--Specifcybuffersizeinbytes//CorrectforAnsidataTemp:=S;//<<--UsetemporaryAnsiStringStream.Write(Pointer(Temp)^,Length(Temp)*SizeOf(AnsiChar));//<<--Specifybuffersizeinbytesend;Note:Thesolutiondependsontheformatofthedatabeingwritten.SeethenewTEncodingclassdescribedabovetoassistinproperlyencodingthetextinthestream.
Replacecallslikethis:
ifStr[I]inLeadBytesthenwiththeIsLeadCharfunction:
ifIsLeadChar(Str[I])thenTMemoryStreamIncaseswhereaTMemoryStreamisbeingusedtowriteoutatextfile,itwillbeusefultowriteoutaByteOrderMark(BOM)asthefirstentryinthefile.HereisanexampleofwritingtheBOMtothefile:
varBOM:TBytes;begin...BOM:=TEncoding.UTF8.GetPreamble;Write(BOM[0],Length(BOM));AllwritingcodewillneedtobechangedtoUTF8encodetheUnicodestring:
varTemp:Utf8String;begin...Temp:=Utf8Encode(Str);//<--Stristhestringbeingwrittenouttothefile.Write(Pointer(Temp)^,Length(Temp));//Write(Pointer(Str)^,Length(Str));<--thisistheoriginalcalltowritethestringtothefile.TStringStreamTStringStreamnowdescendsfromanewtype,TByteStream.TByteStreamaddsapropertynamedByteswhichallowsfordirectaccesstothebyteswithaTStringStream.TStringStreamworksasitalwayshas,withtheexceptionthatthestringitholdsisaUnicode-basedstring.
CallstoMultiByteToWideCharcansimplyberemovedandreplacedwithasimpleassignment.AnexamplewhenusingMultiByteToWideChar:
procedureTWideCharStrList.AddString(constS:string);varSize,D:Integer;beginSize:=SizeOf(S);D:=(Size+1)*SizeOf(WideChar);FList[FUsed]:=AllocMem(D);MultiByteToWideChar(0,0,PChar(S),Size,FList[FUsed],D);Inc(FUsed);end;AndafterthechangetoUnicode,thiscallwaschangedtosupportcompilingunderbothANSIandUnicode:
procedureTWideCharStrList.AddString(constS:string);varL,D:Integer;beginFList[FUsed]:=StrNew(PWideChar(S));Inc(FUsed);end;SysUtils.AppendStrThismethodisdeprecated,andassuch,ishard-codedtouseAnsiStringandnoUnicodeStringoverloadisavailable.
AppendStr(String1,String2);withcodelikethis:
String1:=String1+String2;Or,betteryet,usethenewTStringBuilderclasstoconcatenatestrings.
CallstoGetProcAddressshouldalwaysusePAnsiChar(thereisnoW-suffixedfunctionintheSDK).Forexample:
procedureCallLibraryProc(constLibraryName,ProcName:string);varHandle:THandle;RegisterProc:function:HResultstdcall;beginHandle:=LoadOleControlLibrary(LibraryName,True);@RegisterProc:=GetProcAddress(Handle,PAnsiChar(AnsiString(ProcName)));end;Note:Windows.paswillprovideanoverloadedmethodthatwilldothisconversion.
Inpreviousversions,notalltypedpointerssupportedpointerarithmetic.Becauseofthis,thepracticeofcastingvariousnon-charpointerstoPCharisusedtoenablepointerarithmetic.ForDelphi2009,pointerarithmeticcanbeenabledusingacompilerdirective,anditisspecificallyenabledforthePBytetype.Therefore,ifyouhavecodelikethefollowingthatcastspointerdatatoPCharforthepurposeofperformingpointerarithmeticonit:
functionTCustomVirtualStringTree.InternalData(Node:PVirtualNode):Pointer;beginif(Node=FRoot)or(Node=nil)thenResult:=nilelseResult:=PChar(Node)+FInternalDataOffset;end;YoushouldchangethistousePByteratherthanPChar:
functionTCustomVirtualStringTree.InternalData(Node:PVirtualNode):Pointer;beginif(Node=FRoot)or(Node=nil)thenResult:=nilelseResult:=PByte(Node)+FInternalDataOffset;end;Intheabovesnippet,Nodeisnotactuallycharacterdata.ItisbeingcasttoaPCharmerelyforthepurposeofusingpointerarithmetictoaccessdatathatisacertainnumberofbytesafterNode.ThisworkedpreviouslybecauseSizeOf(Char)=Sizeof(Byte).Thisisnolongertrue,andtoensurethecoderemainscorrect,itneedstobechangetousePByteratherthanPChar.Withoutthechange,Resultwillenduppointingtotheincorrectdata.
IfyouhavecodethatusesTVarRectohandlevariantopenarrayparameters,youmayneedtoadjustittohandleUnicodeString.AnewtypevtUnicodeStringisdefinedforusewithUnicodeStrings.TheUnicodeStringdataisheldinvUnicodeString.SeethefollowingsnippetfromDesignIntf.pas,showingacasewherenewcodeneededtobeaddedtohandletheUnicodeStringtype.
procedureRegisterPropertiesInCategory(constCategoryName:string;constFilters:arrayofconst);overload;varI:Integer;beginifAssigned(RegisterPropertyInCategoryProc)thenforI:=Low(Filters)toHigh(Filters)dowithFilters[I]docasevTypeofvtPointer:RegisterPropertyInCategoryProc(CategoryName,nil,PTypeInfo(vPointer),);vtClass:RegisterPropertyInCategoryProc(CategoryName,vClass,nil,);vtAnsiString:RegisterPropertyInCategoryProc(CategoryName,nil,nil,string(vAnsiString));vtUnicodeString:RegisterPropertyInCategoryProc(CategoryName,nil,nil,string(vUnicodeString));elseraiseException.CreateResFmt(@sInvalidFilter,[I,vType]);end;end;CreateProcessWTheUnicodeversionofCreateProcess(CreateProcessW)behavesslightlydifferentlythantheANSIversion.ToquoteMSDNinreferencetothelpCommandLineparameter:
"TheUnicodeversionofthisfunction,CreateProcessW,canmodifythecontentsofthisstring.Therefore,thisparametercannotbeapointertoread-onlymemory(suchasaconstvariableoraliteralstring).Ifthisparameterisaconstantstring,thefunctionmaycauseanaccessviolation."
Becauseofthis,someexistingcodethatcallsCreateProcessmaystartgivingAccessViolationswhencompiledinDelphi2009.
Examplesofproblematiccode:
Passinginastringconstant
CreateProcess(nil,'foo.exe',nil,nil,False,0,nil,nil,StartupInfo,ProcessInfo);Passinginaconstantexpression
constcMyExe='foo.exe'beginCreateProcess(nil,cMyExe,nil,nil,False,0,nil,nil,StartupInfo,ProcessInfo);end;PassinginastringwithaReferenceCountof-1:
TheyrepresentcodeconstructsthatcouldpotentiallyneedtobechangedtosupportthenewUnicodeStringtype.
SothatsumsupthetypesofcodeidiomsyouneedtoreviewforcorrectnessintheUnicodeworld.Ingeneral,mostofyourcodeshouldwork.Mostofthewarningsyourcodewillreceivecanbeeasilyfixedup.Mostofthecodepatternsyou’llneedtoreviewaregenerallyuncommon,soitislikelythatmuchifnotallofyourexistingcodewillworkjustfine.
InPartII,we’lllookatsomeofthenewfeaturesoftheDelphiRuntimeLibrarythatsupportUnicodeandgeneralstringhandling.
TheTiburonRTLincludesanewclasscalledTCharacter,whichisfoundintheCharacterunit.Itisasealedclassthatconsistsentirelyofstaticclassfunctions.DevelopersshouldnotcreateinstancesofTCharacter,butrathermerelycallitsstaticclassmethodsdirectly.Thoseclassfunctionsdoanumberofthings,including:
TCharacterusesthestandardssetforthbytheUnicodeconsortium.
DeveloperscanusetheTCharacterclasstodomanythingspreviouslydonewithsetsofchars.Forinstance,thiscode:
usesCharacter;beginifMyCharin[‘a’...’z’,‘A’...’Z’]thenbegin...end;end;canbeeasilyreplacedwith
usesCharacter;beginifTCharacter.IsLetter(MyChar)thenbegin...end;end;TheCharacterunitalsocontainsanumberofstandalonefunctionsthatwrapupthefunctionalityofeachclassfunctionfromTCharacter,soifyoupreferasimplefunctioncall,theabovecanbewrittenas:
usesCharacter;beginifIsLetter(MyChar)thenbegin...end;end;ThustheTCharacterclasscanbeusedtodomostanymanipulationorcheckingofcharactersthatyoumightcaretodo.
Inaddition,TCharactercontainsclassmethodstodetermineifagivencharacterisahighorlowsurrogateofasurrogatepair.
TheTiburonRTLalsoincludesanewclasscalledTEncoding.ItspurposeistodefineaspecifictypeofcharacterencodingsothatyoucantelltheVCLwhattypeofencodingyouwantusedinspecificsituations.
Forinstance,youmayhaveaTStringListinstancethatcontainstextthatyouwanttowriteouttoafile.Previously,youwouldhavewritten:
begin...MyStringList.SaveToFile(‘SomeFilename.txt’);...end;andthefilewouldhavebeenwrittenoutusingthedefaultANSIencoding.Thatcodewillstillworkfine–itwillwriteoutthefileusingANSIstringencodingasitalwayshas,butnowthatDelphisupportsUnicodestringdata,developersmaywanttowriteoutstringdatausingaspecificencoding.Thus,SaveToFile(aswellasLoadFromFile)nowtakeanoptionalsecondparameterthatdefinestheencodingtobeused:
begin...MyStringList.SaveToFile(‘SomeFilename.txt’,TEncoding.Unicode);...end;ExecutetheabovecodeandthefilewillbewrittenoutasaUnicode(UTF-16)encodedtextfile.
TEncodingwillalsoconvertagivensetofbytesfromoneencodingtoanother,retrieveinformationaboutthebytesand/orcharactersinagivenstringorarrayofcharacters,convertanystringintoanarrayofbyte(TBytes),andotherfunctionalitythatyoumayneedwithregardtothespecificencodingofagivenstringorarrayofchars.
TheTEncodingclassincludesthefollowingclasspropertiesthatgiveyousingletonaccesstoaTEncodinginstanceofthegivenencoding:
classpropertyASCII:TEncodingreadGetASCII;classpropertyBigEndianUnicode:TEncodingreadGetBigEndianUnicode;classpropertyDefault:TEncodingreadGetDefault;classpropertyUnicode:TEncodingreadGetUnicode;classpropertyUTF7:TEncodingreadGetUTF7;classpropertyUTF8:TEncodingreadGetUTF8;TheDefaultpropertyreferstotheANSIactivecodepage.TheUnicodepropertyreferstoUTF-16.
TEncodingalsoincludesthe
classfunctionTEncoding.GetEncoding(CodePage:Integer):TEncoding;thatwillreturnaninstanceofTEncodingthathastheaffinityforthecodepagepassedintheparameter.
Inaddition,itincludesfollowingfunction:
functionGetPreamble:TBytes;whichwillreturnthecorrectBOMforthegivenencoding.
TEncodingisalsointerfacecompatiblewiththe.NetclasscalledEncoding.
TheRTLnowincludesaclasscalledTStringBuilder.Itspurposeisrevealedinitsname–itisaclassdesignedto“buildup”strings.TStringBuildercontainsanynumberofoverloadedfunctionsforadding,replacing,andinsertingcontentintoagivenstring.Thestringbuilderclassmakesiteasytocreatesinglestringsoutofavarietyofdifferentdatatypes.AlloftheAppend,Insert,andReplacefunctionsreturnaninstanceofTStringBuilder,sotheycaneasilybechainedtogethertocreateasinglestring.
Forexample,youmightchoosetouseaTStringBuilderinplaceofacomplicatedFormatstatement.Forinstance,youmightwritethefollowingcode:
procedureTForm86.Button2Click(Sender:TObject);varMyStringBuilder:TStringBuilder;Price:double;beginMyStringBuilder:=TStringBuilder.Create('');tryPrice:=1.49;Label1.Caption:=MyStringBuilder.Append('Theapplesare$').Append(Price).Append('apound.').ToString;finallyMyStringBuilder.Free;end;end;TStringBuilderisalsointerfacecompatiblewiththe.NetclasscalledStringBuilder.
type//ThecodepageforANSI-Cyrillicis1251CyrillicString=typeAnsistring(1251);AndthenewStringtypewillbeastringwithanaffinityfortheCyrilliccodepage.
TheRTLaddsanumberofroutinesthatsupporttheuseofUnicodestrings.
StringElementSizereturnsthetypicalsizeforanelement(codepoint)inagivenstring.Considerthefollowingcode:
procedureTForm88.Button3Click(Sender:TObject);varA:AnsiString;U:UnicodeString;beginA:='ThisisanAnsiString';Memo1.Lines.Add('TheElementSizeforanAnsiStringis:'+IntToStr(StringElementSize(A)));U:='ThisisaUnicodeString';Memo1.Lines.Add('TheElementSizeforanUnicodeStringis:'+IntToStr(StringElementSize(U)));end;Theresultofthecodeabovewillbe:
procedureTForm88.Button2Click(Sender:TObject);type//ThecodepageforANSI-Cyrillicis1251CyrillicString=typeAnsiString(1251);varA:AnsiString;U:UnicodeString;U8:UTF8String;C:CyrillicString;beginA:='ThisisanAnsiString';Memo1.Lines.Add('AnsiStringCodepage:'+IntToStr(StringCodePage(A)));U:='ThisisaUnicodeString';Memo1.Lines.Add('UnicodeStringCodepage:'+IntToStr(StringCodePage(U)));U8:='ThisisaUTF8string';Memo1.Lines.Add('UTF8stringCodepage:'+IntToStr(StringCodePage(U8)));C:='ThisisaCyrillicString';Memo1.Lines.Add('CyrillicStringCodepage:'+IntToStr(StringCodePage(C)));end;Theabovecodewillresultinthefollowingoutput:
UnicodeStringToUCS4StringUCS4StringToUnicodeStringUnicodeToUtf8Utf8ToUnicodeInadditiontheRTLalsodeclaresatypecalledRawByteStringwhichisastringtypewithnoencodingaffiliatedwithit:
RawByteString=typeAnsiString($FFFF);ThepurposeoftheRawByteStringtypeistoenablethepassingofstringdataofanycodepagewithoutdoinganycodepageconversions.Thisismostusefulforroutinesthatdonotcareaboutspecificencoding,suchasbyte-orientedstringsearches.Normally,thiswouldmeanthatparametersofroutinesthatprocessstringswithoutregardforthestringscodepageshouldbeoftypeRawByteString.DeclaringvariablesoftypeRawByteStringshouldrarely,ifever,bedoneasthiscanleadtoundefinedbehaviorandpotentialdataloss.
Ingeneral,stringtypesareassignmentcompatiblewitheachother.
Forinstance:
MyUnicodeString:=MyAnsiString;willperformasexpected–itwilltakethecontentsoftheAnsiStringandplacethemintoaUnicodeString.Youshouldingeneralbeabletoassignonestringtypetoanother,andthecompilerwilldotheworkneededtomaketheconversions,ifpossible.
Someconversions,however,canresultindataloss,andonemustwatchoutthiswhenmovingfromonestringtypethatincludesUnicodedatatoanotherthatdoesnot.Forinstance,youcanassignUnicodeStringtoanAnsiString,butiftheUnicodeStringcontainscharactersthathavenomappingintheactiveANSIcodepageatruntime,thosecharacterswillbelostintheconversion.Considerthefollowingcode:
procedureTForm88.Button4Click(Sender:TObject);varU:UnicodeString;A:AnsiString;beginU:='ThisisaUnicodeString';A:=U;Memo1.Lines.Add(A);U:='ДобропожаловатьвмирЮникодасиспользованиемДельфи2009!!';A:=U;Memo1.Lines.Add(A);end;TheoutputoftheabovewhenthecurrentOScodepageis1252is:
ThisisaUnicodeString2009!!Asyoucansee,becauseCyrilliccharactershavenomappinginWindows-1252,informationwaslostwhenassigningthisUnicodeStringtoanAnsiString.TheresultwasgibberishbecausetheUnicodeStringcontainedcharactersnotrepresentableinthecodepageoftheAnsiString,thosecharacterswerelostandreplacedbythequestionmarkwhenassigningtheUnicodeStringtotheAnsiString.
SetCodePage,declaredintheSystem.pasunitas
procedureSetCodePage(varS:AnsiString;CodePage:Word;Convert:Boolean);isanewRTLfunctionthatsetsanewcodepageforagivenAnsiString.TheoptionalConvertparameterdeterminesifthepayloaditselfofthestringshouldbeconvertedtothegivencodepage.IftheConvertparameterisFalse,thenthecodepageforthestringismerelyaltered.IftheConvertparameterisTrue,thenthepayloadofthepassedstringwillbeconvertedtothegivencodepage.
SetCodePageshouldbeusedsparinglyandwithgreatcare.Notethatifthecodepagedoesn’tactuallymatchtheexistingpayload(i.e.ConvertissettoFalse),thenunpredictableresultscanoccur.Alsoiftheexistingdatainthestringisconvertedandthenewcodepagedoesn’thavearepresentationforagivenoriginalcharacter,datalosscanoccur.
TheRTLalsoincludesasetofoverloadedroutinesforextractinganarrayofbytesfromastring.Aswe’llseeinPartIII,itisrecommendedthatinsteadofusingstringasadatabuffer,youuseTBytesinstead.TheRTLmakesiteasybyprovidingoverloadedversionsofBytesOf()thattakesasaparameterthedifferentstringtypes.
Tiburon’sRuntimeLibraryisnowcompletelycapableofsupportingthenewUnicodeString.Itincludesnewclassesandroutinesforhandling,processing,andconvertingUnicodestrings,formanagingcodepages,andforensuringaneasymigrationfromearlierversions.
InPartIII,we’llcoverthespecificcodeconstructsthatyou’llneedtolookoutforinensuringthatyourcodeisUnicodeready.
---
TheInternethasbrokendowngeographicalbarriersthatenableworld-widesoftwaredistribution.Asaresult,applicationscannolongerliveinapurelyANSI-basedenvironment.TheworldhasembracedUnicodeasthestandardmeansoftransferringtextanddata.Sinceitprovidessupportforvirtuallyanywritingsystemintheworld,Unicodetextisnowthenormthroughouttheglobaltechnologicalecosystem.
AmongthemanynewfeaturesfoundinDelphi2009istheimbuingofUnicodethroughouttheproduct.ThedefaultstringinDelphiisnowaUnicode-basedstring.SinceDelphiislargelybuiltwithDelphi,theIDE,thecompiler,theRTL,andtheVCLallarefullyUnicode-enabled.
ThemovetoUnicodeinDelphiisanaturalone.WindowsitselfisfullyUnicode-aware,soitisonlynaturalthatapplicationsbuiltforit,useaUnicodestringasthedefaultstring.AndforDelphidevelopers,thebenefitsdon’tstopmerelyatbeingabletousethesamestringtypeasWindows.
TheadditionofUnicodesupportprovidesDelphideveloperswithagreatopportunity.Delphidevelopersnowcanread,write,accept,produce,display,anddealwithUnicodedata–andit’sallbuiltrightintotheproduct.Withonlyfew,orinsomecasestozerocodechanges,yourapplicationscanbereadyforanykindofdatayou,yourcustomersoryouruserscanthrowatit.ApplicationsthatpreviouslyrestrictedtoANSIencodeddatacanbeeasilymodifiedtohandlealmostanycharactersetintheworld.
Delphideveloperswillnowbeabletoserveaglobalmarketwiththeirapplications--eveniftheydon’tdoanythingspecialtolocalizeorinternationalizetheirapplications.Windowsitselfsupportsmanydifferentlocalizedversions,andDelphiapplicationsneedtobeabletoadaptandworkonmachinesrunninganyofthelargenumberoflocalesthatWindowssupports,includingtheJapanese,Chinese,Greek,orRussianversionsofWindows.Usersofyoursoftwaremaybeenteringnon-ANSItextintoyourapplicationorusingnon-ANSIbasedpathnames.ANSI-basedapplicationswon’talwaysworkasdesiredinthosescenarios.WindowsapplicationsbuiltwithafullyUnicode-enabledDelphiwillbeabletohandleandworkinthosesituations.Evenifyoudon’ttranslateyourapplicationintoanyotherspokenlanguages,yourapplicationstillneedstobeabletoworkproperly--nomatterwhattheenduser’slocaleis.
ForexistingANSI-basedDelphiapplications,thenopportunitytolocalizeapplicationsandexpandthereachofthoseapplicationsintoUnicode-basedmarketsispotentiallyhuge.Andifyoudowanttolocalizeyourapplications,Delphimakesthatveryeasy,especiallynowatdesign-time.TheIntegratedTranslationEnvironment(ITE)enablesyoutotranslate,compile,anddeployanapplicationrightintheIDE.Ifyourequireexternaltranslationservices,theIDEcanexportyourprojectinaformthattranslatorscanuseinconjunctionwiththedeployableExternalTranslationManager.ThesetoolsworktogetherwiththeDelphiIDEforbothDelphiandC++Buildertomakelocalizingyourapplicationsasmoothandeasytomanageprocess.
TheworldisUnicode-based,andnowDelphideveloperscanbeapartofthatinanative,organicway.SoifyouwanttobeabletohandleUnicodedata,orifyouwanttosellyourapplicationstoemergingandglobalmarkets,youcandoitwithDelphi2009.
Unicodeencouragestheuseofsomenewterms.Forinstancetheideaof“character”isabitlesspreciseintheworldofUnicodethanyoumightbeusedto.InUnicode,themoreprecisetermis“codepoint”.InDelphi2009,theSizeOf(Char)is2,buteventhatdoesn’ttellthewholestory.Dependingontheencoding,itispossibleforagivencharactertotakeupmorethantwobytes.Thesesequencesarecalled“SurrogatePairs”.SoacodepointisauniquecodeassignedanelementdefinedbytheUnicode.org.Mostcommonlythatisa“character”,butnotalways.
Nowthatallthathasbeenexplained,we’lllookathowDelphi2009implementsaUnicode-basedstring.
ThedefaultstringinDelphi2009isthenewUnicodeStringtype.Bydefault,theUnicodeStringtypewillhaveanaffinityforUTF-16,thesameencodingusedbyWindows.ThisisachangefrompreviousversionswhichhadAnsiStringasthedefaulttype.TheDelphiRTLhasinthepastincludedtheWideStringtypetohandleUnicodedata,butthistypeisnotreference-countedastheAnsiStringtypeis,andthusisn’tasfull-featuredasDelphidevelopersexpectthedefaultstringtobe.
ForDelphi2009,anewUnicodeStringtypehasbeendesigned,thatincorporatesthecapabilitiesofboththeAnsiStringandWideStringtypes.AUnicodeStringcancontaineitheraUnicode-sizedcharacter,oranANSIbyte-sizedcharacter.(NotethatboththeAnsiStringandWideStringtypeswillremaininplace.)TheCharandPChartypeswillmaptoWideCharandPWideChar,respectively.Note,aswell,thatnostringtypeshavedisappeared.Allthetypesthatdevelopersareusedtostillexistandworkasbefore.
However,forDelphi2009,thedefaultstringtypewillbeequivalenttoUnicodeString.Inaddition,thedefaultChartypeisWideChar,andthedefaultPChartypeisPWideChar.
Thatis,thefollowingcodeisdeclaredbythecompiler:
typestring=UnicodeString;Char=WideChar;PChar=PWideChar;UnicodeStringisassignmentcompatiblewithallotherstringtypes;however,assignmentsbetweenAnsiStringsandUnicodeStringswilldotypeconversionsasappropriate.Thus,anassignmentofaUnicodeStringtypetoanAnsiStringtypecouldresultindata-loss.Thatis,ifaUnicodeStringcontainshigh-orderbytedata,aconversionofthatstringtoAnsiStringwillresultinalossofthathigh-orderbytedata.
TheimportantthingtonotehereisthatthisnewUnicodeStringbehavesprettymuchlikestringsalwayshave(withthenotableexceptionoftheirabilitytoholdUnicodedata,ofcourse).Youcanstilladdanystringdatatothem,youcanindexthem,youcanconcatenatethemwiththe‘+’sign,etc.
Forexample,instancesofaUnicodeStringwillstillbeabletoindexcharacters.Considerthefollowingcode:
varMyChar:Char;MyString:string;beginMyString:=‘Thisisastring’;MyChar:=MyString[1];end;ThevariableMyCharwillstillholdthecharacterfoundatthefirstindexposition,i.e.‘T’.Thisfunctionalityofthiscodehasn’tchangedatall.Similarly,ifwearehandlingUnicodedata:
varMyChar:Char;MyString:string;beginMyString:=‘世界您好‘;MyChar:=MyString[1];end;ThevariableMyCharwillstillholdthecharacterfoundatthefirstindexposition,i.e.‘世’.
TheRTLprovideshelperfunctionsthatenableuserstodoexplicitconversionsbetweencodepagesandelementsizeconversions.IftheuserisusingtheMovefunctiononthecharacterarray,theycannotmakeassumptionsabouttheelementsize.
Asyoucanimagine,thisnewstringtypehasramificationsforexistingcode.WithUnicode,itisnolongertruethatoneCharrepresentsoneByte.Infact,itisn’tevenalwaystruethatoneCharisequaltotwobytes!Asaresult,youmayhavetomakesomeadjustmentstoyourcode.However,we’veworkedveryhardtomakethetransitionasmoothone,andweareconfidentthatyou’llbeabletobeupandrunningquitequickly.PartsIIandIIIofthisserieswilldiscussfurtherthenewUnicodeStringtype,talkaboutsomeofthenewfeaturesoftheRTLthatsupportUnicodeenablement,andthendiscussspecificcodingidiomsthatyou’llwanttolookforinyourcode.ThisseriesshouldhelpmakeyourtransitiontoUnicodeasmoothandpainlessendeavor.
WiththeadditionofUnicodeasthedefaultstring,Delphicanaccept,process,anddisplayvirtuallyanyalphabetorcodepageintheworld.ApplicationsyoubuildwithDelphi2009willbeabletoaccept,display,andhandleUnicodetextwithease,andtheywillworkmuchbetterinalmostanyWindowslocale.Delphideveloperscannoweasilylocalizeandtranslatetheirapplicationstoentermarketsthattheyhavepreviouslybeenmoredifficulttoenter.It’saUnicodeworldoutthere,andnowyourDelphiappscanliveinit.
InPartII,we’lldiscussthechangesandupdatestotheDelphiRuntimeLibrarythatwillenableyoutoworkeasilywithUnicodestrings.
以下为有道自动翻译(Delphi园地站长注:翻译不是很准确,如有读者有兴趣翻译,请发给我们发布,谢谢):
兼容所有作业UnicodeString是其他字符串类型;然而,作业和UnicodeStringsAnsiStrings之间做适当的类型转换。因此,赋值类型的一个AnsiStringUnicodeStringdata-loss类型可能导致。这就是说,如果一个UnicodeString含有高阶字节数据转换的那根绳子,将导致损失AnsiString高字节的数据。最重要的是要注意这是这个新的UnicodeString表现得很像串总是(有例外的能力,当然Unicode数据)。你还可以加入任何一个字符串数据,你可以指数,可以连结的“+”号签署,等等。例如,一个UnicodeString仍能够指标特征。考虑以下代码:varMyChar:Char;MyString:string;beginMyString:=‘Thisisastring’;MyChar:=MyString[1];end;
这个变量MyChar仍然会出现在第一个字符的索引位置。”,即“。这一功能的代码并没有改变。同样的,如果我们要处理Unicode数据:varMyChar:Char;MyString:string;beginMyString:=‘世界您好‘;MyChar:=MyString[1];end;
怎样制作CHM格式的电子书?
CHM帮助文件是什么?
原来的软件大多数采用扩展名为HLP的帮助文件(WinHelp),但随着互联网的发展,这种格式的帮助文件已经难以适应软件在线帮助的需要,以及更加人性化更加简单易于查看的需要,因此一种全新的帮助文件系统HTMLHelp由微软率先在Windows98中使用了。由于它是一个经过压缩的网页集合,不但减小了文件的体积,更利于用户从INTERNET上下载,并且还支持HTML、Ac-tiveX、Java、JScript、VisualBasicScripting和多种图像格式(.jpeg、.gif和.png等),因此很快受到广大软件作者和软件用户的欢迎。不过,它的用途又何止这些?
安装完EasyCHM后,您可以在开始菜单|程序|EasyCHM中打开它。要建立CHM帮助文件必须先将我们所有想要让其出现在帮助文件中的内容做成相对独立的网页文件(即HTML文件),这些工作可以在EasyCHM中完成,也可以应用其他网页编辑器(如FontPage或HotDog)来制作。
比如说我们这里将其作为例子的《围城》,就可以按不同的章节来制作。另外,HTML文件之间应该相互链接,如每一章之间应该保留与上一章和下一章超级链接的接口,将这些相对独立的网页制作完成后,将它们储存到一个新建的文件夹中。
首先我们先介绍几种将要用到的过渡文件:
1.hhp文件,这是最常用的一类文件,它实际上是“HTMLHelpproject”的缩写,表示HTML帮助文件项目,它是生成CHM文件最直接用到的一类文件,只需要有一个hhp文件就可以根据它来编译相应的CHM文件。
2.hhc文件,它是“HTMLHelptableofcontents”的缩写,表示的是HTML帮助文件的目录,通常我们在CHM文件浏览器的左边窗口中看到的目录内容就由它来提供,当然,它并不能直接被编译成CHM文件,而先要集成到某一hhp文件中才能发挥作用。
3.hhk文件,它是“HTMLHelpIndexKeyword”的缩写,为我们提供了CHM文件的关键字索引查询功能,也是一个易于查看的帮助文件不可缺少的一部分,同hhc文件一样,它也不能直接编译生成相应的CHM文件。
用EasyCHM制作CHM的简单步骤如下:
1、启动后单击工具栏的“新建”按钮,新建一个工程,选择工程目录(也就是你的网页文件所在的文件夹)。单击“确定”把所有的网页导入。2、单击工具栏的“编译”按钮,键入CHM文件的标题(如:小椴作品典藏版v1.5),CHM的第一页和主页选择一下,选择CHM文件的保存位置。3、单击CHM设置,在“面板”选项卡中取消勾选“目录、索引、搜索、书签”。(这里视情况而定,如果你的图书需要显示“目录、索引、搜索、书签”中的项,勾选即可。)在“位置”选项卡中设置宽度为1024(我的是标准屏幕),宽度为738(根据你的需要设定此值),可以用“窗体定位工具”进行设置。在“其他”选项卡中取消勾选“允许CHM记忆窗体的位置和大小”。4、点击确定后回到“编译工程为CHM”界面,单击“应用”按钮保存以上的设置,单击“生成CHM”,软件会自动生成电子图书。5、单击“查看CHM”,看看有没有不合适的地方,有的话修改一下,没有的话我们的书就做好了。
很快,一本CHM格式的电子版小说就这样生成了,它虽然只是一个独立的文件,但CHM却可以完全脱离自己的机器、脱离EasyCHM运行(需要IE4.0以上支持)。
一个比较简单的CHM帮助文件(没有目录、没有索引)的制作过程就是这样。
经过上面的介绍,现在您一定可以自己编辑制作帮助文件和电子文档了吧,EasyCHM的用途其实还有很多,其中另一个不得不说的功能是:它不仅可以编译一个CHM文件,更可以对现有的CHM文档进行反编译,这样我们可以更直接地借鉴和使用一些制作得非常精美的帮助文档的某些精彩部分,有时搞一点“拿来主义”感觉还是不错的。
Delphi与Vista提供的UAC控制
Vista提供的UAC机制,是Vista的新增功能之一。它的主要目的是防止对于操作系统本身的恶意修改。如果想对于Vista的系统设置进行改动,必须通过UAC的验证才能够进行。通过这样的手段,大大提供了系统的安全性。
关于UAC的利弊,网络上的说法褒贬不一,在这里就不具体讨论了。
对于Delphi程序的影响,UAC主要在于以下几点:
1、由于UAC机制,Delphi对于系统的操作可能无声的失败,而同样的程序,在2000/X下面可能运行正常。譬如注册表的改动。。。
2、为了避免这样的情况,Delphi程序必须支持VistaUAC标注,也就是说,在UAC程序的图标下面显示盾牌标志。这样可以在需要系统更高权限的时候,提醒用户。
为了让程序显示UAC标志,现在看来Vista是通过读取程序的资源(Resource)里面的MANIFEST资源,来决定是否显示“UAC盾牌”。
为了支持Vista,Delphi程序必须在资源里面嵌入MANIFEST信息。
1、首先编辑一个文件,内容如下:
保持为UAC.manifest,这里文件是随意的。特别注意红色的“requireAdministrator”,这个表示程序需要管理员(Administrator)才能正常运行。
2、然后编辑一个RC文件,名为uac.rc
124UAC.manifest
其中:
1-代表资源编号
24-资源类型为RTMAINIFEST
UAC.manifest-前面的文件名称
3、用brcc32编译这个rc文件为res文件
brcc32uac.rc-fouac.res
4、在程序里面加入
{$Ruac.res}
让Delphi编译的时候,把uac.res编译进exe文件
5、把文件放到vista里面运行,就会看程序图标下面显示UAC盾牌标志了。