2014-06-10 5 views
11

Próbuję obejść znane ograniczenie wydajności brzydkiego w System.Classes.pas, który posiada 1980 era granicę stały bufor ($ F000), który wygląda tak:Czy mogę zmodyfikować stałą w klasie RTL System.Classes.TStream i odbudować ją w środowisku wykonawczym w Delphi XE6?

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

Powoduje to znaczne pogorszenie się wyników w naszej aplikacji Delphi. W Delphi XE2 przez XE5, byliśmy w stanie zmienić to i użyj jednej z następujących metod:

  • mogę zmodyfikować źródła Delphi, a następnie, powołując dcc32.exe z pliku wsadowego, przebudować system .Classes.dcu w folderze biblioteki Delphi. Rozumiem, że to jest brzydkie i nie podobało mi się to, ale nie podoba mi się ten brzydki problem z wydajnością w RTL, a nasi użytkownicy nie mogą żyć z powodu problemów związanych z wydajnością, jakie powoduje.

  • Mogłabym spróbować umieścić zmodyfikowany plik system.classes.pas gdzieś w mojej ścieżce wyszukiwania projektu.

Żadne z powyższych podejść nie działa dla mnie w Delphi XE6, teraz, prawdopodobnie dzięki pewnym wewnętrznym zmianom kompilatora. Błąd pojawia się w minimalnej aplikacji wiersza poleceń, które obejmuje System.Contnrs w klauzuli zastosowań, jest to:

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

Przykładowy program do odtworzenia tego problemu (zakładając, że zostały zmodyfikowane System.Classes.pas i zmienił MaxBufSize stała), pokazany jest tu:

program consoletestproject; 

{$APPTYPE CONSOLE} 

{$R *.res} 

uses 
    System.Contnrs, 
    System.SysUtils; 

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

Znowu ten problem łatwo rozmnaża się w Delphi XE6, ale nie jest to problem w XE5 lub wcześniej.

Jaka jest zalecana praktyka, gdy absolutnie MUSZĘ obchodzić podstawowe ograniczenia RTL lub VCL przy użyciu zmodyfikowanej kopii System.Classes.pas lub System.SysUtils.pas lub innej jednostki o bardzo niskim poziomie? (Tak, wiem, że NIE powinieneś tego robić, jeśli nie musisz, nie przejmuj się wykładem.)

Czy istnieje magiczny zestaw parametrów wiersza poleceń, których można użyć poprzez "dcc32.exe" na wierszu poleceń, aby utworzyć zmodyfikowaną jednostkę DCU, która będzie poprawnie łączyła się z powyższym przykładem aplikacji?

Jako drugie pytanie, czy istnieją pliki .dcu, dla których nie istnieje żadne źródło, które zostanie przerwane, gdy ktoś spróbuje to zrobić, w takim przypadku odpowiedź na wszystkie powyższe pytania brzmi: "nie można tego naprawić i jeśli jest błąd w RTL, nie masz szczęścia "?

Jednym z możliwych obejść jest włączenie "$ (BDS) \ source \ rtl \ common" w ścieżce wyszukiwania projektu (lub ścieżki biblioteki), zmuszając każdego uszkodzonego (wymagającego rekompilacji) DCU do odbudowy KAŻDEGO czasu, ale wydaje się to brzydkie i źle.

+0

Można uniknąć tego rodzaju problemów łatanie metodę na pamięć korzystając z objazdu. – RRUZ

+0

Jakieś próbki lub przykłady tego podejścia? Mogę użyć haka kodu i zastąpić całą metodę? –

+0

Tak, zastąpić całą metodę. Wysłałem tutaj kod, żeby zrobić to wiele razy. Więc miejcie innych. Istnieją biblioteki, ale jest to przesada, aby uzyskać pełną wymianę. –

Odpowiedz

10

można pokonać to ograniczenie stosując objazd, spróbuj tego próbkę, która używa Delphi Detours Library

najpierw zdefiniować podpis metody hak

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

następnie wdrożyć obejście

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; 

Wreszcie zamień oryginalną funkcję na trampolinę (możesz użyć tego kodu w części inicjalizacyjnej jakiejś jednostki)

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

I aby zwolnić zaczep można użyć

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

Działa to fantastycznie. Może być również używany z MadExcept lub innymi bibliotekami objazdów, zgodnie z wymaganiami użytkowników. W przypadku MadExcept na przykład kod to: 'if madCodeHook.HookCode (@ TStream.CopyFrom, @Detour_TStreamCopyFrom, @Trampoline_TStreamCopyFrom, 0), następnie HookActive: = true; koniec; 'i' MadCodeHook.UnhookCode (@ Trampoline_TStreamCopyFrom); ' –

+1

Możesz napisać podstawową bibliotekę pożerającą w 20 liniach kodu. Tak długo, jak nie potrzebujesz trampolin. Nie wziąłbym zależności od zewnętrznej biblioteki tylko po to. Chociaż bardzo cenię tę bibliotekę, jeśli potrzebna jest trampolina. –

+0

Już używamy MadExcept, ponieważ musimy wykonać wiele haków niskiego poziomu. Twoja podstawowa biblioteka pożerająca byłaby świetnym pytaniem, powinienem ją poprosić, jeśli masz gotową odpowiedź. –

6

Aktualizacja 1: Poniższa sugestia nie działa dla jednostki Classes w XE6. Podstawową techniką jest dźwięk i rozwiązuje podobne problemy. Ale w przypadku XE6, przynajmniej jednostki Classes, nie jest od razu oczywiste, jak ją ponownie skompilować.

Wydaje się to być błąd wprowadzony w XE6 ponieważ technika ta jest przeznaczona do pracy i jest oficjalnie zatwierdzony przez Embarcadero: http://blog.marcocantu.com/blog/2014_august_buffer_overflow_bitmap.html

Aktualizacja 2:

W XE7, ten problem już nie istnieje. Wygląda na to, że wszystko, co zostało złamane w XE6 zostało naprawione.


Potrzebujesz opcji kompilatora, aby dopasować te używane podczas kompilacji jednostki przez Embarcadero. Z tego powodu twoja sekcja implementacji zmienia się tylko wtedy, gdy wydaje się, że powinna odnieść sukces.

Zacznij domyślny projekt i używać CTRL + O + O wygenerować te opcje. Otrzymuję

{$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} 

kiedy robię to w XE6.

Połóż to na górze swojej kopii urządzenia i powinieneś być gotowy do pracy. Prawdopodobnie możesz uciec z ich podziałem, w zależności od opcji projektu hosta. W moim kodzie znajduję:

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

wystarcza.

+0

Jest możliwe, że w XE6 biblioteka nie jest kompilowana z tymi samymi wartościami domyślnymi, co nowy projekt. Próbuję dowiedzieć się, co jest nie tak w XE6, ale jeszcze go nie zidentyfikowałem. Oznacza to, że ciąg znaków wygenerowany w XE6 w nowym projekcie, gdy wykonuję Control + O + O, nie rozwiązuje problemu. –

+0

To jest wiarygodne. Zajęcia są dość wyjątkowe. Takie podejście zawsze działało dla mnie w przeszłości, ale być może XE6 jest inny. –

+0

Takie podejście działa w XE2, XE3, XE4 i XE5 (po prostu przetestowane z System.classes.pas) i wydaje się NIE działać w przypadku System.Classes.pas w XE6, być może z powodu jakiejś bardzo niskiego poziomu magii kompilatora. –

Powiązane problemy