Entwickler-Ecke
Sonstiges (FPC / Lazarus) / Linux API - TRichMemo - Grafik einfügen
galagher - Mo 04.05.20 06:12
Titel: TRichMemo - Grafik einfügen
Hallo!
Weiss jemand, wie ich per Code in ein TRichMemo eine Grafik einfügen kann, ohne den Weg über ClipBoard zu nehmen?
Alternativ: Wie realisiere ich eine "eigene" Zwischenablage, die ich dazu verwenden kann? Der Hintergrund dabei ist, dass ich dabei den Inhalt der systemweiten Zwischenablage nicht überschreiben will.
jaenicke - Mo 04.05.20 07:54
Du musst soweit ich weiß ein TRichMemoInlinePicture erstellen, dem Konstruktor dein Bild mitgeben und das dann mit InDelInline in das TRichMemo einfügen.
galagher - Mo 04.05.20 08:14
jaenicke hat folgendes geschrieben : |
Du musst soweit ich weiß ein TRichMemoInlinePicture erstellen, dem Konstruktor dein Bild mitgeben und das dann mit InDelInline in das TRichMemo einfügen. |
Sagt mir jetzt erstmal gar nichts, aber ich seh's mir an! :mrgreen:
galagher - Mo 04.05.20 15:43
Das fügt Grafiken als eingebettetes Objekte ein, ich möchte aber lediglich eine Grafik so einfügen, wie man sie auch per ClipBoard einfügen kann.
Ich sehe mir jetzt erstmal den Vorschlag von
jaenicke an:
jaenicke hat folgendes geschrieben : |
Du musst soweit ich weiß ein TRichMemoInlinePicture erstellen, dem Konstruktor dein Bild mitgeben und das dann mit InDelInline in das TRichMemo einfügen. |
Am einfachsten wäre es aber per ClipBoard. Kann man denn davon nicht eine eigene Instanz erstellen? - Ja, kann man, aber dabei wird trotzdem der Inhalt der "echten" Zwischenablage ersetzt. Wie kann ich das verhindern?
jaenicke - Mo 04.05.20 16:36
galagher hat folgendes geschrieben : |
Am einfachsten wäre es aber per ClipBoard. |
Nein, der korrekte Weg ist genauso einfach.
galagher hat folgendes geschrieben : |
Kann man denn davon nicht eine eigene Instanz erstellen? - Ja, kann man, aber dabei wird trotzdem der Inhalt der "echten" Zwischenablage ersetzt. Wie kann ich das verhindern? |
Gar nicht, die Zwischenablagenfunktionalität macht ja Windows und nicht die Ansteuerung in der eigenen Anwendung.
Das ist quasi als ob du dir ein eigenes Lenkrad im 3D-Drucker kopierst und dann fragst wie du damit nun ein Auto lenken kannst ohne das echte Auto zu lenken. ;-)
galagher - Mo 04.05.20 17:31
jaenicke hat folgendes geschrieben : |
Nein, der korrekte Weg ist genauso einfach. |
Ist er nicht! In RichMemo.pas gibt es zwar die erfoderlichen Prozeduren, die sind aber leer.
Also habe ich das Ganze zuerst mal in Form1 erstellt und dort funktioniert es auch. Dann habe ich alles in RichMemo.pas kopiert, den Code natürlich angepasst, aber ich bekomme dort, sobald ich an imageList Width und Height zuweise, die Fehlermeldung:
Zitat: |
Projekt project1 hat Exception-Klasse »External: SIGSEGV« ausgelöst. |
Wozu brauche ich denn da überhaupt eine ImageList?
Ich blicke da nicht durch, und das Sample-Projekt bringt mich auch nicht weiter (von dort habe ich ja die Vorgehensweise mit der ImageList)...
//Edit: Wenn ich die ImageList weglasse, wird ein "leerer Bereich" eingefügt, aber kein sichtbares Bitmap.
galagher - Mo 04.05.20 18:38
Es funktioniert dank:
https://forum.lazarus.freepascal.org/index.php?topic=33563.0
Ein einziges Beispiel ist Goldes Wert!
Ich habe den Code angepasst, sodass nun keine Datei mehr geöffnet werden muss, sondern
Picture übergeben wird. Nach dem Einfügen gebe ich piclist frei (aber natürlich nicht dessen Elemente).
Warum richmemo.pas das nicht schon OnBoard hat (genauso wie so maches andere), weiss der Kuckuck... Aber ich arbeite dran!
galagher - Mi 06.05.20 16:51
Die Grafik wird nicht mitgespeichert. Da macht sich also jemand die Arbeit, eine gut funktionierende Funktion zum Einfügen von Grafiken zu schreiben. Aber natürlich kommt der niemals auf die Idee, dass man das Ganze dann auch speichern können will! :mrgreen: :evil: Nein, wie denn auch? Das ist ja abwegig! Wer will schon Text UND Grafiken als Datei speichern? :autsch:
Nein, im Ernst: Das ärgert mich. Eine halbe Sache, an sich gut, aber doch letzlich so, wie sie ist, unbrauchbar. Leider kann ich das nicht fertig schreiben, weil ich keine Idee habe, wie man die Daten der Grafik in den RTF-Text hinein bekommt.
Wenn man sich die rtf-Datei ansieht, findet sich darin der String {\pict{\*\picprop}\wmetafile0 , es fehlen aber die Daten der Grafik. Wie erhalte ich diese als Text? Ich habe zwar eine Funktion, die das zumindest mit Bitmaps erledigt, aber es funktioniert nicht, wenn man die da einfach dran hängt.
Ich habe das vorläufig so gelöst, dass ich den Text oder das Bitmap der Zwischenablage sichere, dann die Grafik über ClipBoard einfüge und danach den ursprünglichen Inhalt der Zwischenablage wiederherstelle. Das klappt, ist aber natürlich unelegant...
Weiss jemand wie ich beliebigen Inhalt der Zwischenablage sichern und wieder herstellen kann? :eyecrazy:
galagher - Mi 06.05.20 18:44
galagher hat folgendes geschrieben : |
Weiss jemand wie ich beliebigen Inhalt der Zwischenablage sichern und wieder herstellen kann? :eyecrazy: |
ich habe das jetzt erstmal so gelöst:
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:
| procedure TCustomRichMemo.InsertGraphic(const aPicture: TPicture); var Stream: TMemoryStream; i, n: Integer; aArray: array of TClipBoardFormat; begin Stream := TMemoryStream.Create; SetLength(aArray, ClipBoard.FormatCount);
n := ClipBoard.FormatCount;
for i := 0 to ClipBoard.FormatCount-1 do begin aArray[i] := ClipBoard.Formats[i]; ClipBoard.GetFormat(ClipBoard.Formats[i], Stream); end;
ClipBoard.Assign(aPicture.Bitmap); PasteFromClipboard;
ClipBoard.Clear; for i := 0 to n-1 do ClipBoard.AddFormat(aArray[i], Stream); end; |
Scheint zu funktionieren, aber geht das auch besser?
//Edit: Funktioniert aber nicht immer, teilweise enthält ClipBoard danach irgendwelche Zeichen. Was stimmt da denn nicht?
jaenicke - Mi 06.05.20 21:39
Du packst alles in einen Stream? Wie soll das denn funktionieren? Woher soll denn beim Laden erkannt werden wo welches Format endet usw.?
galagher - Do 07.05.20 05:47
jaenicke hat folgendes geschrieben : |
Du packst alles in einen Stream? Wie soll das denn funktionieren? |
Nun, AddFormat oder auch SetFormat verlangt eben einen Stream. Ich dachte, das Array erledigt das, aber da wird nun einmal ein Stream verlangt. Und das Array enthält die korrekten Daten ja (denke ich...).
jaenicke hat folgendes geschrieben : |
Woher soll denn beim Laden erkannt werden wo welches Format endet usw.? |
Ja, stimmt. Das ist dann wohl auch der Grund, warum die Zwischenablage manchmal nur "Unsinn" enthält, einfach nur irgendwelche Zeichen. Das passiert genau dann, wenn zB. rtf-formatierter Text in der Zwischenblage ist. Dann ist dort ja nicht nur Text, sondern eben auch der rtf-Code.
Wie kann ich das also verbessern? Muss ja prinzipiell funktionieren: 1. Sichere ClipBoard, 2. schreibe da was rein, 3. stelle ClipBoard wieder her. Im Grunde nichts Aufregendes. Aber wie?
jaenicke - Do 07.05.20 09:50
galagher hat folgendes geschrieben : |
jaenicke hat folgendes geschrieben : | Du packst alles in einen Stream? Wie soll das denn funktionieren? | Nun, AddFormat oder auch SetFormat verlangt eben einen Stream. |
Die Betonung liegt auf
einem Stream. Da packst du ja quasi einfach nur Daten hintereinander rein.
galagher hat folgendes geschrieben : |
Wie kann ich das also verbessern? Muss ja prinzipiell funktionieren: 1. Sichere ClipBoard, 2. schreibe da was rein, 3. stelle ClipBoard wieder her. Im Grunde nichts Aufregendes. Aber wie? |
Einfach einen Stream pro Typ verwenden. ;-)
galagher - Do 07.05.20 14:38
jaenicke hat folgendes geschrieben : |
Einfach einen Stream pro Typ verwenden. ;-) |
Woher weiss ich vorher, wie viele Typen es aktuell gibt bzw. wie genau erstelle ich einen Stream pro Typ?
Th69 - Do 07.05.20 15:23
Du benutzt doch schon die Anzahl ClipBoard.FormatCount? Und davon erstellst du dynamisch entsprechend viele Streams und packst jedes Format in den zugehörigen Stream (und beim Wiederherstellen entsprechend) - so wie du es ja bisher für das array of TClipBoardFormat auch schon gemacht hast.
galagher - Do 07.05.20 15:36
Th69 hat folgendes geschrieben : |
Du benutzt doch schon die Anzahl ClipBoard.FormatCount? Und davon erstellst du dynamisch entsprechend viele Streams und packst jedes Format in den zugehörigen Stream (und beim Wiederherstellen entsprechend) - so wie du es ja bisher für das array of TClipBoardFormat auch schon gemacht hast. |
Ich steh wohl auf der Leitung. Die können doch nicht alle den selben Namen haben? Und wie packe ich die einzelnen Formate da hinein?
Das Prinzip ist mir schon klar, aber die Umsetzung nicht...
//Edit:
Ok, die einzelnen Formate schreibe ich mit
GetFormat hinein. Gut. aber wie erstelle ich n Streams? n ist eine Zahl, die ich vorher nicht kenne.
//Edit: Doch, n kenne ich. n = ClipBoard.FormatCount.
Bleibt die Frage: Wie erstelle ich die einzelnen Streams, die ja nicht alle einfach Stream heissen können?
Th69 - Do 07.05.20 16:00
Genau so wie du es für
aArray auch gemacht hast:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19:
| var aStreams: array of TMemoryStream; begin n := ClipBoard.FormatCount;
SetLength(aStreams, n);
for i := 0 to n-1 do begin aArray[i] := ClipBoard.Formats[i]; ClipBoard.GetFormat(aArray[i], aStreams[i]); end; end |
PS:
aArray solltest du besser
aFormats nennen!
galagher - Do 07.05.20 16:24
Th69 hat folgendes geschrieben : |
Delphi-Quelltext 1: 2: 3:
| var aStreams: array of TMemoryStream; | |
Das ist es, was mir nicht klar war! Jetzt scheint es mir ja ganz einleuchtend, das so zu machen, aber an ein Array von Streams dachte ich nicht!
Hier die ganze Prozedur:
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:
| procedure TCustomRichMemo.InsertGraphic(const aPicture: TPicture); var i, n: Integer; aFormats: array of TClipBoardFormat; aStreams: array of TMemoryStream; begin try n := ClipBoard.FormatCount; SetLength(aFormats, n); SetLength(aStreams, n);
for i := 0 to n-1 do aStreams[i] := TMemoryStream.Create;
for i := 0 to n-1 do begin aFormats[i] := ClipBoard.Formats[i]; ClipBoard.GetFormat(aFormats[i], aStreams[i]); end;
ClipBoard.Assign(aPicture.Bitmap); PasteFromClipboard;
for i := Low(aFormats) to High(aFormats) do ClipBoard.SetFormat(aFormats[i], aStreams[i]);
finally for i := High(aStreams) downto Low(aStreams) do aStreams[i].Free; end; end; |
galagher - Mo 11.05.20 22:56
Nach merfachem Testen kommt es öfter dann zu dem SIGSEGV-Fehler, wenn ich RTF-Daten in der Zwischenablage habe. Wenn ich jedoch ein Sleep(450) einfüge, tritt dieser Fehler nicht auf. :eyecrazy: Mittlerweile habe ich den Code so modifiziert, dass er die RTF-Formatierung in der Zwischenablage erkennt und einfügt. Aber es funktioniert immer nur zusammen mit Sleep!
Draufgekommen bin ich, als ich den Code mit Haltepunkten versehen habe: Eine Pause genügt, und es klappt, und auch Sleep bewirkt dies. Ausserdem tritt der Fehler nur dann auf, wenn die RTF-Daten "frisch" aus der Zwischenablage kommen. Wenn sie mit der Prozedur wieder dorthin zurück geschrieben wurden, funktioniert alles auch ohen Sleep.
Habt ihr eine Erklärung für dieses Phänomen?
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:
| procedure TCustomRichMemo.InsertGraphic(const aPicture: TPicture); var i, n: Integer; aFormats: array of TClipBoardFormat; aStreams: array of TMemoryStream; begin try n := ClipBoard.FormatCount; SetLength(aFormats, n); SetLength(aStreams, n);
Sleep(450) for i := 0 to n-1 do aStreams[i] := TMemoryStream.Create;
for i := 0 to n-1 do begin aFormats[i] := ClipBoard.Formats[i]; ClipBoard.GetFormat(aFormats[i], aStreams[i]); end;
ClipBoard.Assign(aPicture.Bitmap); PasteFromClipboard;
for i := Low(aFormats) to High(aFormats) do ClipBoard.SetFormat(aFormats[i], aStreams[i]);
finally for i := High(aStreams) downto Low(aStreams) do aStreams[i].Free; end; end; |
galagher - So 17.05.20 17:59
Weiss denn keiner weiter? Der Fehler tritt jetzt, obwohl ich die unten stehende Prozedur seit Tagen nicht verändert habe, nur noch sehr selten auf, und wenn, dann immer dann, wenn die RTF-Daten schon
vor dem Programmstart in der Zwischenablage waren. Da hilf auch das try-except nichts, es knallt beim Abfragen des Formats der Zwischenablage:
if Windows.IsClipboardFormatAvailable(CF_RICHTEXT). Mit
if ClipBoard.HasFormat(CF_RICHTEXT) ebenso.
Weiters: Muss CF_RICHTEXT als Word oder als UINT (= LongWord) dekalriert werden? In Lazarus' VirtualTree.pas sind ClipBoard-Formate als Word's deklariert, irgendwo im Internet habe ich aber UINT aufgeschnappt. Teste nun mit Word...
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:
| implementation var CF_RICHTEXT: Word; procedure TCustomRichMemo.InsertGraphic(const aPicture: TPicture); var i, n: Integer; aFormats: array of TClipBoardFormat; aStreams: array of TMemoryStream; cpFormat: TClipBoardFormat; begin try try n := ClipBoard.FormatCount; SetLength(aFormats, n); SetLength(aStreams, n);
for i := 0 to n-1 do aStreams[i] := TMemoryStream.Create;
for i := 0 to n-1 do begin aFormats[i] := ClipBoard.Formats[i]; ClipBoard.GetFormat(aFormats[i], aStreams[i]); end;
if Windows.IsClipboardFormatAvailable(CF_RICHTEXT) then cpFormat := CF_RICHTEXT;
ClipBoard.Assign(aPicture.Bitmap); PasteFromClipboard;
if cpFormat = CF_RICHTEXT then for i := Low(aFormats) to High(aFormats) do begin if aFormats[i] = cpFormat then Clipboard.SetFormat(cpFormat, aStreams[i]) end else for i := Low(aFormats) to High(aFormats) do ClipBoard.SetFormat(aFormats[i], aStreams[i]);
finally for i := High(aStreams) downto Low(aStreams) do aStreams[i].Free; end;
except ; end; end;
initialization CF_RICHTEXT := RegisterClipboardFormat(RichEdit.CF_RTF); |
jaenicke - So 17.05.20 18:58
galagher hat folgendes geschrieben : |
Weiters: Muss CF_RICHTEXT als Word oder als UINT (= LongWord) dekalriert werden? |
RegisterClipboardFormat liefert einen UINT zurück, von daher ist das auch der passende Typ.
galagher - So 17.05.20 19:36
jaenicke hat folgendes geschrieben : |
galagher hat folgendes geschrieben : | Weiters: Muss CF_RICHTEXT als Word oder als UINT (= LongWord) dekalriert werden? | RegisterClipboardFormat liefert einen UINT zurück, von daher ist das auch der passende Typ. |
Ok, danke!
Ich muss mich korrigieren: Der Fehler tritt, wenn, dann schon an dieser Stelle auf:
Delphi-Quelltext
1: 2: 3: 4: 5:
| for i := 0 to n-1 do begin aFormats[i] := ClipBoard.Formats[i]; ClipBoard.GetFormat(aFormats[i], aStreams[i]); end; |
Die Fehlermeldung lautet:
Zitat: |
Projekt project1 hat Exception-Klasse "External SIGSEV" ausgelöst.
Bei Adresse: 7FF833245544 |
Wie gesagt, zig Mal funktioniert es, dann 1x nicht. Und auch nicht nur dann, wenn der RTF-formatierte Text schon in der Zwischenablage ist, bevor mein Programm startet, auch das muss ich korrigieren.
Auch ist es egal, wo ich CF_RICHTEXT deklariere, ob im
CreateWnd oder im
initialization-Abschnitt oder auch gar nicht. Es klappt meistens, aber eben nicht immer.
Das Beste: Wenn ich im Debug-Modus das Programm einfach weiter ausführe, ist die Grafik eingefügt und der Text ist samt Formatierung wieder in der Zwischenablage, so, als sei nichts gewesen!
Wenn ich das Programm alleine, ohne Lazarus, ausführe, kommt die Fehlermeldung:
Zitat: |
project1.exe funktioniert nicht mehr. [...] |
galagher - So 17.05.20 20:46
Ich denke, ich habe die Ursache des Fehlers gefunden: Es geht (manchmal) schief, wenn versucht wird, auf ein ungültiges ClipBoard-Format zuzugreifen. Das passiert zB., wenn die Zwischenablage RTF-formatierten Text einer alten Microsoft-Word-Version enthält.
Leider kann ich das nicht abfangen, denn eine Funktion
ClipBoard.isValidFormat(...) gibt es nicht.
Verwende ich als Quelle hingegen ein aktuelles LibreOffice, tritt der Fehler nicht mehr auf.
galagher hat folgendes geschrieben : |
Das Beste: Wenn ich im Debug-Modus das Programm einfach weiter ausführe, ist die Grafik eingefügt und der Text ist samt Formatierung wieder in der Zwischenablage, so, als sei nichts gewesen! |
Ja, das ist das Beste daran. Ich habe das jetzt so behoben, dass ich daraus eine Funktion gemacht habe, die False zurück gibt, wenn dieser Fehler auftritt, und dabei dennoch weiterarbeitet, denn es klappt ja trotzdem:
Delphi-Quelltext
1: 2:
| if not ClipBoard.GetFormat(aFormats[i], aStreams[i]) then Result := False; |
Ob das ratsam ist, kann ich nicht beurteilen, aber, wie gesagt, alles funktioniert dabei ganz normal.
jaenicke - So 17.05.20 23:11
Interessant wäre bei dem Fehler der Stacktrace. Die Speicheradresse hilft da nicht viel...
galagher - Mo 18.05.20 08:16
Ich muss den Code noch verbessern: So, wie er jetzt ist, sind danach alle ClipBoard-Formate im RTF-Format, normaler Text steht dann anderen Anwendungen nicht mehr zur Verfügung.
jaenicke hat folgendes geschrieben : |
Interessant wäre bei dem Fehler der Stacktrace. Die Speicheradresse hilft da nicht viel... |
Meinst du die Zeile, bei der der Debugger (in einem eigenen Fenster) anhält und diese markiert?
Th69 - Mo 18.05.20 10:05
Ja, und dann das Fenster
Call Stack [
https://wiki.freepascal.org/IDE_Window:_Call_Stack] (auch Stacktrace genannt) anschauen, um die genaue Ursache (hoffentlich) zu finden.
Aber was willst du überhaupt mit der Sonderbehandlung von
CF_RICHTEXT (beim Wiederherstellen der Zwischenablage)?
galagher - Mo 18.05.20 13:03
Th69 hat folgendes geschrieben : |
Aber was willst du überhaupt mit der Sonderbehandlung von CF_RICHTEXT (beim Wiederherstellen der Zwischenablage)? |
Ohne
CF_RICHTEXT ist sonst nur der reine Text, ohne RTF-Formatierung, in der Zwischenablage.
Th69 - Mo 18.05.20 14:22
Ich meine
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8:
| if cpFormat = CF_RICHTEXT then for i := Low(aFormats) to High(aFormats) do begin if aFormats[i] = cpFormat then Clipboard.SetFormat(cpFormat, aStreams[i]) end else ... |
Warum du also nur im
else-Zweig alle Formate wieder zurückschreibst?
galagher - Mo 18.05.20 16:13
Th69 hat folgendes geschrieben : |
Warum du also nur im else-Zweig alle Formate wieder zurückschreibst? |
Ja, das meinte ich damit, dass ich dann nur noch den RTF-formatierten Text in der Zwischenablage habe. Ich weiss im Moment aber nicht, wie ich herausfinde, wo ich den RTF-formatierten Text zurückschreiben muss.
galagher - Mo 18.05.20 17:00
Nun, das Assembler-Fenster markiert die Zeile:
00007FF833245544 807a0f05 cmpb $0x5,0xf(%rdx)
Wo finde ich das Fenster Call Stack bzw. Stacktrace?
Was die Wiederherstellung der verschiedenen Formate anlangt, komme ich nicht weiter:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17:
| for i := 0 to n-1 do begin aFormats[i] := ClipBoard.Formats[i]; if not ClipBoard.GetFormat(aFormats[i], aStreams[i]) then Result := False; end;
for i := Low(aFormats) to High(aFormats) do begin if aFormats[i] = CF_RICHEDIT then Clipboard.SetFormat(CF_RICHEDIT, aStreams[i]) else ClipBoard.SetFormat(aFormats[i], aStreams[i]); end; |
Funktioniert aber nicht. Es ist dann nur reiner Text in der Zwischenablage.
Th69 - Mo 18.05.20 17:42
Müsste im Menü "View -> Debug Windows -> Call stack" sein (laut Internet, da ich Lazarus nicht kenne).
Ich verstehe immer noch nicht, was du genau erreichen möchtest.
Fehlen dir noch Grundkenntnisse beim Programmieren? Die Sonderabfrage auf
CF_RICHTEXT bei deiner unteren Schleife ist doch komplett überflüssig (da die Schleife doch sowieso über alle gespeicherten Formate iteriert).
Ich kann dir das Tool
Free Clipboard Viewer [
https://www.freeclipboardviewer.com/windowsclipboard.html] empfehlen, das zeigt alle zurzeit enthaltenen Formate an (habe ich gestern auch für ein Programm bei mir bzgl. HTML-Format benötigt).
Es liegt ja an dem jeweiligen Programm, welches der aktuell enthaltenen Formate es benutzt (ein reiner Texteditor nutzt nur das Text-Format, wenn es denn enthalten ist und ignoriert alle anderen Formate - und andere Programme überprüfen dann die Formate in jeweiliger Priorität und das Text-Format dann meistens als letztes).
galagher - Mo 18.05.20 18:25
Th69 hat folgendes geschrieben : |
Müsste im Menü "View -> Debug Windows -> Call stack" sein (laut Internet, da ich Lazarus nicht kenne). |
Ok, das ist dann also "Aufrufstack". Dort steht:
Zitat: |
Index Position Zeile Funktion
0 :7FF833245544 - ?? |
Ich muss das jedesmal abtippen, da kopieren nicht funktioniert, weil die Zwischenablage in diesen Momenten gesperrt ist.
Th69 hat folgendes geschrieben : |
Ich verstehe immer noch nicht, was du genau erreichen möchtest. |
Ich möchte ine Grafik in ein TRichMemo einfügen. Die Methode, die TRichMemo mitbringt, ist unbrauchbar, weil man damit zwar Grafiken einfügen kann, diese aber nicht gespeichert werden, weil die entsprechenden Daten nicht in die Datei geschrieben werden. Aber per ClipBoard funktioniert es. Also dachte ich, sichere den Inhalt der Zwischenablage, kopiere die Grafik hinein, füge die Grafik ins RichMemo ein, stelle den Inhalt der Zwischenablage wieder her.
Funktioniert prinzipiell. Ich kann aber nicht erreichen, dass bei RTF-Daten die beiden Formate CF_RICHEDIT und CF_TEXT erhalten bleiben. Entweder habe ich ausschliesslich CF_RICHEDIT oder ausschliesslich CF_TEXT, je nachdem, wie ich den Code abändere (s. den Code-Schnipsel).
Th69 hat folgendes geschrieben : |
Fehlen dir noch Grundkenntnisse beim Programmieren? Die Sonderabfrage auf CF_RICHTEXT bei deiner unteren Schleife ist doch komplett überflüssig (da die Schleife doch sowieso über alle gespeicherten Formate iteriert). |
Ja, das sollte man meinen. Es ist aber nicht so. Meine Grundkenntnisse beim Programmieren sind durchaus ausreichend, zu erkennen, dass das überflüssig ist - normalerweise. Hier aber nicht, denn wenn ich den Code so abändere: ...
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9:
| for i := Low(aFormats) to High(aFormats) do ClipBoard.SetFormat(aFormats[i], aStreams[i]); |
... schreibe ich natürlich alles in die Zwischenablage zurück, aber es ist danach nur reiner Text in der Zwischenablage, die RTF-Formatierung geht verloren.
Leider kann man das alles mit Delphi nicht nachstellen, da gibt es zB. kein
ClipBoard.GetFormat.
galagher - Di 19.05.20 07:25
jaenicke hat folgendes geschrieben : |
Ein Screenshot ist auch möglich. ;-)
Aber es gibt auch einen Knopf für das Kopieren: |
Da gibt es einen Button "Alles kopieren" (oder so, in diesem Sinne, hab ich jetzt nicht vor mir). Das funktionierte nicht.
Es bleibt aber die Frage stehen, warum ich das RTF-Format nicht richtig hin bekomme! Es wird jedenfalls nicht automatisch erkannt, automatisch wird nur CF_TEXT, CF_BITMAP usw. erkannt.
Da gibt es noch eine weitere Möglichkeit: Ein temporäres TRichMemo. Unelegant, zugegeben, wird aber vermutlich funktionieren. Kann ich ja mal versuchen.
Th69 - Di 19.05.20 13:25
Wie überprüfst du denn, welche Formate alles in der Zwischenablage sind?
Wenn du also zweimal deinen Code aufrufst (mit Auslesen und zurückschreiben aller Formate), dann fehlt plötzlich das RTF-Format?
Was mich auch noch wundert, ist, daß du extra noch
RichEdit.CF_RTF registrieren mußt, denn das ist ja ein Standard-Format (s.a. "Standard Clipboard Formats"-Liste im oben schon verlinkten
FreeClipboardViewer [
https://www.freeclipboardviewer.com/windowsclipboard/]).
Edit: OK, in
Windows Clipboard Formats [
https://www.codeproject.com/Reference/1091137/Windows-Clipboard-Formats] steht, daß es nur ein "Common Application Clipboard Format" ist und kein "Standard Clipboard Format".
Aber entspricht denn
RichEdit.CF_RTF = 'Rich Text Format' (bzw.
'text/richtext')?
Edit: Auch das konnte ich jetzt in
How to reliably detect RICHTEXT format on clipboard? [
https://stackoverflow.com/questions/45837425/how-to-reliably-detect-richtext-format-on-clipboard] nachlesen.
Du kannst auch mal mittels
Clipboard.SupportedFormats(list) dir eine Liste aller vom System unterstützten Formate zurückliefern lassen (ohne vorher die extra Registrierung durchzuführen), s.a.
View the clipboard contents [
https://wiki.lazarus.freepascal.org/Clipboard#View_the_clipboard_contents].
Und bzgl.
galagher hat folgendes geschrieben: |
Leider kann man das alles mit Delphi nicht nachstellen, da gibt es zB. kein ClipBoard.GetFormat. |
In der VCL gibt es die Methoden
TPicture.LoadFromClipboardFormat [
http://docwiki.embarcadero.com/Libraries/Rio/de/Vcl.Graphics.TPicture.LoadFromClipboardFormat] und
TPicture.SaveToClipboardFormat [
http://docwiki.embarcadero.com/Libraries/Rio/de/Vcl.Graphics.TPicture.SaveToClipboardFormat], da diese hauptsächlich für die verschiedenen Bildformate benutzt werden. Andererseits sind dies auch nur Wrapper für die WinAPI-Funktionen, s.
Using the Clipboard [
https://docs.microsoft.com/de-de/windows/win32/dataxchg/using-the-clipboard]:
-
Copying Information to the Clipboard [
https://docs.microsoft.com/de-de/windows/win32/dataxchg/using-the-clipboard#copying-information-to-the-clipboard]
-
Pasting Information from the Clipboard [
https://docs.microsoft.com/de-de/windows/win32/dataxchg/using-the-clipboard#pasting-information-from-the-clipboard]
galagher - Di 19.05.20 16:08
Th69 hat folgendes geschrieben : |
Wie überprüfst du denn, welche Formate alles in der Zwischenablage sind? |
Gar nicht, ich sichere zuerst alle Formate und stelle sie danach wieder her. Das funktioniert aber mit dem RTF-Format nicht. Da bleibt nur Plain-Text übrig. Ansosnten funktioniert das sehr wohl, mit normalem Text, mit Icons und mit Bitmaps.
Th69 hat folgendes geschrieben : |
Wenn du also zweimal deinen Code aufrufst (mit Auslesen und zurückschreiben aller Formate), dann fehlt plötzlich das RTF-Format? |
Nein, nur wenn ich alle Formate zurückschreibe, ohne das RTF-Format extra zu berücksichtigen. Es wird offenbar nicht erkannt.
Auch, wenn ich mir die Position im Array merke und das RTF-Format (den formatierten Text) explizit an diese Position zurückschreibe, klappt es nicht. Da passt offenbar der Wert von "i" bei
aStream[i] nicht. Oder was weiss ich. :nixweiss: Es geht eben nicht.
Th69 hat folgendes geschrieben : |
Was mich auch noch wundert, ist, daß du extra noch RichEdit.CF_RTF registrieren mußt, denn das ist ja ein Standard-Format |
Wie sonst? CF_RTF ist ja ein String, kein Format. Ich
muss es also registrieren.
Ja, es entspricht! Das habe ich mir von eben dieser Seite abgeschaut.
Th69 hat folgendes geschrieben : |
Du kannst auch mal mittels Clipboard.SupportedFormats(list) dir eine Liste aller vom System unterstützten Formate zurückliefern lassen (ohne vorher die extra Registrierung durchzuführen), |
Da ist
'Rich Text Format' bzw.
'text/richtext' nicht dabei.
Wird vor dem Einfügen der Grafik, also beim Sichern der Formate der Zwischanablage, das RTF-Format noch erkannt (
Clipboard.HasFormat(CF_RICHTEXT liefert
True zurück), kann ich es nach dem Einfügen der Grafik, also bei Wiederherstellen der Formate der Zwischanablage, nicht mehr wiederherstellen. Der reine Text ist ja da, aber jede RTF-Formatierung ist verloren gegangen.
Wie erwartet, funktioniert es aber mit einem temporären TRichMemo, in das ich den RTF-formatierten Inhalt einfüge und hinterher von dort wieder in die Zwischanablage zurück kopiere. Aber irgendwie ist das doch nicht besonders schön. Klappt und kann man zur Not auch belassen, aber der direkte Weg über die Formate wäre eleganter.
Th69 - Di 19.05.20 17:22
Ich frage noch mal: Wie überprüfst du denn, welche Formate alles in der Zwischenablage sind?
Damit meine ich nicht codetechnisch, sondern verwendest du ein externes Programm oder woher weißt du, daß das Richtext-Format nicht mehr zurückgeschrieben wird?
Darum habe ich dir ja auch den Link zu dem FreeClipboardViewer gegeben.
Und mit welchem Programm erzeugst du denn überhaupt den Richtext in der Zwischenablage?
galagher - Di 19.05.20 17:44
Th69 hat folgendes geschrieben : |
Ich frage noch mal: Wie überprüfst du denn, welche Formate alles in der Zwischenablage sind?
Damit meine ich nicht codetechnisch, sondern verwendest du ein externes Programm oder woher weißt du, daß das Richtext-Format nicht mehr zurückgeschrieben wird? |
Ganz einfach: Indem ich den Text, der ja das RTF-Format sein müsste, wenn man alles korrekt wieder zurückschreibt, ins RichMemo einfüge. Da wird aber eben nur Text ohne Formatierung eingefügt.
Ich habe dabei jetzt den FreeClipboardViewer eingesetzt: das RTF-Format ist hinterher tatsächlich nicht mehr vorhanden, sondern (u. a.) nur noch der reine Text. Also entweder ist die Wiederherstellung nicht korrekt, aber was kann bei dieser
for-Schleife falsch sein? Oder das RTF-Format wird in
aStreams[i] nicht mit gesichert. Das wird's sein, denke ich.
Th69 hat folgendes geschrieben : |
Und mit welchem Programm erzeugst du denn überhaupt den Richtext in der Zwischenablage? |
Einerseits mit dem RichMemo selbst, andererseits mit Microsoft Word und mit LibreOffice, den RTF-Text kopiere ich dann in die Zwischenablage. Immer ist beim Zurückschreiben das RTF-Format weg, wie man mit FreeClipboardViewer sieht, obwohl es vorher vorhanden war.
Th69 - Di 19.05.20 18:07
Wenn ich den Kommentar in
Clipboard.pp [
https://github.com/alrieckert/lazarus/blob/master/lcl/clipbrd.pp]
Zitat: |
SetFormat
Clears the clipboard and adds the data. |
richtig verstehe, so kann mit dieser Funktion immer nur genau 1 Format gespeichert werden...
Probiere mal stattdessen
AddFormat!
Zitat: |
AddFormat:
Use these functions to add data to the supported formats.
|
(und vor der Schleife noch
Clear aufrufen)
galagher - Di 19.05.20 19:54
Th69 hat folgendes geschrieben : |
Probiere mal stattdessen AddFormat! |
Es funktioniert! :dance: Jetzt passt alles!
Nun tritt aber der SIGSEGV-Fehler wieder auf, wenn ich RTF-Text aus dem alten Word verwende. Er tritt auf beim Sichern der Formate in der Schleife:
Delphi-Quelltext
1: 2: 3: 4: 5:
| for i := 0 to n-1 do begin aFormats[i] := ClipBoard.Formats[i]; end; |
Nicht schon beim 1.Durchlauf, sondern beim 8. Den Fehler kann ich aber nicht abfangen.
Klar, jetzt ist der Code besser, übersichlicher und sauberer als mit dem leidigen temporären TRichEdit. Aber nun habe ich das Problem wieder, dass der mit Daten alter Programme (ca. 20 Jahre) nicht kann. Ok, sollte man ja ohnehin nicht mehr verwenden, aber es geht ums Prinzip: Warum?
Vielleicht findest du ja die Ursache, oder man kann noch etwas verbessern. Hier die ganze Funktion:
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:
| function TCustomRichMemo.InsertGraphic(const aPicture: TPicture): Boolean; var i, n: Integer; aFormats: array of TClipBoardFormat; aStreams: array of TMemoryStream; begin Result := True;
try
n := ClipBoard.FormatCount; SetLength(aFormats, n); SetLength(aStreams, n);
for i := 0 to n-1 do aStreams[i] := TMemoryStream.Create;
try for i := 0 to n-1 do begin aFormats[i] := ClipBoard.Formats[i]; Result := ClipBoard.GetFormat(aFormats[i], aStreams[i]); end;
ClipBoard.Assign(aPicture.Bitmap); PasteFromClipboard;
ClipBoard.Clear; for i := Low(aFormats) to High(aFormats) do ClipBoard.AddFormat(aFormats[i], aStreams[i]);
finally for i := High(aStreams) downto Low(aStreams) do aStreams[i].Free; end;
except Result := False; ClipBoard.Clear; end; end; |
galagher - Di 19.05.20 20:11
galagher hat folgendes geschrieben : |
Nun tritt aber der SIGSEGV-Fehler wieder auf, wenn ich RTF-Text aus dem alten Word verwende. Er tritt auf beim Sichern der Formate in der Schleife:
Delphi-Quelltext 1: 2: 3: 4: 5:
| for i := 0 to n-1 do begin aFormats[i] := ClipBoard.Formats[i]; end; | |
Ok, der Fehler tritt so oder so auf, mit oder ohne temporärem RichMemo. Denn er tritt ja an einer Stelle auf, wo ein RichMemo noch gar nicht vorkommt.
Aber er tritt nur manchmal auf. Ich möchte gerene darauf reagieren, aber das Programm rumpelt drüber und dann knallts... Wie gesagt, immer nur bei ClipBoard-Daten alter Programme.
jaenicke - Di 19.05.20 20:33
Den kompletten Stacktrace von dem Fehler haben wir bisher aber noch nicht gesehen. ;-)
Und da wir das ohne die passenden Daten nicht reproduzieren können, können wir auch nicht überlegen wo der Fehler sein könnte.
galagher - Di 19.05.20 21:17
jaenicke hat folgendes geschrieben : |
Den kompletten Stacktrace von dem Fehler haben wir bisher aber noch nicht gesehen. ;-)
Und da wir das ohne die passenden Daten nicht reproduzieren können, können wir auch nicht überlegen wo der Fehler sein könnte. |
Wenn das weiterhilft, gerne!
jaenicke - Di 19.05.20 22:37
Hmm, dann ist da schon richtig etwas kaputt, wenn es nicht einmal mehr einen richtigen Stacktrace gibt. Dann bleibt nur manuell durch debuggen...
Nach meinen bisherigen Erfahrungen mit Lazarus ist es durchaus nicht unwahrscheinlich, dass du dabei einen Bug findest.
galagher - Mi 20.05.20 05:30
jaenicke hat folgendes geschrieben : |
Hmm, dann ist da schon richtig etwas kaputt, wenn es nicht einmal mehr einen richtigen Stacktrace gibt. Dann bleibt nur manuell durch debuggen... |
Das Komische ist ja, dass es unter Lazarus, wenn man es im Debug-Modus ausführt, und dann, nach dem Auftreten des Fehlers, weiter ausführt, einfach ganz normal weiterläuft. Alles funktioniert, als sei nicht passiert. Nur ohne Lazarus, also "alleine", beendet sich das Programm einfach. Und ich kann das wie gesagt nicht abfangen. Zumindest weiss ich nicht, wie.
jaenicke - Mi 20.05.20 05:44
galagher hat folgendes geschrieben : |
Und ich kann das wie gesagt nicht abfangen. Zumindest weiss ich nicht, wie. |
try..except klappt nicht?
Aber wie gesagt, interessant wäre ja an welcher Stelle genau der Fehler auftritt, wenn du durch den Quelltext debuggst, also durch TClipboard.GetFormats usw. (ich sehe dort z.B. eine Unterscheidung nach CanReadFromCache).
Th69 - Mi 20.05.20 08:02
Tja, manchmal hilft Doku lesen. ;-)
Und auch bzgl. des SIGSEGV-Fehlers:
Exceptions: Exception Classes [
https://wiki.freepascal.org/Exceptions#Exception_classes]
Füge die Unit
SysUtils mal hinzu.
Dann solltest du aber zusätzlich einen
try..except-Block um die fehlerhafte Funktion packen.
PS: Das Erzeugen der Stream-Objekte kannst du auch in die 2. Schleife packen (wie für das Formats-Array).
Und wenn du den Code wiederverwendbar machen möchtest, dann solltest du die Funktionalität in eine eigene Klasse packen (z.B.
ClipboardSaver).
galagher - Mi 20.05.20 11:47
jaenicke hat folgendes geschrieben : |
Aber wie gesagt, interessant wäre ja an welcher Stelle genau der Fehler auftritt, wenn du durch den Quelltext debuggst, also durch TClipboard.GetFormats usw. (ich sehe dort z.B. eine Unterscheidung nach CanReadFromCache). |
In clipbrd.pas ist
GetFormats zwar deklariert, aber im
implementation-Teil nicht vorhanden. Wo also finde ich
CanReadFromCache?
Seh ich mir an!
Danke für eure Tipps!
jaenicke - Mi 20.05.20 12:29
galagher hat folgendes geschrieben : |
In clipbrd.pas ist GetFormats zwar deklariert, aber im implementation-Teil nicht vorhanden. Wo also finde ich CanReadFromCache? |
Debugge doch einfach von der Zeile in deinem Quelltext aus durch. ;-)
galagher - Mi 20.05.20 14:59
Th69 hat folgendes geschrieben : |
Füge die Unit SysUtils mal hinzu. |
SysUtils ist eingebunden.
Th69 hat folgendes geschrieben : |
Dann solltest du aber zusätzlich einen try..except-Block um die fehlerhafte Funktion packen. |
Nützt leider nichts.
Th69 hat folgendes geschrieben : |
PS: Das Erzeugen der Stream-Objekte kannst du auch in die 2. Schleife packen (wie für das Formats-Array).
Und wenn du den Code wiederverwendbar machen möchtest, dann solltest du die Funktionalität in eine eigene Klasse packen (z.B. ClipboardSaver). |
Habe ich vor! Erstmal herausfinden, was da los ist.
jaenicke hat folgendes geschrieben : |
Debugge doch einfach von der Zeile in deinem Quelltext aus durch. ;-) |
Zwei Möglichkeiten:
1. Das Programm wird mit einer SIGSEGV-Meldung abgebrochen, mit oder ohne try/except. Debuggen von der Zeile im Quelltext? Ich setzt einen Haltepunkt bei
aFormats[i] := ClipBoard.Formats[i];, der Debugger hält mehrfach dort an, dann kracht es. Das
except wird erst gar nicht angesprungen. (
ClipBoard.Formats; verwendet ja
ClipBoard.GetFormats, da ist ein Haltepunkt also prinzipiell sinnvoll).
2. Es klappt alles wie gewünscht.
Mal so, mal so, aber nie mit RTF-Text oder Grafiken aktueller Programme. Wenn ich den RTF-Text mit LibreOffice in die Zwischenablage kopiere, befindet sich dort ein Format namens "Richtext Format". Wenn ich den RTF-Text mit Word in die Zwischenablage kopiere, gibt es dieses Format nicht. Ob das die Ursache ist, weiss ich nicht.
Ich kann leider nirgendwo
CanReadFromCache finden, geschweige denn den Code von
ClipBoard.GetFormats.
Welche Möglichkeiten gibt es noch, wenn try/except nichts nützen?
jaenicke - Mi 20.05.20 15:50
galagher hat folgendes geschrieben : |
jaenicke hat folgendes geschrieben : | Debugge doch einfach von der Zeile in deinem Quelltext aus durch. ;-) | Zwei Möglichkeiten:
1. Das Programm wird mit einer SIGSEGV-Meldung abgebrochen, mit oder ohne try/except. Debuggen von der Zeile im Quelltext? Ich setzt einen Haltepunkt bei aFormats[i] := ClipBoard.Formats[i];, der Debugger hält mehrfach dort an, dann kracht es. Das except wird erst gar nicht angesprungen. |
Ja, aber was passiert dort?
Hast du noch nie schrittweise debuggt? Sprich F7 um in eine Methode hinein zu debuggen, F8 um darüber hinweg zu debuggen?
Beispiel:
Th69 - Mi 20.05.20 15:52
Läuft das Programm denn außerhalb der IDE (mit aktiviertem
try..except) durch?
Hast du evtl. in der IDE "Halt on exception" in den Optionen aktiviert?
Und hast du nur einen leeren
except-Block? Dann versuche mal
Delphi-Quelltext
1: 2: 3: 4: 5:
| try except on E : Exception do ShowMessage(E.Message); end |
galagher - Do 21.05.20 19:53
jaenicke hat folgendes geschrieben : |
Hast du noch nie schrittweise debuggt? Sprich F7 um in eine Methode hinein zu debuggen, F8 um darüber hinweg zu debuggen? |
F7 nicht so oft, aber F8 doch, natürlich, aber es war noch nie so mühsam wie diesmal, weil es ja nicht jedesmal zu diesem SIGSEGV kommt. Aber ich denke, ich habe die Stelle gefunden in win32winapi.inc in Prozedur TWin32WidgetSet.ClipboardGetData bei
Size := Windows.GlobalSize(DataHandle);:
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:
| function TWin32WidgetSet.ClipboardGetData(ClipboardType: TClipboardType; FormatID: TClipboardFormat; Stream: TStream): Boolean; var DataHandle: HGLOBAL; Data: pointer; Size: integer; {$IFDEF VerboseWin32Clipbrd} DbgFormatID: integer; {$ENDIF} Bitmap: TBitmap; BufferStream: TMemoryStream; BufferWideString: widestring; BufferString: ansistring;
function ReadClipboardToStream(DestStream: TStream): Boolean; begin Result := false;
DataHandle := Windows.GetClipboardData(FormatID); if DataHandle<>HWND(0) then begin Size := Windows.GlobalSize(DataHandle); |
Warum klappt es manchmal, dann wieder nicht? Und: Wenn ich das Programm einfach trotzdem weiter laufen lasse, wird die Grafik eingefügt und der ursprüngliche Inhalt der Zwischanablage ist wieder vorhanden!
Vor allem aber: Wie kann ich das nun beheben?
galagher - Do 21.05.20 20:27
Mittlerweile bin ich mir nicht mehr sicher, dass das die einzige Stelle ist, bei der der Fehler auftritt. Denn wenn ich dort einen Haltepunkt setze und dann F8 drücke, läuft der Code weiter und dann erst kommt SIGSEGV.
Auch das Ummanteln der Codestelle mit try/except nützt nichts.
Weil der Fehler ja nicht jedesmal auftriff, ist das Debuggen wirklich mühselig.
galagher - Do 21.05.20 20:35
Th69 hat folgendes geschrieben : |
Läuft das Programm denn außerhalb der IDE (mit aktiviertem try..except) durch? |
Nein. Es bricht mit der Meldung "... funktioniert nicht mehr ... muss beendet werden" ab.
Th69 hat folgendes geschrieben : |
Hast du evtl. in der IDE "Halt on exception" in den Optionen aktiviert? |
Ich finde diese Einstellung nicht, ich weiss es nicht
Th69 hat folgendes geschrieben : |
Und hast du nur einen leeren except-Block? |
Nein, egal ob leer oder nicht, es geht nie ins except.
Th69 - Fr 22.05.20 15:36
Versuche mal einen eigenen "Runtime error" wie DivideByZero (x/0) auszulösen und zu fangen - klappt das denn?
galagher - Fr 22.05.20 16:00
Th69 hat folgendes geschrieben : |
Versuche mal einen eigenen "Runtime error" wie DivideByZero (x/0) auszulösen und zu fangen - klappt das denn? |
Ja, das klappt mit dem try/except. Ich habe mir eine kleine Prozedur gebastelt:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
| var x, y: Double; begin try x := 1; y := 0; Caption := IntToStr(Round(x / y));
except ShowMessage('Fehler.'); end; end; |
Mit der IDE kommt zunächst eine SIGFPE-Fehlermeldung und die IDE markiert die Zeile
Caption := IntToStr. Wenn ich das Programm weiter ausführe, springt es ins except und ShowMessage erscheint.
Ohne IDE springt es gleich ins except und ShowMessage erscheint.
Th69 - Fr 22.05.20 17:07
Das, was mir dann noch einfällt, wäre die Source-Unit von TClipboard zu duplizieren (und einen anderen Klassennamen bzw. Namensbereich geben), dort dann uses SysUtils hinzufügen und mit dieser neuen Klasse noch mal auszuprobieren.
Edit: Oh, sehe gerade, daß der SIGSEGV-Fehler ja noch tiefer in der Klasse TWin32WidgetSet auftritt: Ist das eine Basisklasse von TClipboard?
Trotzdem probiere mal (zuerst) das oben beschriebene.
galagher - Fr 22.05.20 17:20
Th69 hat folgendes geschrieben : |
Das, was mir dann noch einfällt, wäre die Source-Unit von TClipboard zu duplizieren (und einen anderen Klassennamen bzw. Namensbereich geben), dort dann uses SysUtils hinzufügen und mit dieser neuen Klasse noch mal auszuprobieren. |
Aber in clipbrd.pp ist SysUtils bereits angegeben. Was meinst du?
Th69 - Fr 22.05.20 17:47
Dann ist das ja noch eigenartiger.
Ist das denn in TWin32WidgetSet auch schon eingebunden?
PS: Linkst du die Bibliotheken dynamisch oder statisch (d.h. verwendest du die Runtime-Packages oder nicht)?
galagher - Fr 22.05.20 18:05
Th69 hat folgendes geschrieben : |
Ist das denn in TWin32WidgetSet auch schon eingebunden? |
Nun, TWin32WidgetSet ist in Win32.int deklariert, und ja, SysUtils ist auch dort eingebunden.
Th69 hat folgendes geschrieben : |
PS: Linkst du die Bibliotheken dynamisch oder statisch (d.h. verwendest du die Runtime-Packages oder nicht)? |
Die Packages sind fix in Lazarus, das heisst, ich habe die IDE kompiliert, um die verschiedenen Packages nutzen zu können.
Th69 - Fr 22.05.20 19:15
Kommst du denn auch an den Sourcecode von
SysUtils ran, um zu schauen, wie dort die Exceptions für die Runtime-Fehler eingeschaltet werden?
Irgendwie muß es ja einen Unterschied geben, denn bei deinem Code funktioniert es ja.
Edit: Es wird wohl
ErrorProc [
https://www.freepascal.org/docs-html/rtl/system/errorproc.html] auf eine eigene Prozedur gesetzt (evtl. kannst du in dieser mal debuggen, d.h. Haltepunkt setzen).
galagher - Fr 22.05.20 19:19
Th69 hat folgendes geschrieben : |
Kommst du denn auch an den Sourcecode von SysUtils ran, um zu schauen, wie dort die Exceptions für die Runtime-Fehler eingeschaltet werden? |
Ja, den Sourcecode habe ich. Wonach genau suchen wir denn?
Th69 - Fr 22.05.20 19:28
Hatte gerade noch meinen Beitrag editiert: schau mal nach der Prozedur, welcher
ErrorProc zugewiesen wird.
Evtl. liegt es ja auch an diesem speziellen SIGSEV-Fehler. Dieser dürfte wohl "216 General Protection fault" aus
Appendix D: Run-time errors [
https://www.freepascal.org/docs-html/user/userap4.html] entsprechen.
Kannst du diesen mal mit deinem Testprogramm auslösen (d.h. einen ungültigen Speicherzugriff, z.B. auf eine feste Adresse bzw. Dereferenzierung von
nil)?
galagher - Fr 22.05.20 19:28
In sysutils.pp kommt ErrorProc nicht vor.
Aber ich habe eine interessante Seite gefunden:
https://www.delphipraxis.net/149513-clipboard-temporaer-speichern.html
Leider kann ich den Code aber nicht nutzen, weil Lazarus keine Prozedur
ClipBoard.GetAsHandle hat. Lieber würde ich aber natürlich den momentanen Weg mit den Arrays beibehalten.
Edit: Und auch
ClipBoard.SetAsHandle gibt es nicht.
galagher - Fr 22.05.20 19:41
In sysutils.inc habe ich gefunden:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9:
| Procedure InitExceptions; begin ErrorProc:=@RunErrorToExcept; end;
|
Edit: Ich denke ja doch, dass es - namentlich bei Lazarus - erlaubt ist, fremden Sourcecode zur Gänze zu posten.
galagher - Fr 22.05.20 20:04
Was und wie genau zB.?
galagher - Fr 22.05.20 20:16
Th69 hat folgendes geschrieben : |
Und dann debugge mal die RunErrorToExcept-Prozedur (Step by Step). |
Ich soll den Code also in eine Prozedur meines Programms testweise einbauen, try/except drum rum, und das dann testen?
Th69 - Fr 22.05.20 20:27
Ja, so wie dein vorheriges Testprogramm mit der DivideByZero-Exception und dann Haltepunkt auf die erste Zeile der RunErrorToExcept-Prozedur setzen und schrittweise debuggen, was da passiert (warum dein except-Block nicht angesprungen wird).
Edit:
Du sollst nicht den Code der RunErrorToExcept-Prozedur kopieren, sondern diesen nur debuggen (d.h. die Sourcedatei im Editor öffnen und Haltepunkt dort setzen - es kann sein, daß du erst dein Programm starten mußt und einen eigenen Haltepunkt anspringen mußt, ehe du den Haltepunkt in der Systemdatei setzen kannst).
PS: Falls du explizit 216 abfangen willst, dies sollte dann eine EAccessViolation-Exception sein.
galagher - Fr 22.05.20 20:56
Th69 hat folgendes geschrieben : |
Edit:
Du sollst nicht den Code der RunErrorToExcept-Prozedur kopieren, sondern diesen nur debuggen (d.h. die Sourcedatei im Editor öffnen und Haltepunkt dort setzen - es kann sein, daß du erst dein Programm starten mußt und einen eigenen Haltepunkt anspringen mußt, ehe du den Haltepunkt in der Systemdatei setzen kannst). |
Lazarus akzeptiert in sysutils.inc keine Haltepunkte...
Th69 hat folgendes geschrieben : |
Ja, so wie dein vorheriges Testprogramm mit der DivideByZero-Exception und dann Haltepunkt auf die erste Zeile der RunErrorToExcept-Prozedur setzen und schrittweise debuggen, was da passiert (warum dein except-Block nicht angesprungen wird). |
Im Testcode wird
except sehr wohl angesprungen, und zwar mit und ohne IDE!
Th69 hat folgendes geschrieben : |
Edit:
Du sollst nicht den Code der RunErrorToExcept-Prozedur kopieren, sondern diesen nur debuggen (d.h. die Sourcedatei im Editor öffnen und Haltepunkt dort setzen - es kann sein, daß du erst dein Programm starten mußt und einen eigenen Haltepunkt anspringen mußt, ehe du den Haltepunkt in der Systemdatei setzen kannst). |
Ok, dann entferne ich das aus meinem Beitrag. Aber wie gesagt: Haltepunkt klappt in sysutils.inc nicht. Liegt's an der Dateinamenerweiterung oder an der Tatsache, das die Methoden dort nur im
implementation-Teil und nicht auch im
interfacce stehen?
Th69 hat folgendes geschrieben : |
PS: Falls du explizit 216 abfangen willst, dies sollte dann eine EAccessViolation-Exception sein. |
Hm... Schritt für Schritt erstmal...
Edit:
Für heute erstmal
vielen Dank!
Th69 - Fr 22.05.20 21:05
galagher hat folgendes geschrieben : |
Lazarus akzeptiert in sysutils.inc keine Haltepunkte... |
Das ist blöd. ;-(
galagher hat folgendes geschrieben : |
Im Testcode wird except sehr wohl angesprungen, und zwar mit und ohne IDE! |
Auch bei einem SIGSEGV-Fehler?
Dann verstehe ich nicht, warum es nicht bei deinem Clipboard-Programm funktioniert?
galagher hat folgendes geschrieben : |
Für heute erstmal vielen Dank! |
Dann gute Nacht!
galagher - Sa 23.05.20 04:21
Th69 hat folgendes geschrieben : |
Dann verstehe ich nicht, warum es nicht bei deinem Clipboard-Programm funktioniert? |
Ich auch nicht. Und was ich noch nicht verstehe: Der SIGSEGV-Fehler tritt nur so etwa jedes 10. Mal auf, ansonsten funktioniert es.
Mit der IDE kam diesmal danach noch folgende Meldung:
Zitat: |
Projekt project1 hat Exception-Klasse "Exception" ausgelöst mit der Meldung:
IndexOfCachedFormatID: Internal Error: invalid FormatID 0 for clipboard
In Datei 'include\clipboard.inc' in Zeile 58 |
Und Zeile 58 dieser Datei ist eben genau dies:
Delphi-Quelltext
1: 2: 3:
| raise Exception.Create( 'IndexOfCachedFormatID: Internal Error: invalid FormatID 0 for '+ ClipboardTypeName[ClipboardType]); |
Vielleicht hilf das ja weiter.
Zu den SIGSEGV-Fehlermeldungen selbst: Die linke Grafik zeigt den SIGSEGV-Fehler bei dem "
pIntPointer := nil;"-Test, die rechte den SIGSEGV-Fehler beim Einfügen der Grafik.
Edit:
Und was die Sache noch seltsamer macht: Ich kopiere also irgendeinen RTF-Text von Word in die Zwischanablage, füge danach meine Grafik ein, es knallt und das Programm beendet sich. Danach starte ich das Programm erneut, füge wieder die Grafik ein und diesmal funktioniert es. Und wenn ich dann die Grafik noch so oft einfüge - es klappt.
Wenn es 1x klappt, dann bleibt das auch so. Wenn nicht, dann bleibt auch das so. Bis ich exakt den selben Text erneut kopiere. Dann sind die Karten neu gemischt und es wird klappen oder eben nicht, meist jedoch schon... :eyecrazy:
Th69 - Sa 23.05.20 08:26
Guten Morgen,
sind das dann beides Fehlermeldungen vom System oder sind das deine eigenen (per
ShowMessage(E.Message))?
Und bei dem
DivideByZero-Testprogramm verhält es sich anders, d.h. da wird direkt der
except-Block angesprungen?
Du könntest auch einfach mal selber
ErrorProc auf eine eigene Prozedur umleiten und dann nur folgenden Code dort verwenden:
Delphi-Quelltext
1: 2: 3: 4: 5: 6:
| var E : Exception; begin E := Exception.Create('Test-Exception'); Raise E at Address,Frame; end; |
Und wenn der dann gefangen wird (ohne Dialog), dann temporär vor dem Clipboard-Aufruf diese
ErrorProc-Prozedur verwenden und nachher wieder auf die ursprüngliche zurücksetzen.
galagher - Sa 23.05.20 08:38
Guten Morgen!
Th69 hat folgendes geschrieben : |
sind das dann beides Fehlermeldungen vom System oder sind das deine eigenen (per ShowMessage(E.Message))? |
Beides sind Fehlermeldungen vom System.
Th69 hat folgendes geschrieben : |
Und bei dem DivideByZero-Testprogramm verhält es sich anders, d.h. da wird direkt der except-Block angesprungen? |
Ja, ausserhalb der IDE schon. Mit der IDE kommt zuerst eine SIGFPE-Meldung und dann das
except. Bei dem
IntVar := pIntPointer^-Test verhält es sich ebenso, nur dass da mit der IDE eben eine SIGSEGV-Meldung kommt.
Th69 hat folgendes geschrieben : |
Du könntest auch einfach mal selber ErrorProc auf eine eigene Prozedur umleiten und dann nur folgenden Code dort verwenden:
Delphi-Quelltext 1: 2: 3: 4: 5: 6:
| var E : Exception; begin E := Exception.Create('Test-Exception'); Raise E at Address,Frame; end; |
Und wenn der dann gefangen wird (ohne Dialog), dann temporär vor dem Clipboard-Aufruf diese ErrorProc-Prozedur verwenden und nachher wieder auf die ursprüngliche zurücksetzen. |
Das verstehe ich einfach nicht!
galagher - Sa 23.05.20 11:36
Ok, ich habe ErrorProc nach dem Vorbild von sysutils.inc in meinen Code eingebaut, die Deklaration ErrorProc:=@RunErrorToExcept; habe ich in den initialization-Abschnitt von RichMemo.pas gesetzt. Deinen Code habe ich dann in Prozedur RunErrorToExcept eingefügt.
Ergebnis: Unverändert, alles ist wie vorher. :? RunErrorToExcept wird nicht erreicht.
Th69 - Sa 23.05.20 12:37
Vllt. ist die Zuweisung zu früh (und sie wird von der anderen Initialisierung überschrieben).
Setze es mal direkt vor dem try.
galagher - So 24.05.20 18:59
Th69 hat folgendes geschrieben : |
Vllt. ist die Zuweisung zu früh (und sie wird von der anderen Initialisierung überschrieben).
Setze es mal direkt vor dem try. |
Welches
try und welche Zuweisung genau meinst du:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9:
| try for i := 0 to n-1 do begin aStreams[i] := TMemoryStream.Create; aFormats[i] := ClipBoard.Formats[i]; ClipBoard.GetFormat(aFormats[i], aStreams[i]); |
galagher - Mo 25.05.20 21:03
Der SIGSEGV-Fehler tritt nur bei Zwischenablagendaten von meinen beiden alten Programmen auf, bei Zwischenablagendaten anderer aktueller Programme funktioniert es ja problemlos, also akzeptiere ich das erstmal so.
Wie ich heute festgestellt habe, funktioniert ClipBoard.Clear nicht. TClipBoard ist offenbar ein wenig buggy...
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2024 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!