Can I modify a constant in the RTL class System.Classes.TStream and rebuild it at runtime in Delphi XE6?

前端 未结 2 1589
抹茶落季
抹茶落季 2020-12-10 04:14

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:



        
相关标签:
2条回答
  • 2020-12-10 04:32

    Update 1: The suggestion below does not work for the Classes unit in XE6. The basic technique is sound and does solve similar problems. But for XE6, at least the Classes unit, it is not immediately obvious how to re-compile it.

    This appears to be a fault introduced in XE6 because this technique is meant to work and is officially endorsed by Embarcadero: http://blog.marcocantu.com/blog/2014_august_buffer_overflow_bitmap.html

    Update 2:

    In XE7, this problem no longer exists. It would appear that whatever was broken in XE6 has been fixed.


    You need the compiler options to match those used when the unit was compiled by Embarcadero. That's the reason why your implementation section only change fails when it seems like it ought to succeed.

    Start a default project and use CTRL + O + O to generate these options. I get

    {$A8,B-,C+,D+,E-,F-,G+,H+,I+,J-,K-,L+,M-,N-,O+,P+,Q-,R-,S-,T-,U-,V+,W-,X+,Y+,Z1}
    

    when I do this in XE6.

    Put that at the top of your copy of the unit and you should be good to go. You can probably get away with a cut-down subset of these, depending on your host project options. In my code I find that:

    {$R-,T-,H+,X+}
    

    suffices.

    0 讨论(0)
  • 2020-12-10 04:35

    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);
    
    0 讨论(0)
提交回复
热议问题