Entwickler-Ecke

Grafische Benutzeroberflächen (VCL & FireMonkey) - An Caretposition in TJvRichEdit rtf-Text einfügen


galagher - Mo 25.03.19 07:33
Titel: An Caretposition in TJvRichEdit rtf-Text einfügen
Hallo!

Folgender Code fügt an der Caret-Position in einem TJvRichEdit beliebigen, rtf-formatierten Text ein. Müsste auch für TRichEdit geeignet sein, ich habe es dzt. für TJvRichEdit. Egal. Es geht um folgendes:

Der Code funktioniert mit 32Bit-Kompiliereung einwandfrei, aber nicht bei 64Bit, da wird die eingebaute Fehlermeldung ausgegeben, weil rtfStream.dwError <> $0000.

Wie muss ich das umbauen, dass es auch bei 64Bit-Kompilierung funktioniert?


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
{rtf-Daten an ein JvRichEdit an der Caretposition einfügen, ohne den bisherigen}
{Inhalt zu überschreiben. Wenn srcJvRichEdit und dstJvRichEdit identisch sind,}
{wird an das Quell-JvRichEdit an- bzw. eingefügt.}
{http://delphidabbler.com/tips/57}
procedure InsertRtfJvRichEdit(const srcJvRichEdit, dstJvRichEdit: TJvRichEdit);
var
  rtfStream: TEditStream;
  sourceStream : TMemoryStream;

  function EditStreamReader(
    dwCookie: DWORD;
    pBuff: Pointer;
    cb: LongInt;
    pcb: PLongInt): DWORD; stdcall;
  begin
    result := $0000;
    try
      pcb^ := TStream(dwCookie).Read(pBuff^, cb) ;
    except
      result := $FFFF;
    end;
  end{EditStreamReader}

begin  {InsertRtfJvRichEdit}
  dstJvRichEdit.Lines.BeginUpdate;
  sourceStream := TMemoryStream.Create;
  try
    srcJvRichEdit.Lines.SaveToStream(sourceStream) ;

    sourceStream.Position := 0;

    (*
    {Ausreichende Länge sicherstellen!}
    dstJvRichEdit.MaxLength :=
      dstJvRichEdit.MaxLength + sourceStream.Size + Length(dstJvRichEdit.Text);
    *)

    {Besser:}
    {Ausreichende Länge sicherstellen!}
    dstJvRichEdit.MaxLength := 0;

    rtfStream.dwCookie := DWORD(sourceStream) ;
    rtfStream.dwError := $0000;
    rtfStream.pfnCallback := @EditStreamReader;

    dstJvRichEdit.Perform(
      EM_STREAMIN,
      SFF_SELECTION or SF_RTF or SFF_PLAINRTF, LPARAM(@rtfStream));

    if rtfStream.dwError <> $0000 then
    begin
      Application.MessageBox(
             'Fehler beim Einfügen der Daten.',
             'Fehler',
             mb_OK or mb_ICONSTOP or mb_DEFBUTTON1);
    end;

    (* raise Exception.Create('Fehler beim Einfügen der Daten.') ; *)
  finally
    sourceStream.Free;
    dstJvRichEdit.Lines.EndUpdate;
  end;
end;


Dies hier ist die Schwachstelle:

Delphi-Quelltext
1:
2:
3:
dstJvRichEdit.Perform(
  EM_STREAMIN,
  SFF_SELECTION or SF_RTF or SFF_PLAINRTF, LPARAM(@rtfStream));

Und hier ist es konkret SF_RTF, was den Fehler verursacht. Lasse ich das weg, passiert gar nichts.


Delete - Mo 25.03.19 09:23

- Nachträglich durch die Entwickler-Ecke gelöscht -


galagher - Mo 25.03.19 09:47

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
der Wert SF_RTF hat darauf keine Auswirkung.
Wenn ich den aber weglasse, kommt der Fehler nicht, allerdings funktioniert dann der Code natürlich auch nicht!

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Die Callback-Methode EditStreamReader() wird wohl $FFFF liefern. Überprüfe das mal.
Ich denke schon, dass das so ist, denn es kommt ja zu dem Fehler und es erscheint dann genau die vordefinierte Fehlermeldung.

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Der Doku nach sollte die Variable pBuff vom Typ PByte anstelle von Pointer sein, so nebenbei.
Ok, werde ich korrigieren.

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Die ganzen Zeiger-Typen werden wahrscheinlich eine andere Größe in einer Win64-Anwendung annehmen, daher gelingt der Stream nicht.
Ich schaue mir das alles heute noch an!


Delete - Mo 25.03.19 09:55

- Nachträglich durch die Entwickler-Ecke gelöscht -


galagher - Mo 25.03.19 10:27

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Könntest du ein Beispiel hinzufügen, wie so ein Aufruf bei dir aussieht?
Gerne, kann ich aber ebenfalls erst heute Abend machen!


galagher - Mo 25.03.19 18:36

Ok, hier ist nun, was ich mache:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
var
  s: String;
  tmpJvRichEdit: TJvRichEdit;
begin
  tmpJvRichEdit := TJvRichEdit.Create(Self);
  tmpJvRichEdit.Hide;
  tmpJvRichEdit.Parent := Form1;

  s := //Irgendein rtf-Code

  //SetRichText arbeitet mit LoadFromStram, wodurch der bereits vorhandene Text
  //gelöscht werden würde, deshalb verwenden ich ein temporäres JvRichEdit
  SetRichText(s, tmpJvRichEdit);

  //Nun füge ich den Inhalt von tmpJvRichEdit in JvRichEdit ein
  InsertRtfJvRichEdit(tmpJvRichEdit, JvRichEdit);

  //...
end;


Das ist alles! SetRichText arbeitet einwandfrei, daran liegt es also schon mal nicht.

//Edit:
Ich habe das nun korrigiert: pBuff: PByte;
Aber auch das bringt keinen Erfolg.


Delete - Mo 25.03.19 19:10

- Nachträglich durch die Entwickler-Ecke gelöscht -


galagher - Mo 25.03.19 19:25

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:

Delphi-Quelltext
1:
pcb^ := TStream(dwCookie).Read(pBuff^, cb) ;                    

Hier bekommst du eine Zugriffsverletzung.
Hab' ich vorhin herausgefunden, ja. Das geht ins except.

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Auf Anhieb weiss ich nicht, wie man die Zeile umschreiben könnte.
Werde googlen...


galagher - Mo 25.03.19 19:28

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Als Win64 Plattform muss die Callback-Methode wohl außerhalb der InsertRtfJvRichEdit() Methode definiert werden, dann funktioniert es auch.

Ich habe deinen Nachtrag erst jetzt gelesen - das ist es! Ausserhalb! Darauf muss man erst kommen! :dance2:


Delete - Mo 25.03.19 19:38

- Nachträglich durch die Entwickler-Ecke gelöscht -


galagher - Mo 25.03.19 20:58

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Naja, wenn der Compiler ständig in der Zeile meckert, egal wie TStream initialisiert wird, und es sich um eine Zugriffsverletzung handelt, kam mir nur noch das in den Sinn.

Zuerst dachte ich ja, dass es daran liegt:

Delphi-Quelltext
1:
2:
3:
dstJvRichEdit.Perform(
  EM_STREAMIN,
  SFF_SELECTION or SF_RTF or SFF_PLAINRTF, LPARAM(@rtfStream));

Dann, dass es lediglich an SF_RTF liegt, denn ohne dieses kam die Zugriffsverletzung nicht, da passierte einfach gar nichts.

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Meide verschachtelte Methoden so gut es geht. Es scheint bei Win64 Anwendung da ein Problem zu geben.

Nun, "normale", einfachere Methoden bereiten da kein Problem. Ich bin der Meinung, wenn eine Methode nur von einer einzigen anderen Methode gebraucht und genutzt wird, "lege" ich sie in die aufrufende Methode "hinein", das ist sauber und aufgeräumt.

Ich kann jetzt wieder - mit mühsam zusammengeschriebenem rtf-Code - sogar Tabellen einfügen!