AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

RichEdit - Bug oder Fehler meinerseits?

Ein Thema von TStringlist · begonnen am 12. Jul 2004 · letzter Beitrag vom 1. Aug 2004
Antwort Antwort
TStringlist

Registriert seit: 1. Dez 2003
360 Beiträge
 
Turbo Delphi für Win32
 
#1

RichEdit - Bug oder Fehler meinerseits?

  Alt 12. Jul 2004, 00:27


Es ist einfach zum verrückt werden! Das muss doch ein Bug sein, oder?

Zuerst mal die Situation: Nach einer jeden Änderung eines aktuellen RichEdit1-Textes save ich diesen auch komplett in einen TMemoryStream ab. Hierfür nehme ich einen API-Thread, der dazu jeweils einmal im OnIdle-Eventhandler mittels 'ResumeThread' 'angestoßen' wird, ...der dann mit Hilfe von EM_STREAMOUT und einer Callback-Funktion die eigentliche Auslesearbeit ausführt ...und der sich danach per 'SuspendThread' selbst wieder in den Ruhezustand versetzt. Um dass ein gleichzeitiger Char-Input währenddessen aber auch weiter flüssig durchführbar bleibt, muss dann diese Callback-Funktion auch noch break-bar sein (...denn für das Auslesen von großen Byte-Mengen (5000000-6000000) benötigt ein 1.2 GHz AMD-Prozessor immerhin noch knapp 0,4 Sekunden, sofern auch der Stream vorab schon entsprechend groß vorlag, sonst übrigens >16 SEK.!!!). Kommt dann also währenddessen ein solcher neuer Char-Input im RichEdit1.OnKeyDown-Eventhandler an, dann wird dort ein Flag zum Abbruch dieses oben erwähnten StreamOut-Prozesses gesetzt und daraufhin in einer while-Anweisung die Abbruchsbestätigung dafür abgewartet. Danach kann der neue Key normal weiterverarbeitet werden und alles quasi wieder von neuem losgehen.

Problem: Solange ich nun das Ganze mit einigermaßen kleinen Texten mache funktioniert das auch ohne Probleme. Bei größeren jedoch (eben >= 5-6 Mega-Bytes) kommt es dann aber leider nach einiger Zeit regelmäßig zu einem Absturz. Reduplizieren lässt sich dieser Absturz leicht per automatischer Key-Wiederholung mittels der jeweils 'am Stück' 90-100 Chars ge'insert'et und dann anschließen ebenfalls wieder per automatischer BackSpace-Wiederholung delet'et werden. Nach ein paar Wiederholungen des Ganzen tritt dann leider immer der Absturz ein. (Bei meinen letzten 10 Versuchen jeweils nach (1694,597,914,43,2245,1291,2456,9,185,16 =) 945 Char-In/Out-Acts im Schnitt).

Große Frage also: Woran könnte das bloß liegen? Am Code vielleicht? Eventuell könnte der eine oder andere das ganze Prog. ja auch mal schnell kurz kopieren (sind ja nur ~150 Zeilen) und bei sich schnell selbst mal austesten ..vielleicht liegst ja irgendwie an meinem System , *g*. Meine Vermutung: Irgend etwas 'hinter' der Callback-Funktion stimmt nicht, zumal diese ja auch bei den großen Texten erst nach einer jeweils zweimaligen Wiederholung der Breakaufforderung wirklich breakt ...bei kleinen Texten breakt sie sofort. Alleine diese Unregelmäßigkeit zeigt schon sehr in diese Richtung...

Zum Code: Der Einfachheit halber habe ich schonmal alles nicht unbedingt nötige aus dem Code rausgeschmissen. Übrig geblieben ist nur so eine Art Skelett, was aber eben leider auch immer noch zum Absturz ausreicht. Außerdem habe ich auch die wenigen Vor- & Nachbereitungsarbeiten (+ der Texterzeugung) jetzt mal bequemerweise schnell in einer SpeedButton1Click-Proc bzw. im Initialization/Finalization-Part zusammengefasst. Den SpeedButton2 benutze ich zur Erzeugung eines künstlichen Breaks der Callback-Funktion, um diesen auch bei kleinen/großen Texten mal vergleichend ausdebuggen zu können:


Delphi-Quellcode:
unit Unit1;

interface

uses
  Windows, RichEdit, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls, Buttons;

type
  TForm1 = class(TForm)
    RichEdit1: TRichEdit;
    SpeedButton1: TSpeedButton;
    SpeedButton2: TSpeedButton;
    Label1: TLabel;
    procedure SpeedButton1Click(Sender: TObject);
    procedure RichEdit1KeyDown(Sender: TObject; var Key: Word;
      Shift: TShiftState);
    procedure SpeedButton2Click(Sender: TObject);
  private
    procedure ApplicationIdleEvent(Sender: TObject; var Done: Boolean);
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;
  ThreadHandle,ThreadID : Cardinal;
  ThreadActive : integer;
  ThreadBeenden : integer;
  AEditStreamStruc : TEditStream;
  AMemoryStream : TMemoryStream;
  FixFlag : Boolean;
  BreakFlag : integer; // zum Breaken der Callback-Funktion von außen
  ChrCounter : integer;

implementation

{$R *.dfm}


function EditStreamOutCallbackFunc(dwCookie: Longint; pbBuff: PByte;
         cb: Longint; var pcb: Longint): DWORD; stdcall;
begin
  if BreakFlag = 1 then begin
    // wenn also 'draußen' das Break-Flag auf 1 (~true) gesetzt wurde:
    Result := $ffffffff; // heißt: Abbruch
    exit;
  end;

  Result := 0; // heißt: auch danach ggf. weiter auslesen

  try
    pcb := AMemoryStream.Write(pbBuff^,cb);
  except
    Result := 1; // heißt: Abbruch
  end;

  if (pcb <> cb) then Result := 1; // heißt: Abbruch
end;

// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

procedure AThread;
begin
  repeat

    AMemoryStream.Position := 0;
    with AEditStreamStruc do begin
      dwCookie := 0;
      dwError := 0;
      pfnCallback := @EditStreamOutCallbackFunc;
    end;
    form1.RichEdit1.Perform(EM_STREAMOUT, SF_RTF, Longint(@AEditStreamStruc));

    InterlockedExchange(ThreadActive,0);
    SuspendThread(ThreadHandle);

  until ThreadBeenden = 1;
end;

//------------------------------------------------------------------------------

procedure TForm1.ApplicationIdleEvent(Sender: TObject; var Done: Boolean);
begin
  if FixFlag then begin
    FixFlag := false;
    InterlockedExchange(BreakFlag,0);

    // Diese und die nächste Zeile existieren nur zur Erzeugung eines künstlichen Breaks,
    if SpeedButton2.Caption = 'Breakthen //...zwecks einfachen debuggens desselbigen
    begin InterlockedExchange(BreakFlag,1); SpeedButton2.Caption := 'no Breakend;

    InterlockedExchange(ThreadActive,1);
    ResumeThread(ThreadHandle);
  end;
end;


procedure TForm1.RichEdit1KeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  InterlockedExchange(BreakFlag,1);
  while ThreadActive = 1 do sleep(1);
  FixFlag := true;

  // die nächsten 2 Zeilen dienen nur der Anzeige wann der Absturz auftritt
  inc(ChrCounter);
  label1.Caption := IntToStr(ChrCounter);
end;

//-------------------------------------------------------------------------------

procedure TForm1.SpeedButton1Click(Sender: TObject);
const AWord = 13 + 10*256;
      InputLen = 6000000;
type ChB = ^Byte; ChW = ^Word;
var AStream : TMemoryStream;
    I : integer;
begin
  SendMessage(RichEdit1.Handle,EM_SETLIMITTEXT,$7FFFFFFE,0);
  AStream := TMemoryStream.Create;
  AStream.SetSize(InputLen);
  for I := 0 to InputLen-1 do ChB(integer(AStream.Memory)+I)^:=65;
  for I := 1 to InputLen div 100 do ChW(integer(AStream.Memory)+I*100-2)^:=AWord;
  RichEdit1.Lines.LoadFromStream(AStream);
  AStream.Free;

  ThreadBeenden := 0;
  ThreadActive := 0;
  ThreadHandle := BeginThread(Nil,0,@AThread,NIL,CREATE_SUSPENDED,ThreadID);
  SetThreadPriority(ThreadHandle,THREAD_PRIORITY_NORMAL);

  Application.OnIdle := ApplicationIdleEvent;
end;

//-------------------------------------------------------------------------------

// Diese Proc erzeugt nur einen künstlichen Break, bzw. eine entsprechende
// Debug-Möglichkeit eines Breaks dieser obigen Callback-Funktion
procedure TForm1.SpeedButton2Click(Sender: TObject);
begin
  SpeedButton2.Caption := 'Break';
end;

initialization
  AMemoryStream := TMemoryStream.Create;
  AMemoryStream.SetSize(7000000); // sollte vorab schon entsprechend groß sein

  FixFlag := false;

finalization
  AMemoryStream.Free;
  
  InterlockedExchange(ThreadBeenden,1);
  ResumeThread(ThreadHandle);
  CloseHandle(ThreadHandle);

end.
DANKE für jeden (guten) Tipp! ...bzw. natürlich auch für eine Bestätigung meiner Bug-Vermutung!
MfG (& Thx ggf.)
  Mit Zitat antworten Zitat
Benutzerbild von yankee
yankee

Registriert seit: 10. Mär 2004
1.134 Beiträge
 
Lazarus
 
#2

Re: RichEdit - Bug oder Fehler meinerseits?

  Alt 12. Jul 2004, 06:45
sry, ich kann dir keinen direkten Tipp geben, aber ändre doch mal den BBCode um den Quelltet von source in delphi, damit man auch das Syntaxhighlighting drin hat. Ist eben was übersichlicher...
Bei einem so langen Quelltext ist es vielleicht nicht ganz falsch, den als Datei ins Attachment zu stellen...
Letzter Tipp: Drogen. Machen zwar nicht glücklich, geben einem aber wenigstens das Gefühl glücklich zu sein.

Have a lot of fun!
  Mit Zitat antworten Zitat
TStringlist

Registriert seit: 1. Dez 2003
360 Beiträge
 
Turbo Delphi für Win32
 
#3

Re: RichEdit - Bug oder Fehler meinerseits?

  Alt 12. Jul 2004, 10:12
Hallo.

Tatsächlich, sieht wesentlich besser aus, ...hatte sich mir bisher noch gar nicht so richtig aufgedrängt.

Na jedenfalls, Danke auch für diesen Tipp.
MfG (& Thx ggf.)
  Mit Zitat antworten Zitat
TStringlist

Registriert seit: 1. Dez 2003
360 Beiträge
 
Turbo Delphi für Win32
 
#4

Re: RichEdit - Bug oder Fehler meinerseits?

  Alt 1. Aug 2004, 21:12
...ach stimmt ja, dieser Thread war ja auch noch irgendwie offen geblieben.

Die Lösung habe ich übrigens schon etwas länger. Nun ja "Lösung" - jedenfalls die Art wie man den Code hier auch noch schreiben kann und wie die Sache danach ohne weitere Probleme läuft.

Notwendig war dafür eigentlich nur, auf eine jeweils immer wieder neue Erzeugung des API-Threads (mit einer dann auch baldigen Terminierung desselbigen) umzusteigen, anstatt wie vorher ihn nur ein einziges Mal am Programmstart zu erzeugen und ihn dann mittels ThreadResume & ThreadSuspend immer wieder nur jeweils an- und auszuschalten. Jedenfalls schien der API-Thread bei dieser (seiner) Aufgabe im Laufe der Zeit irgendwie etwas zu korrumpieren und dann dem Primärthread in die Quere zu kommen. Nun aber, durch einen jeweils immer wieder ganz neuen Auf- und Abbau dieses API-Threads, können sich jedenfalls keine event. kleinen Fehler (wie auch immer) im Laufe der Zeit zu größeren aufaddieren ...der API-Thread wird so quasi immer wieder neu initialisiert und steht darüber hinaus dem Primärthread bei dessen Aktionen (z.B. am gleichen Control) dann auch noch nicht mal mehr theoretischst irgendwie im Wege. Summa summarisch sieht das letztlich also auch von daher schon etwas sicherer aus. Bei kritischeren (bzw. 'tiefer grabenden') Arbeiten des Threads eventuell ein guter Ratschlag.


..nur für den Fall, dass mal einer bei einer Suche hier vorbeikommt.
MfG (& Thx ggf.)
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 06:01 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz