我可以在RTL类System.Classes.TStream中修改一个常量,并在Delphi XE6的运行时重建它吗? [英] Can I modify a constant in the RTL class System.Classes.TStream and rebuild it at runtime in Delphi XE6?

查看:436
本文介绍了我可以在RTL类System.Classes.TStream中修改一个常量,并在Delphi XE6的运行时重建它吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在努力解决System.Classes.pas中一个已知的丑陋的性能限制,它具有20世纪80年代的恒定缓冲区限制($ F000),如下所示:

  function TStream.CopyFrom(const Source:TStream; Count:Int64):Int64; 
const
MaxBufSize = $ F000;
....

这在我们的Delphi应用程序中造成了严重的性能损失。在delphi XE2到XE5中,我们可以修改它,并使用以下方法之一:




  • 我可以修改Delphi源,然后通过从批处理文件调用dcc32.exe,在Delphi库文件夹中重建System.Classes.dcu文件。我意识到这是丑陋的,我不喜欢这样做,但是我也不喜欢RTL中的这个丑陋的性能问题,我们的用户不能忍受性能上的困扰。


  • 我可以尝试将一个修改后的system.classes.pas文件放在我的项目搜索路径中。




以上两种方法都不适用于Delphi XE6,现在,感谢大家有些内部编译器的改动。在使用子句中包含System.Contnrs的最小命令行应用程序中出现的错误是:

  [dcc32致命错误] System.Classes.pas(19600):F2051 Unit System.Contnrs编译了不同版本的System.Classes.TComponent 

重现此问题的示例程序(假设您已修改System.Classes.pas并更改了MaxBufSize常量),如下所示:

 程序consoletestproject; 

{$ APPTYPE CONSOLE}

{$ R * .res}

使用
System.Contnrs,
系统.SysUtils;

var
列表:System.Contnrs.TObjectList;
begin
WriteLn('Hello world');
结束。

同样,这个问题在Delphi XE6中很容易重现,但在XE5或更早版本中不是问题。



当您绝对必须使用System.Classes.pas或System.SysUtils.pas或某些版本的修改版本解决基本的RTL或VCL限制时,推荐的做法是什么其他非常低级别的单位? (是的,我知道你不应该这样做,如果你不需要,不要打扰讲座。)



是否有一套神奇的命令行参数可以通过命令行中的dcc32.exe使用,以产生一个修改后的DCU,它将与上面的应用示例正确连接。



作为次要问题,有没有源代码存在的.dcu文件,当有人尝试这样做时会有破坏的情况,在这种情况下,所有上述的答案是你不能解决这个问题,如果RTL中有一个错误,你运气不好?



一个可能的解决方法是在项目搜索路径中包含$(BDS)\source\rtl\common图书馆路径),迫使每个破碎(需要重新编译)DCU重建每个时间,但这似乎是丑陋和错误的。

解决方案

你可以使用绕行来克服这个限制,尝试使用 Delphi Detours Library



首先定义钩子的签名>

  var 
Trampoline_TStreamCopyFrom:function(Self:TStream; const Source:TStream;计数:Int64):Int64 = nil;

然后实现绕行

  function Detour_TStreamCopyFrom(Self:TStream; const Source:TStream; Count:Int64):Int64; 
const
MaxBufSize = 1024 * 1024; // now 1 mb now :)
var
BufSize,N:Integer;
缓冲区:TBytes;
begin
如果Count <= 0 then
begin
Source.Position:= 0;
计数:= Source.Size;
结束
结果:= Count;
如果Count> MaxBufSize then BufSize:= MaxBufSize else BufSize:= Count;
SetLength(Buffer,BufSize);
尝试
,而Count<> 0 do
begin
如果Count> BufSize then N:= BufSize else N:= Count;
Source.ReadBuffer(Buffer,N);
Self.WriteBuffer(Buffer,N);
Dec(Count,N);
结束
finally
SetLength(Buffer,0);
结束
结束

最后用蹦床替换原来的功能(您可以在某些单元的初始化部分使用此代码)

  Trampoline_TStreamCopyFrom:= InterceptCreate(@ TStream.CopyFrom,@Detour_TStreamCopyFrom); 

要释放挂钩,您可以使用

  if Assigned(Trampoline_TStreamCopyFrom)then 
InterceptRemove(@Trampoline_TStreamCopyFrom);


I am trying to work around a known ugly performance limitation in System.Classes.pas, which has a 1980s era constant buffer limit ($F000) that looks like this:

function TStream.CopyFrom(const Source: TStream; Count: Int64): Int64;
const
  MaxBufSize = $F000;
....

This is causing major performance penalties in our Delphi application. In delphi XE2 through XE5, we were able to modify this and use one of the following approaches:

  • I could modify the Delphi sources, and then, by invoking dcc32.exe from a batch file, rebuild the System.Classes.dcu file in the Delphi library folder. I realize this is ugly and I didn't like doing this, but I don't like this ugly performance issue in the RTL either, and our users can not live with the performance headaches it causes.

  • I could try to put a modified system.classes.pas file somewhere in my project search path.

Neither of the above approaches is working for me in Delphi XE6, now, thanks probably to some internal compiler changes. The error I get in a minimal command line application that includes System.Contnrs in its uses clause, is this:

[dcc32 Fatal Error] System.Classes.pas(19600): F2051 Unit System.Contnrs was compiled with a different version of System.Classes.TComponent

The sample program to reproduce this problem (assuming you have modified System.Classes.pas and changed the MaxBufSize constant), is shown here:

program consoletestproject;

{$APPTYPE CONSOLE}

{$R *.res}

uses
   System.Contnrs,
   System.SysUtils;

var
  List:System.Contnrs.TObjectList;
begin
   WriteLn('Hello world');
end.

Again, this problem reproduces easily in Delphi XE6, but is not a problem in XE5, or earlier.

What is the recommended practice when you absolutely MUST work around a fundamental RTL or VCL limitation using a modified copy of System.Classes.pas or System.SysUtils.pas or some other very low level unit? (Yes, I know you should NOT do this if you don't have to, don't bother with a lecture.)

Are there a magic set of command line parameters you can use via "dcc32.exe" on the command line, to produce a modified DCU that will link properly with the application example above?

As a secondary question, are there .dcu files for which no source exists that will break when one tries to do this, in which case the answer to all of the above is, "you can't fix this, and if there's a bug in the RTL, you're out of luck"?

One possible workaround is to include "$(BDS)\source\rtl\common" in your project search path (or library path), forcing each broken (needing recompile) DCU to rebuild EACH time, but this seems ugly and wrong.

解决方案

You can overcome this limitation using a detour, try this sample which uses the Delphi Detours Library

First define the signature of the method to hook

var
 Trampoline_TStreamCopyFrom : function (Self : TStream;const Source: TStream; Count: Int64): Int64 = nil;

then implement the detour

function Detour_TStreamCopyFrom(Self : TStream;const Source: TStream; Count: Int64): Int64;
const
  MaxBufSize = 1024*1024; //use 1 mb now :)
var
  BufSize, N: Integer;
  Buffer: TBytes;
begin
  if Count <= 0 then
  begin
    Source.Position := 0;
    Count := Source.Size;
  end;
  Result := Count;
  if Count > MaxBufSize then BufSize := MaxBufSize else BufSize := Count;
  SetLength(Buffer, BufSize);
  try
    while Count <> 0 do
    begin
      if Count > BufSize then N := BufSize else N := Count;
      Source.ReadBuffer(Buffer, N);
      Self.WriteBuffer(Buffer, N);
      Dec(Count, N);
    end;
  finally
    SetLength(Buffer, 0);
  end;
end;

Finally replace the original function by the trampoline (you can use this code in the initialization part of some unit)

  Trampoline_TStreamCopyFrom     := InterceptCreate(@TStream.CopyFrom,   @Detour_TStreamCopyFrom);

And to release the hook you can use

 if Assigned(Trampoline_TStreamCopyFrom) then
   InterceptRemove(@Trampoline_TStreamCopyFrom);

这篇关于我可以在RTL类System.Classes.TStream中修改一个常量,并在Delphi XE6的运行时重建它吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆