给你转贴一下别人的一个帖子:
自从Delphi6出来以后,一个经常被提到的问题是Proxies.pas文件从源文件中消失了。
这个改变是大趋势的一个部分。Borland在Delphi 5中没有装载DsgnIntf.dcu,这显然是要强迫迎合Delphi和C++Builder的许可协议。运行时代码在很多控件中常被不经意地用到。在某些方面Borland鼓励:如果你运用新的控件向导,你将发现这个向导只创建了一个单元,它把控件运行时的框架代码和注册函数放在同一个单元中。
在Delphi6中Borland更进一步,不仅用DesignIntf替换了DsgnIntf,而且属性编辑器也被放进了DesignEditors,DesignMenus,DesignWindows和其它的一些设计文件里。特别是DesignEditors使用了其它的一个名叫Proxies的IDE文件。(Proxies代码放在DesignIDE.bpl文件中。)不用说这些改变将会产生编译时的错误。
如果你的运行时代码已经与设计时代码分开了,那么就很容易修改。打开设计时包,然后选择一个目录,点击Add按钮。填写designide.dcp和点击确定。重新编译你的包,这时错误已经没有了。
如果你的设计时代码和运行时代码已经混合在一起了,那应该怎么解决呢?在Delphi里DesignIDE.bpl不是一个可以再分发的包。因此,即使是只是设计时包使用了组件的运行时代码或是只是控件dcu用了都将产生问题。
99.99%的情况事实上很容易解决。你的运行时代码事实上没有使用设计时代码;问题是没有合适的分开。
设计时包应该包括:
1、所有的注册声明。
2、所有的属性编辑器。
3、所有的组件编辑器。
4、将需要DesignIDE和每一个保存组件自己的运行时包。
运行时包应该包括:
1、组件自己。
2、任何编辑器也许会用到的组件可能自己在运行时调用的窗体。
维一有点混惑的地方是:属或组件是否使用了一个窗体。假如这个窗体在运行时对于组件是可用的,那么它应该包含在运行时包里。如果它只是在设计时可使用,那它就应该包含在设计时包里。一个常见的错误是误认这个窗体本身是一个编辑器,但事实上它不是。而是组件编辑器调用了这个窗体,它是设计时编辑器。
你应该养成一个把组件分开成两个包的习惯,即使是你只在程序中静态地进行链接,因为混合运行时和设计时代码将使你的代码膨胀。你的设计时代码在运行时不会被执行,但是链接器不会知道,所以把它也一起链接进去了。(这就是为什么DsgnIntf要设法链接进去的原因。)
让我们看一个简单的例子,了解如何把设计时代码从运行时代码中分离出去:
{ TMixedComponent }
TMixedComponent = class(TComponent)
private
FFileName: String;
published
property FileName : String read FFileName write FFileName;
{ Published declarations }
end;
{ TMixedFileNameProperty }
TMixedFileNameProperty = class(TPropertyEditor)
function AllEqual: boolean;
override;
procedure Edit;
override;
function GetAttributes: TPropertyAttributes;
override;
function GetValue: string;
override;
procedure SetValue (const Value: string);
override;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterPropertyEditor(TypeInfo(string), TMixedComponent, ’FileName’,
TMixedFileNameProperty);
RegisterComponents(’Samples’, [TMixedComponent]);
end;
function TMixedFileNameProperty.AllEqual: boolean;
var
FirstVal: string;
i: Integer;
begin
FirstVal := GetStrValue;
Result := True;
i := 1;
while Result and (i < PropCount) do
begin
Result := Result and (GetStrValueAt(i) = FirstVal);
Inc(i);
end;
end;
procedure TMixedFileNameProperty.Edit;
var
Dlg: TOpenDialog;
begin
Dlg := TOpenDialog.Create(Application);
try
with Dlg do
begin
Title := ’File for ’ + TComponent(GetComponent(0)).Name;
FileName:= Value;
if Execute then
Value := FileName;
end;
finally
FreeAndNil(Dlg);
end
end;
function TMixedFileNameProperty.GetAttributes: TPropertyAttributes;
begin
Result := [paDialog]
end;
function TMixedFileNameProperty.GetValue: string;
begin
Result := GetStrValue;
end;
procedure TMixedFileNameProperty.SetValue(const Value: string);
begin
SetStrValue(Value);
end;
end.
把设计时代码从运行时代码中分离出去的最简单方法是,把所有需要DesignIntf 和DesignEditors 代码放入它们自己的单元中去,那个单元将要添加使用组件单元的声明。组件自己不需要知道那个自己运作的IDE编辑器。首先,把DesignIntf 和 DesignEditors 单元从组件单元的Uses部分删除掉,然后让编译/链接器告诉你哪些类需要移到它们自己的单元中去:
[Error] MixedComponent.pas(23): Undeclared identifier: ’TPropertyEditor’
[Error] MixedComponent.pas(23): Class type required
[Error] MixedComponent.pas(24): Method ’AllEqual’ not found in base class
[Error] MixedComponent.pas(25): Method ’Edit’ not found in base class
[Error] MixedComponent.pas(26): Undeclared identifier: ’TPropertyAttributes’
[Error] MixedComponent.pas(27): Method ’GetValue’ not found in base class
[Error] MixedComponent.pas(28): Method ’SetValue’ not found in base class
[Error] MixedComponent.pas(35): Undeclared identifier: ’RegisterPropertyEditor’
[Error] MixedComponent.pas(35): Undeclared identifier: ’TMixedFile’
[Error] MixedComponent.pas(46): Undeclared identifier: ’GetStrValue’
[Error] MixedComponent.pas(49): Undeclared identifier: ’PropCount’
[Error] MixedComponent.pas(51): Undeclared identifier: ’GetStrValueAt’
[Error] MixedComponent.pas(51): Operator not applicable to this operand type
[Error] MixedComponent.pas(64): Undeclared identifier: ’GetComponent’
[Error] MixedComponent.pas(65): Undeclared identifier: ’Value’
[Error] MixedComponent.pas(75): Undeclared identifier: ’paDialog’
[Error] MixedComponent.pas(80): Undeclared identifier: ’GetStrValue’
[Error] MixedComponent.pas(85): Undeclared identifier: ’SetStrValue’
[Fatal Error] JOComponents.dpk(33): Could not compile used unit
’MixedComponent.pas’
下一步是创建一个新的单元存放这些代码。可以命名为类似MixedComponentReg的名子。把Register函数也移到那个单元中去。下面我们可以从错误信息中得知哪些需要移走。第一个错误信息是[Error] MixedComponent.pas(23): Undeclared identifier: ’TPropertyEditor’,这个信息指出了一个继承自那个设计时单元的类。这是个很清楚的指示,它指明了它是设计时代码和这个类要被移到一个新创建的单元。
到此,运行时包将会被成功编译(如果还不行,继续把设计时代码从单元中移去,直到没有错误产生)。现在组件在你的应用程序运行时已不再需要Proxies.pas和其它设计时单元了。这个运行时组件非常简单,如下:
unit MixedComponent;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;
type
{ TMixedComponent }
TMixedComponent = class(TComponent)
private
FFileName: String;
published
property FileName : String read FFileName write FFileName;
{ Published declarations }
end;
implementation
end.
这最后一步就是把你的组件和它的属性编辑器编译到一个设计时包中,然后安装到IDE中去。
通过File | New | Other 选择 package创建一个新的包。调出包选项,选择design-time only,给Description 项填一个描述文字。选择Requires folder,点击Add按钮。在Requires Package 编辑对话框填写Designide.dcp,点击OK。同样,为你的组件运行时包添加dcp。在这种情况下,它放在了JOComponents.dpk,因此JOComponents.dcp被添加到requires项。在requires里有:JOComponents, designide 和 rtl。最后,选择包含目录,添加MixedComponentReg.pas 到包里。
到现在我们已经基本完成了!打开MixedComponentReg.pas 添加一对单元到uses部分,这要看你的组件或属性编辑器是否要在声明中使用这个组件(一些复杂的编辑器有时需要知道这个组件的存在)。如果是这样,把它加到Interface的uses部分。否则,加到implementation的uses部分。DesignIntf和DesignEditors就放到Interface的uses里。SysUtils, Forms, Dialogs, 和 Classes 在内部的属性编辑器的不同地方使用,因此就放到implementation部分。最后的MixedComponentReg应该象下面这样:
unit MixedComponentReg;
interface
uses DesignIntf, DesignEditors;
type
{ TMixedFileNameProperty }
TMixedFileNameProperty = class(TPropertyEditor)
function AllEqual: boolean;
override;
procedure Edit;
override;
function GetAttributes: TPropertyAttributes;
override;
function GetValue: string;
override;
procedure SetValue (const Value: string);
override;
end;
procedure Register;
implementation
uses MixedComponent, SysUtils, Forms, Dialogs, Classes;
procedure Register;
begin
RegisterPropertyEditor(TypeInfo(string), TMixedComponent, ’FileName’,
TMixedFileNameProperty);
RegisterComponents(’Samples’, [TMixedComponent]);
end;
function TMixedFileNameProperty.AllEqual: boolean;
var
FirstVal: string;
i: Integer;
begin
FirstVal := GetStrValue;
Result := True;
i := 1;
while Result and (i < PropCount) do
begin
Result := Result and (GetStrValueAt(i) = FirstVal);
Inc(i);
end;
end;
procedure TMixedFileNameProperty.Edit;
var
Dlg: TOpenDialog;
begin
Dlg := TOpenDialog.Create(Application);
try
with Dlg do
begin
Title := ’File for ’ + TComponent(GetComponent(0)).Name;
FileName:= Value;
if Execute then
Value := FileName;
end;
finally
FreeAndNil(Dlg);
end
end;
function TMixedFileNameProperty.GetAttributes: TPropertyAttributes;
begin
Result := [paDialog]
end;
function TMixedFileNameProperty.GetValue: string;
begin
Result := GetStrValue;
end;
procedure TMixedFileNameProperty.SetValue(const Value: string);
begin
SetStrValue(Value);
end;
end.
剩下的就是编译和安装设计时包了。现在运行时代码已经完全从设计时代码中分了出来。这是一个简单的例子,它唯一有点复杂的是属性编辑器使用一个窗体获得数据,而且那个窗体在运行时也可以利用。在这种情况下,窗体被保留在运行时包里,设计时属性编辑器会从运行时包里调用这个窗体。
[ 作者: 不详 添加时间: 2001-7-13 13:58:55 ]
原著:Borland Corporation
翻译:Musicwind®
日期:2001-07-11
声明:
以下文章的内容取自Delphi 6附带的帮助文件。
版权所有Borland Corporation。
翻译此稿事先没有得到Borland的批准,鲁莽之处,恳请Borland多多原谅。
总则
打开Delphi 5 (或更早版本)的工程,新版本会自动对其进行升级。以下将要介绍的是新版本中可能会对现有的Delphi工程带来潜在影响的一些改动。
IDE(集成开发环境)特性
包名称的自动更新
兼容性问题
想要了解更多其他信息,请参考”Delphi6新特性”这一部分的内容。
兼容性问题
以下列出的是可能影响你的Delphi应用程序的几个大的方面:
一.由于VCL体系结构的变化而引起的Provider和Client Dataset 的一些事件的变动
TCustomClientDataSet的引入,使得Delphi 5或早期版本中的事件处理机制有些改动。
DBCLIENT.PAS单元中有5种类型的6个事件发生了变化,他们分别是:
事件类型 变化
TResolverErrorEvent 影响到provider的 OnUpDateError 事件。
TBeforeUpdateRecordEvent 影响到provider的 BeforeUpdateRecord事件。
TAfterUpdateRecordEvent 影响到provider的 AfterUpdateRecord事件。
TProviderDataEvent 影响到provider的 OnGetData and OnUpdateData事件。
TReconcileErrorEvent 影响到client dataset的 OnReconcileError 事件。
必须将以上提到的这些事件对应的处理函数(过程)中的TClientDataSet替换为TCustomClientDataSet。
二.使用默认数据库登录的代码的变动
原先,将一个连接组件(比如 Tdatabase,TADOConnection,或TDCOMConnection)的LoginPrompt属性设置为True,则会有默认的登录对话框弹出。这一特性已经不再保留除非你在单元引用中增加一个引用DBLogDlg的语句。如果想要应用程序仍然能够弹出默认的登录对话框,务必加上 Use DBLogDlg这么一句,否则便不会有任何提示输入用户名和密码的对话框出现。
三.潜在的二进制Form文件的不兼容
过去,新版本Delphi创建的二进制Form文件(或称DFM文件)可以被老版本的Delphi读取。但是现在不行了。某些二进制Form文件可能不能被老版本正确的读取,其原因是Delphi 6内部的字符串的流化和原先不同。过去,流化操作假设一个本地特殊的字符集。而现在新的流化操作假设字符集为UTF-8。由此带来的问题就是,如果Delphi 6的二进制Form文件中包含有码值大于127的字符出现(比如版权符®),则该文件就不能被Delphi 的老版本正确读取。
如果你想在老版本的Delphi 中打开Delphi 6 的Form文件,那么请先将该Form文件存为文本格式而非二进制格式。
四.有关可赋值的常量
编译宏$WRITEABLECONST现在的缺省值为关(OFF),这是为了防止在Delphi的工程中运用可赋值的常量。可赋值的常量,也就是定义一个常量,但是却允许在运行期间改变其值。例子如下:
const
foo: Integer = 12;
begin
foo := 14;
end.
在以往的Delphi版本中,有这么一个特性:常量不是真正的常量。使用编译宏$WRITEABLECONST OFF,则以上的代码中Foo的赋值将引发一个编译错误。若要避免这个错误,可将Foo的声明改为 var。
将常量用作一个可以初始化的局部变量,这样的代码如下:
procedure MyProc;
const
somedata: Integer = 12;
begin
Inc(somedata, 3);
end;
现在你要做的是将局部常量的声明移到过程的外部,使其成为一个全局的变量。然后代码变为:
var
somedata: Integer = 12;
procedure MyProc;
begin
Inc(somedata, 3);
end;
对于过度依赖于常量的代码(比如ActiveX 控件的包装器),可以通过在源文件中插入一个{$WRITEABLECONST ON}的编译命令来修正。这一特性,在RTL, VCL, CLX,和 DB 等核心的源代码中被禁止使用,但是在周边的单元比如ActiveX 控件的包装器中倒可以接受。
总而言之,你应该意识到“可赋值的常量”这个说法的自相矛盾性。Delphi的以往版本中的这一特性,只是为了与老的16位的编译器的兼容而保留,但现在对于Delphi的开发者来说这已经毫无意义了。另外,要养成好的编程习惯,应当尽量避免使用“可赋值的常量”。
五.Cardinal类型的负数值
过去,Delphi处理Cardinal类型的负数值时使用32位的机制,这样使得结果为一些零头的值(Cardinal类型允许的最大的值与当前值的差加一)。例子如下:
var
c: Cardinal;
i: Int64;
begin
c := 4294967294;
i := -c;
WriteLn(i);
end;
在以往版本的Delphi中,I的值应当是2。但是现在就不是这样的了。在Delphi 6中,Cardinal类型是先转化为64位的有符号类型,然后做取负数值的运算,所以最终结果I的值为-4294967294。
可能有些代码依赖于原先错误的Cardinal负数值的实现方法,所以希望读者对于Delphi 的这一新特性引起足够的重视。花足够多的时间来检验你的代码中是否存在对Cardianl类型的值取负数的情况是很值得的,同时确信一点,Delphi的这个新的特性对你的程序的正确性不构成影响。
六.单元DsgnIntf改名及相关变化
程序中对于DsgnIntf的引用,需要改为对一个新的单元的引用:DesignIntf。可能还得加上DesignEditors、Editors 和RTLConsts 几个单元到你的引用列表中。除此之外你还得将designide加入到你的包的Requires的列表中。另外,对dsnide50的引用可能得手动改为DesignIde,如果Delphi没有自动更改的话。
任何引用了IDesigner的运行期包,需要改为IDesignerHook以防止运行期时对于designide单元的引用要求。在运行期代码中,IDesignerHook 功能足够使用,无需担心。设计期时可以使用IDesigner,如以下代码一样:
var
RealDesigner: IDesigner;
...
SomeDesignerHook.QueryInterface(IDesigner,RealDesigner);
...
来获得IDesignerHook 提供的IDesigner的接口。IDesinerHook的使用只需要引用Classes和Forms两个单元。但是IDesigner还得加上DesignIntf单元,由于该单元被包含在许多其它包中,而其中的一些包可能是不能二次分发的。
七.有关组件编辑器的变化
Delphi 6中,TComponentEditor类有了不同的祖先。在Delphi 5中,它从TInterfacedObject继承而来;现在它从一个新的类,TBaseComponentEditor继承而来。同时,TComponentEditorClass也变为TbaseComponentEditor的类类型,而不是TComponentEditor的类类型。这些体系结构上的变化可能需要你修改老的Delphi程序。
八.TDesignWindow 的变化
许多变化都和类TDesignWindow有关。它的声明被移到单元DesignWindows中,并且FromClosed方法被替换为DesignerClosed。以往,在FromClosed事件中可以通过访问参数Aform来访问Form。而在新的事件DesignerClosed中,我们需要通过Designer的Root属性来访问Form。
在FormClosed事件中,我们可以通过调用TDesignerSelectionList.Create 或者 TComponentList.Create来创建选择列表。而在DesignerClosed事件中做同样操作得使用IDesignerSelections接口。你可以调用CreateSelectionList函数来获得一个接口。
SelectionClosed方法的参数也和Delphi 5版本中的有所不同。
九.VCL 包的变化
一些VCL相关的包已经被重新分配到其他的包里。假如你在工程中引用了vcl50.dcp,那么你需要将这个引用改为引用vcl.dcp和rtl.dcp。
十.OpenGL 接口单元改到rtl.dcp中
Borland OpenGL接口单元(opengl.dcu)在Delphi 5的库单元目录中是一个独立的单元。在Delphi 6中它被合并到rtl.dcp中。这可能导致某些Delphi 5的工程升级到Delphi 6时引发一些问题。
举个例子来说。在Delphi 5的工程中,可能你会将与OpenGL单元同名的单元放置在工程目录中,以覆盖系统提供的OpenGL单元。而在Delphi 6中,假如有任何组件引用了rtl.dcp,则将导致命名冲突,非得更改名称才行。
十一.HTTPApp.pas 单元中的一些类型声明移动到 HTTPProd.pas单元中
HTTPApp 单元中的一部分类型被移动到了HTTPProd 单元中。他们是THTMLBgColor,THTMLAlign 和THTMLVAlign。如果你的工程中使用了这些类,那么需要将引用的单元由HTTPProd改为 HTTPApp 。
十二.Search 单元被删除,SearchBuf例程做了修改并被移动
单元Search在Delphi 6中不再保留。SearchBuf例程,用来在一个文本的缓冲区中定位子串,已经被移动到StrUtils单元中,并且调用的参数也发生了变化。最后一个参数改为一个TstringSearchOptions的对象。如果你的工程因为不能找到Search单元而无法编译,请将引用Search改为引用StrUtils单元。同时你应当检查对SearchBuf的调用是否符合新的语法。