Autor |
Beitrag |
Mathematiker
Beiträge: 2622
Erhaltene Danke: 1447
Win 7, 8.1, 10
Delphi 5, 7, 10.1
|
Verfasst: Fr 22.05.15 17:40
Hallo,
in einer Funktion zum Laden temporärer Schriftarten gebe ich true zurück, wenn alles problemlos funktionierte.
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:
| function fontladen(const name:string;i:integer):boolean; var ms1: TResourcestream; ms: TMemoryStream; bib :HINST; cn: dword; begin bib := LoadLibraryex(pchar('.....dll'),0,LOAD_LIBRARY_AS_DATAFILE); try ms1 := TResourceStream.Create(bib,name,'RT_RCDATA'); try ms:= TMemoryStream.Create; try ms.CopyFrom(ms1, 0); fonthandle[i]:=AddFontMemResourceEx(ms.Memory, ms.Size, nil, @cn); result:=true; finally ms.Free; end; finally ms1.Free; end; finally FreeLibrary(bib); end; end; |
fonthandle ist korrekt definiert und es gibt auch keine Probleme.
Da ich aber im Falle eines Fehlers zur Laufzeit reagieren möchte, habe ich oben result auf false gesetzt. Beim Übersetzen meldet mir Delphi 5 nun aber an dieser Stelle
Zitat: | Auf 'fontladen' zugewiesener Wert wird niemals benutzt |
Kann ich sicher sein, dass result zu Beginn der Funktion automatisch false ist, also im Fehlerfall auch zurückgegeben wird? Ich vermute, dass das nicht so ist. Aber wie kann ich dann im Fehlerfall reagieren?
Wahrscheinlich stelle ich mich wieder ziemlich blöd an.
Danke für Hinweise und beste Grüße
Mathematiker
_________________ Töten im Krieg ist nach meiner Auffassung um nichts besser als gewöhnlicher Mord. Albert Einstein
|
|
Perlsau
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Fr 22.05.15 17:57
Daß der Compiler meckert, der zugewiesene Wert werde niemals benutzt, ist korrekt, denn du überschreibst ihn ja, ohne in zuvor anderweitig einzusetzen. Da es sich nicht um eine Fehlermeldung handelt, funktioniert dein Code damit auch genau so, wie du es erwartest. Wenn du dich dagegen an diesem Hinweis/dieser Warnung störst, setzt du einen Try-Except-Block ein, wo du im Exception-Fall Result auf False setzt.
Meiner Kenntnis und Erfahrung nach gehören die beiden Create-Anweisungen nicht in den Try-Finally-Block, denn wenn der Create-Befehl mißlingt, wäre es unsinnig, im Finally ein Free auszuführen. Ich würde daher mit einem Try-Except-Block beginnen:
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:
| function fontladen(const name:string;i:integer):boolean; var ms1: TResourcestream; ms: TMemoryStream; bib :HINST; cn: dword; begin Try bib := LoadLibraryex(pchar('.....dll'),0,LOAD_LIBRARY_AS_DATAFILE); ms1 := TResourceStream.Create(bib,name,'RT_RCDATA'); ms := TMemoryStream.Create; Try ms.CopyFrom(ms1, 0); fonthandle[i]:= AddFontMemResourceEx(ms.Memory, ms.Size, nil, @cn); Result := true; Finally ms.Free; ms1.Free; FreeLibrary(bib); End; Except on e:exception Do Begin Result := False; ShowMessage(e.message); End; End; end; |
Nachtrag: Den Bezeichner Name sollte man wenn möglich vermeiden, denn der ist bereits als Property Name der Form (self.name) vergeben ... könnte hin & wieder zu Mißverständnissen führen.
Nachnachtrag: Nicht initialisierte Variablen enthalten einen Zufallswert.
Für diesen Beitrag haben gedankt: Mathematiker
|
|
Mathematiker
Beiträge: 2622
Erhaltene Danke: 1447
Win 7, 8.1, 10
Delphi 5, 7, 10.1
|
Verfasst: Fr 22.05.15 18:04
Hallo Perlsau,
Danke für die Antwort. Genau das habe ich gesucht.
Perlsau hat folgendes geschrieben : | Meiner Kenntnis und Erfahrung nach gehören die beiden Create-Anweisungen nicht in den Try-Finally-Block, denn wenn der Create-Befehl mißlingt, wäre es unsinnig, im Finally ein Free auszuführen. Ich würde daher mit einem Try-Except-Block beginnen: |
Ist logisch, d.h. hier werde ich deinen Vorschlag übernehmen. Das gilt auch für die Verwendung von "name".
Danke und beste Grüße
Mathematiker
_________________ Töten im Krieg ist nach meiner Auffassung um nichts besser als gewöhnlicher Mord. Albert Einstein
|
|
OlafSt
Beiträge: 486
Erhaltene Danke: 99
Win7, Win81, Win10
Tokyo, VS2017
|
Verfasst: Sa 23.05.15 01:46
Hmmm... Zuerst wird empfohlen, keine Creates in einem Try-Block zu machen und dann im gleichen Atemzug stehts im Code. Ich würds so machen:
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:
| function fontladen(const name:string;i:integer):boolean; var ms1: TResourcestream; ms: TMemoryStream; bib :HINST; cn: dword; begin bib := LoadLibraryex(pchar('.....dll'),0,LOAD_LIBRARY_AS_DATAFILE); ms1 := TResourceStream.Create(bib,name,'RT_RCDATA'); ms := TMemoryStream.Create; Try ms.CopyFrom(ms1, 0); fonthandle[i]:= AddFontMemResourceEx(ms.Memory, ms.Size, nil, @cn); Result := true; except on e:exception Do Begin Result := False; ShowMessage(e.message); End; End; ms.Free; ms1.Free; FreeLibrary(bib); end; |
_________________ Lies, was da steht. Denk dann drüber nach. Dann erst fragen.
|
|
Mathematiker
Beiträge: 2622
Erhaltene Danke: 1447
Win 7, 8.1, 10
Delphi 5, 7, 10.1
|
Verfasst: Sa 23.05.15 08:07
Hallo OlafSt,
mein Problem ist, dass ich die Wirkungsweise von try-except und try-finally wahrscheinlich noch nicht ganz verstanden habe.
OlafSt hat folgendes geschrieben : | Hmmm... Zuerst wird empfohlen, keine Creates in einem Try-Block zu machen und dann im gleichen Atemzug stehts im Code. |
Perlsau hatte abgeraten try-finally zu verwenden und dafür try-except empfohlen. Dann müsste es doch korrekt sein.
Was geschieht bei deinem Vorschlag
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8:
| ... bib := LoadLibraryex(pchar('.....dll'),0,LOAD_LIBRARY_AS_DATAFILE); ms1 := TResourceStream.Create(bib,name,'RT_RCDATA'); ms := TMemoryStream.Create; ... ms.Free; ms1.Free; FreeLibrary(bib); |
wenn ich die DLL nicht laden kann oder es Fehler beim Streamerzeugen gibt?
Müsste es dann nicht einen großen "Knall" geben. Ich weiß es nicht.
Beste Grüße
Mathematiker
_________________ Töten im Krieg ist nach meiner Auffassung um nichts besser als gewöhnlicher Mord. Albert Einstein
|
|
Perlsau
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Sa 23.05.15 08:07
OlafSt hat folgendes geschrieben : | Hmmm... Zuerst wird empfohlen, keine Creates in einem Try-Block zu machen |
Das ist nicht ganz richtig: Ich hatte empfohlen, keine Creates im Try- Finally-Block unterzubringen.
Gerade eben habe ich mit Delphi 2009 folgenden Test durchgeführt:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17:
| function TFormMain.TryFinallyTest(Var Datei: String): Boolean; Var FStream : TFileStream; begin FStream := TFileStream.Create(Datei,fmOpenRead); Try Result := FileExists(Datei); except on e:exception Do Begin Datei := 'Datei existiert nicht'; Result := False; End; End; FStream.Free; ShowMessage('FStream wurde freigegeben'); end; |
Wenn hier z.B. ein Dateiname übergeben wird, der auf eine Datei verweist, die nicht existiert, werden die Anweisungen, die auf den Try-Except-Block folgen, nicht ausgeführt. In diesem Fall ist bereits das Erzeugen des Streams mißlungen. Das ist ja soweit nicht schlimm, denn ein Stream, der nicht erzeugt wurde, muß auch nicht freigegeben werden. Jetzt haben wir aber in des Mathematikers Code mehrere Creates. Ich simuliere das in meinem Beispiel mit einer Stringliste, die noch vor dem Create des Filestreams erzeugt wird:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22:
| function TFormMain.TryFinallyTest(Var Datei: String): Boolean; Var Liste : TStringList; FStream : TFileStream; begin Liste := TStringList.Create; FStream := TFileStream.Create(Datei,fmOpenRead); Try FStream.Position := 0; Liste.LoadFromStream(FStream); Result := Liste.Count > 0; except on e:exception Do Begin Datei := 'Datei existiert nicht'; Result := False; End; End; FStream.Free; Liste.Free; ShowMessage('FStream und Liste wurden freigegeben'); end; |
Das Resultat, wenn die Datei nicht existiert:
An unexpected memory leak has occurred. The unexpected small block leaks are:
Somit stellt der Kommentar in deinem Code, der zeigen soll, wie man's richtig macht, eine gewisse Irreführung dar, denn dort geht es nach einem Fehler eben nicht weiter. Die Funktion wird im Gegenteil dort abgebrochen, wo der Fehler auftritt bzw. wo die Exception ausgelöst wird.
Mein Code, den ich Mathematiker empfahl, ist aber – das gestehe ich freimütig ein – auch nicht korrekt, denn wenn dort z.B. ms := TMemoryStream.Create fehlschlägt, werden die davor erzeugten Objekte nicht mehr freigegeben. In meinem Beispiel hier funktioniert das jedoch so, wie es sollte:
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:
| function TFormMain.TryFinallyTest(Var Datei: String): Boolean; Var Liste : TStringList; FStream : TFileStream; begin Try Liste := TStringList.Create; FStream := TFileStream.Create(Datei,fmOpenRead); Try FStream.Position := 0; Liste.LoadFromStream(FStream); Result := Liste.Count > 0; Finally FStream.Free; Liste.Free; ShowMessage('FStream und Liste wurden freigegeben'); End; Except on e:exception Do Begin Liste.Free; Datei := e.Message; Result := False; ShowMessage('Liste wurden freigegeben'); End; End; end; |
Zuletzt bearbeitet von Perlsau am Sa 23.05.15 08:49, insgesamt 1-mal bearbeitet
Für diesen Beitrag haben gedankt: Mathematiker
|
|
Perlsau
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Sa 23.05.15 08:17
Moin, Mathe-Genie,
Kannst du mal testen, was passiert, wenn du das letzte Beispiel, das ich oben gepostet hatte, auf deine Situation überträgst und z.B. das Erzeugen von ms1 oder m1 fehlschlägt? Ich habe es ja mit meinem TFileStream leicht, einen Fehler zu erzeugen, indem ich einfach eine nicht existierende Datei übergebe. Leider hab ich keine Ahnung, was es bei dir mit LoadLibrary und TResourceStream auf sich hat. Beim Erzeugen eines MemoryStreams kann im Grunde nicht viel schieflaufen. Den würde ich daher als erstes erzeugen.
Du meinstest, daß "du die Wirkungsweise von try-except und try-finally wahrscheinlich noch nicht ganz verstanden" hast.
Meiner Kenntnis nach wird bei in einem Try-Finally-Block der Finally-Block auf jeden Fall ausgeführt, egal ob ein Fehler auftrat oder nicht: Finally bedeutet abschließend, am Ende, zuletzt, zum Schluß usw.
In einem Try-Except-Block wird dagegen der Except-Block nur dann ausgeführt, wenn zwischen Try und Except ein Fehler auftrat.
In verschachtelten Try-Except- bzw. Try-Finally-Blöcken reagiert auch der äußere Block auch auf Fehler inneren Block. Das heißt, wenn ich innerhalb eines Try-Finally einen Try-Except platziere, und dort ein Fehler auftritt (eine Exception ausgelöst wird), wird dennoch der Finally-Block im äußeren Try-Finally ausgeführt bzw. umgekehrt ebenso.
Ich denke noch mal darüber nach, wie man deine Methode 100prozentig narrensicher macht.
Für diesen Beitrag haben gedankt: Mathematiker
|
|
Perlsau
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Sa 23.05.15 08:48
Ich glaub, ich hab's jetzt:
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:
| function fontladen(const name:string;i:integer):boolean; var ms1: TResourcestream; ms: TMemoryStream; bib :HINST; cn: dword; begin Try bib := LoadLibraryex(pchar('.....dll'),0,LOAD_LIBRARY_AS_DATAFILE); Try ms1 := TResourceStream.Create(bib,name,'RT_RCDATA');
Try ms := TMemoryStream.Create;
Try ms.CopyFrom(ms1, 0); fonthandle[i]:= AddFontMemResourceEx(ms.Memory, ms.Size, nil, @cn); Result := true; Finally ms.Free; ms1.Free; FreeLibrary(bib); End;
Except on e:exception Do Begin ms1.Free; Result := False; ShowMessage('Fehler bei TMemoryStream: ' + e.message); End; End;
Except on e:exception Do Begin FreeLibrary(bib); Result := False; ShowMessage('Fehler bei TResourceStream: ' + e.message); End; End;
Except on e:exception Do Begin Result := False; ShowMessage('Fehler bei LoadLibrary: ' + e.message); End; End; end; |
So müßte sich nach meinem Dafürhalten eigentlich alles in trockenen Tüchern befinden:
- Im ersten Try-Except-Block wird versucht, dieses Loadlibrary auszuführen. Schlägt das fehl, wird die entsprechende Fehlermeldung ausgegeben und nichts freigegeben, weil ja noch nichts erzeugt wurde.
- Im zweiten Try-Except-Block wird versucht, den Resourcestream zu erzeugen. Schlägt das fehl, muß zumindest LoadLibrary freigegeben werden.
- Im dritten Try-Except-Block wird versucht, den Memorystream zu erzeugen. Schlägt das fehl, müssen LoadLibrary und Resourcestream freigegeben werden.
- Im innersten Try-Finally-Block sind bereits alle Objekte erzeugt und müssen daher auch freigegeben werden.
- Tritt ein Fehler in diesem innersten Block auf, werden aber auch wieder die äußeren Exception-Blöcke ausgeführt. Da beißt sich die Katze in den bereits abgebissenen Schwanz ...
Für diesen Beitrag haben gedankt: Mathematiker
|
|
Delphi-Laie
Beiträge: 1600
Erhaltene Danke: 232
Delphi 2 - RAD-Studio 10.1 Berlin
|
Verfasst: Sa 23.05.15 10:37
Finally wird in der Tat immer ausgeführt. Doch warum an allen Stellen so kompliziert? bib müßte ein Handle sein, und das würde ich auswerten (if bib<>INVALID_HANDLE_VALUE then...).
Für diesen Beitrag haben gedankt: Mathematiker
|
|
Mathematiker
Beiträge: 2622
Erhaltene Danke: 1447
Win 7, 8.1, 10
Delphi 5, 7, 10.1
|
Verfasst: So 24.05.15 12:08
Hallo Perlsau,
vielen Dank für deine ausführliche Hilfe.
Perlsau hat folgendes geschrieben : | Leider hab ich keine Ahnung, was es bei dir mit LoadLibrary und TResourceStream auf sich hat. |
Die ttf-Dateien der nicht zum Standard-Windows gehörenden (selbst gebastelten) Schriftarten liegen als RT_RCDATA in einer DLL.
In einigen Programmteilen des Matheprogramms (z.B. Menüpunkt Arithmetik/Zahlzeichen) benötige ich diese aber.
Deshalb werden die Daten aus der Ressourcen-DLL geladen und temporär installiert. Deshalb "TResourceStream".
Danke auch für die Erklärung von Try-Finally und Try-Except.
Perlsau hat folgendes geschrieben : | Ich denke noch mal darüber nach, wie man deine Methode 100prozentig narrensicher macht. |
Und deine Lösung ist super. Ich habe mal probeweise die DLL "entfernt", eine nicht vorhandene Schriftart "gerufen" und die Streams vor der weiteren Verwendung "gelöscht". Jedes Mal erhalte ich, wie es sein sollte, die entsprechende Fehlermeldung.
Damit dürfte die Methode sicher sein.
Delphi-Laie hat folgendes geschrieben : | Finally wird in der Tat immer ausgeführt. Doch warum an allen Stellen so kompliziert? bib müßte ein Handle sein, und das würde ich auswerten (if bib<>INVALID_HANDLE_VALUE then...). |
Danke für den Hinweis. Geht sicher auch. Perlsaus Lösung ist aber optimal, d.h. ich werde diese übernehmen.
Mit besten Grüßen
Mathematiker
_________________ Töten im Krieg ist nach meiner Auffassung um nichts besser als gewöhnlicher Mord. Albert Einstein
|
|
Perlsau
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: So 24.05.15 12:49
Moin, das Prinzip "meiner" Lösung ist eigentlich ganz einfach zu verstehen:
- Erstes Objekt in einem Try-Except-Block erstellen, bei Mißlingen Fehlermeldung ausgeben, und weil nichts erzeugt wurde, muß auch kein FreeLibrary (oder wie das heißt, ich tipp das gerade aus dem Stegreif) ausgeführt werden.
- Zweites Objekt in einem Try-Except-Block erstellen, bei Mißlingen wurde das erste ja bereits erzeugt, also das erste freigeben und Fehlermeldung ausgeben.
- Drittes Objekt in einem Try-Except-Block erstellen, bei Mißlingen wurden das erste und das zweite bereits erzeugt, also das 1+2 freigeben und Fehlermeldung ausgeben.
- Im innersten Try-Finally sind alle Erzeugungen gelungen, also müssen alle wieder freigegeben werden.
Ganz sicher war ich mir aber nicht, daher ist es schön, nun diese Rückmeldung lesen zu können. Teste ruhig mal alle nur erdenklichen Möglichkeiten durch, insbesondere solltest du auch mal einen Fehler im inneren Try-Finally erzeugen. Ich teste allermeist bei meinen eigenen Projekten übergründlich, auf daß mir da nur ja kein häßlicher Bug durch die Finger rutscht
|
|
mandras
Beiträge: 429
Erhaltene Danke: 107
Win 10
Delphi 6 Prof, Delphi 10.4 Prof
|
Verfasst: So 24.05.15 16:26
Nochmal zur ursprünglichen Frage:
"Auf 'fontladen' zugewiesener Wert wird niemals benutzt"
Dieser Warnhinweis des Compilers ist, hm.. wirklich nur ein Hinweis, daß es so sein könnte.
Der Compiler geht von einer linearen Abarbeitung der Statements aus und berücksichtigt die try-Blöcke nicht.
(selbst ohne try-Blöcke könnte der Funktionsaufruf von fontladen innerhalb eines try-Blocks erfolgen,
auch in diesem Fall wäre der Warnhinweis nicht korrekt).
|
|
Perlsau
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: So 24.05.15 17:44
mandras hat folgendes geschrieben : | Nochmal zur ursprünglichen Frage: "Auf 'fontladen' zugewiesener Wert wird niemals benutzt" |
Ursprüngliche Frage? Ich lese dort was ganz anderes:
"Kann ich sicher sein, dass result zu Beginn der Funktion automatisch false ist, also im Fehlerfall auch zurückgegeben wird?"
Da das der Satz mit dem Fragezeichen ist, ging ich davon aus, daß genau dies die Frage war, die gestellt wurde. Auch die Überschrift weist auf diese Frage hin: Ist der Rückgabewert definiert oder nicht? Und in der Tat hatte Mathematiker mir oben eindeutig bestätigt, daß es genau das war, was er gesucht hatte.
mandras hat folgendes geschrieben : | Der Compiler geht von einer linearen Abarbeitung der Statements aus und berücksichtigt die try-Blöcke nicht. |
Wer hat dir denn das erzählt? Das wäre mir absolut neu – aber man lernt ja nie aus, deshalb warte ich mal ab, ob sich das am Ende nicht doch noch bewahrheitet ...
Wäre der Rückgabewert der Function undefiniert, würde der Compiler genau diesen Umstand als Warnung ausgeben. Davon habe ich jedoch nichts lesen können.
Wenn du einen Try-Except-Block hast und dort zwischen Try und Except als letztes True zuweist, im Exception-Abschnitt dagegen False, ist der Rückgabewert auf jeden Fall definiert. Ich habe noch nie erlebt, daß der Compiler hier meckern würde, der Rückgabewert wäre nicht definiert. Wenn du das anders erlebt hast – was ja durchaus im Bereich des Möglichen wäre, ich habe ja nicht mit allen Delphi-Versionen Erfahrungen sammeln können –, würde ich mich darüber freuen, wenn du ein Beispiel unter Angabe deiner Delphi-Version postest, anhand dessen man testen kann, daß der Compiler hier eventuell fehlende Zuweisung bemängelt.
Anders sähe es aus, wenn Result := False im Exception-Abschnitt fehlen würde. Man könnte in diesem Fall auch, wie Mathematiker das anfangs gelöst hatte, Result gleich am Anfang der Function auf False setzen. Dann käme aber wieder die Meldung, daß der Wert nirgends benutzt wird, denn er wird ja im Try-Block überschrieben, bevor irgend was anderes damit angestellt wird. Und richtigerweise hat Mathematiker oben auch geschrieben, daß dort der Warnhinweis entsteht (wird ja im Meldungsfenster mit Zeilennummer angezeigt).
mandras hat folgendes geschrieben : | (selbst ohne try-Blöcke könnte der Funktionsaufruf von fontladen innerhalb eines try-Blocks erfolgen,
auch in diesem Fall wäre der Warnhinweis nicht korrekt). |
Ob es (in diesem Fall) wirklich Sinn macht, die Fehlerbehandlung in der aufrufenden Methode und nicht in der aufgerufenen Methode, wo der Fehler tatsächlich entstehen kann, zu platzieren, vermag ich im Moment nicht mit Sicherheit zu behaupten, bezweifle es aber dennoch (falls es das war, was du beschreiben wolltest). Ich selbst behandle Fehler immer genau dort, wo die Gefahr lauert und habe die von dir vorgeschlagene Variante in der Tat noch nicht ausprobiert. Das hat auch den Vorteil, daß ich nicht in jeder aufrufenden Methode, die diese Function aufruft, eine Fehlerbehandlung unterbringen muß.
|
|
Mathematiker
Beiträge: 2622
Erhaltene Danke: 1447
Win 7, 8.1, 10
Delphi 5, 7, 10.1
|
Verfasst: So 24.05.15 19:35
Hallo,
mandras hat folgendes geschrieben : | Dieser Warnhinweis des Compilers ist, hm.. wirklich nur ein Hinweis, daß es so sein könnte. |
Schön, könnte so sein. Ich weiß es nicht.
Ein solchen Warnhinweis könnte ich vielleicht ignorieren, wenn es um das Laden z.B. einer Bitmap geht. Es wäre nicht schön aber nach dem Schließen des Programms alles noch in Ordnung.
Bei den Schriftarten ist das leider ein größeres Problem. Gerade in der ersten Testphase (vor längerer Zeit!) sorgten kleinere und größere Fehler für fehlerhafte Installationen von Schriftarten, die erst nach einem Windows-Neustart per Hand entfernt werden konnten. Und einem Programmanwender bei einem Fehler einen Neustart des Rechners zuzumuten, dürfte wohl untragbar sein.
Deshalb möchte ich absolut sicher gehen. Dafür muss ich aber wissen, ob korrekt installiert wurde oder irgendein Fehler auftrat. Daher meine Frage.
Und Perlsaus Lösung ist genau das, was ich suche.
Beste Grüße
Mathematiker
_________________ Töten im Krieg ist nach meiner Auffassung um nichts besser als gewöhnlicher Mord. Albert Einstein
|
|
Martok
Beiträge: 3661
Erhaltene Danke: 604
Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
|
Verfasst: So 24.05.15 23:28
Falls die Ausgangsfrage war: wird Result auf irgendwas initialisiert. Antwort: nein, und je nach Optimierungsgrad kann da sogar der letzte Parameter drin stehen (nämlich dann, wenn Result komplett im Register gehalten wird, dann ist das nämlich EAX, was auch für Parameter genutzt wird. Da bin ich schonmal drüber geflogen) Dein Erster Code ist formal schon korrekt gewesen und ist so auch an manchen Stellen in der VCL zu finden.
Ich bin ja immer noch der Meinung, dass diese Warnung an der Stelle ein Compilerbug ist. Eigentlich kommt die ja, wenn jeder mögliche Codepfad dazu führt, dass die Variable überschrieben wird -- ist hier aber nicht so, alles was eine Exception wirft hat geht andere Wege. Lustigerweise merkt der Optimizer das ja auch und optimiert die erste Zuweisung nicht raus.
Und was die Try/Finally angeht: eigentlich müsstest du das für jedes Objekt einzeln paaren (so wie dein erster Code). Wird dann allerdings etwas länglich, weswegen man das oft gruppiert. Ist natürlich nicht so sicher, aber wenn die einzige erwartete Exception bei den Konstruktoren ein EOutOfMemory ist, kann man das meistens ignorieren. Das überleben die wenigsten Anwendungen, dann kommts auf ein Leak mehr oder weniger auch nicht mehr an
_________________ "The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."
Für diesen Beitrag haben gedankt: Mathematiker
|
|
mandras
Beiträge: 429
Erhaltene Danke: 107
Win 10
Delphi 6 Prof, Delphi 10.4 Prof
|
Verfasst: Mo 25.05.15 00:02
Unter Berücksichtigung nicht nur des Schlüsselelements ("?") habe ich den Sachverhalt wie folgt interpretiert:
1- Mathematiker will eine Funktion programmieren, die true liefert wenn alles korrekt lief und false wenn ein Fehler auftrat.
2- Ursprünglich war Zeile 7 seines Codes nicht auskommentiert, es erfolgte der von ihm genannte Warnhinweis des Compilers.
3- Dann hat Mathematiker Zeile 7 auskommentiert, der Warnhinweis war weg. Dann bleibt aber die Frage ob das Funktionsergebnis definiert ist, wenn im Code keine Zuweisung erfolgt
Punkt 3 ist ja geklärt. Delphi führt keine eigene Initialisierung des Funktionsergebnisses durch
Zu Punkt 2:
Perlsau kritisiert folgende meiner Aussagen:
>Der Compiler geht von einer linearen Abarbeitung der Statements aus und berücksichtigt die try-Blöcke nicht.
Dabei bleibe ich. Hat mir auch niemand erzählt. Die Aussage basiert auf Beobachtung, anders läßt sich das Verhalten des Compilers im angesprochenen Fall kaum erklären.
Nehmen wir einmal folgenden Code (getestet unter D6, mit Optimierungen an, neuere Versionen könnten es evtl. anders machen):
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
| function test:boolean; var j,k:integer; *begin result:=true; * j:=0; * try * k:=4 div j; * Result:=false; finally * if k>5 then j:=3; end; *end; |
Mit * markiert: Statements die in Maschinencode übernommen werden
Was sieht man? Ja, Result := false wird in der Realität niemals ausgeführt obwohl dafür Maschinencode generiert wird. Eigentlich sollte die Funktion immer true liefern. Wird sie wohl aber nicht, da das betr. Statement nicht codiert.
Jetzt wird's witzig:
Wenn man statt eines finally ein except verwendet codiert nun auch die Zeile "Result := true".
Während ich dies hier tippselte hat Martok noch einen Kommentar hinzugefügt.
Er schrieb: "Lustigerweise merkt der Optimizer das ja auch und optimiert die erste Zuweisung nicht raus. "
Ich war der Meinung, daß ich dies auch einmal so gesehen hatte,
bei meinen jetzigen Tests konnte ich das nicht reproduzieren, nur bei try-except wird die erste Zuweisung codiert.
Daher gehe ich in Richtung Martoks Kommentar - man könnte hier durchaus von einem Compilerbug sprechen
Perlsau schrieb noch:
> Wäre der Rückgabewert der Function undefiniert, würde der Compiler genau diesen Umstand als Warnung ausgeben.
Stimmt auch nicht immer:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9:
| function test:boolean; var j:integer; begin j:=0; try Result := (4 div j) > 4; finally end; end; |
Meldet keine Fehler. Dennoch ist der Rückgabewert undefiniert.
Noch etwas interessantes:
Wird im ursprünglichen Beispiel von Mathematiker die Zeile 7 (result:=false;) hinter das TRY in Zeile 9 verschoben erfolgt keine Compilerwarnung. Und das Statement codiert.
Alles in allem - alles nicht so einfach. Ich würde manches davon auch als Compilerbug bezeichnen.
Für diesen Beitrag haben gedankt: Mathematiker
|
|
Mathematiker
Beiträge: 2622
Erhaltene Danke: 1447
Win 7, 8.1, 10
Delphi 5, 7, 10.1
|
Verfasst: Mo 25.05.15 11:26
Hallo,
mandras hat folgendes geschrieben : | Alles in allem - alles nicht so einfach. |
Den Eindruck habe ich mittlerweile auch und bin verwirrt.
Offensichtlich ist das Problem wesentlich komplexer, als ich gedacht hatte.
mandras hat folgendes geschrieben : | Ich würde manches davon auch als Compilerbug bezeichnen. |
Das wird es dann wohl sein, denn das Verschieben von result:=false, wie von dir beschrieben, in die Zeile 9 beseitigt auch bei Delphi 5 die Warnung. Hätte ich dies gleich so gemacht, wäre ich nie auf die Frage gekommen.
Auf jeden Fall muss ich mich wohl von dem "Glauben" verabschieden, dass der Compiler stets korrekt arbeitet.
Vielen Dank für die Anmerkungen.
Beste Grüße
Mathematiker
_________________ Töten im Krieg ist nach meiner Auffassung um nichts besser als gewöhnlicher Mord. Albert Einstein
|
|
jaenicke
Beiträge: 19273
Erhaltene Danke: 1740
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Mo 25.05.15 12:05
Mathematiker hat folgendes geschrieben : | Auf jeden Fall muss ich mich wohl von dem "Glauben" verabschieden, dass der Compiler stets korrekt arbeitet. |
Bezogen auf deinen ursprünglichen Quelltext oben ist die Meldung doch korrekt. Durch das try..finally kommt es auf jeden Fall zur Zuweisung an Result im innersten try es sei denn es tritt eine Exception auf. Wenn aber eine Exception auftritt, ist die Zuweisung nicht nötig, da sie ohnehin nie ausgewertet wird.
Das Ergebnis von LoadLibraryEx würde ich aber auswerten und das FreeLibrary nur ausführen, wenn das auch erfolgreich war.
Für diesen Beitrag haben gedankt: Mathematiker
|
|
SMO
Beiträge: 120
Erhaltene Danke: 18
D2005 Personal
|
Verfasst: Do 28.05.15 14:36
mandras hat folgendes geschrieben : | Die Aussage basiert auf Beobachtung, anders läßt sich das Verhalten des Compilers im angesprochenen Fall kaum erklären.
Nehmen wir einmal folgenden Code (getestet unter D6, mit Optimierungen an, neuere Versionen könnten es evtl. anders machen):
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
| function test:boolean; var j,k:integer; *begin result:=true; * j:=0; * try * k:=4 div j; * Result:=false; finally * if k>5 then j:=3; end; *end; |
Mit * markiert: Statements die in Maschinencode übernommen werden
Was sieht man? Ja, Result := false wird in der Realität niemals ausgeführt obwohl dafür Maschinencode generiert wird. Eigentlich sollte die Funktion immer true liefern. Wird sie wohl aber nicht, da das betr. Statement nicht codiert. |
Das lässt sich ziemlich einfach erklären. Erstens ist der Compiler leider nicht in der Lage, besser zu optimieren (hier: Konstantenpropagierung), sonst würde er sich weigern den Code überhaupt zu compilieren (wegen Division durch 0). Zweitens ist es aus dieser Sicht vollkommen richtig, dass Zeile 4 beanstandet/ignoriert wird. Denn der Wert wird niemals benutzt. Er wird durch die zweite Zuweisung in Zeile 8 überschrieben. Die Tatsache, dass es vorher zu einer Exception kommt, ist irrelevant, denn wenn eine Funktion von einer Exception unterbrochen/beendet wird, dann ist der Rückgabewert der Funktion undefiniert/irrelevant!
mandras hat folgendes geschrieben : | > Wäre der Rückgabewert der Function undefiniert, würde der Compiler genau diesen Umstand als Warnung ausgeben.
Stimmt auch nicht immer:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9:
| function test:boolean; var j:integer; begin j:=0; try Result := (4 div j) > 4; finally end; end; |
Meldet keine Fehler. Dennoch ist der Rückgabewert undefiniert. |
Du verwechselst hier zwei unterschiedliche Sichten. Aus mathematischer Sicht ist der Wert undefiniert, weil Division durch 0 undefiniert. Aus Sicht des dummen Compilers ist der Rückgabewert sehr wohl definiert, nämlich als "(4 div j) > 4". Zur Laufzeit, wenn es hier zu einer Exception kommt, dann ist der Rückgabewert dieses Stücks Code tatsächlich undefiniert, eben genau wegen der Exception.
Mathematiker hat folgendes geschrieben : | Perlsaus Lösung ist aber optimal, d.h. ich werde diese übernehmen. |
Die Lösung der Perlsau ist alles andere als optimal.
Wie wäre es damit:
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:
| function fontladen(const name: string; i: integer): boolean; var rs: TResourceStream; bib: HMODULE; h: THandle; cn: dword; begin bib := LoadLibraryEx(PChar('.....dll'), 0, LOAD_LIBRARY_AS_DATAFILE); Result := bib <> 0; if Result then try try rs := TResourceStream.Create(bib, name, RT_RCDATA); h := AddFontMemResourceEx(rs.Memory, rs.Size, nil, @cn); rs.Free; Result := h <> 0; fonthandle[i] := h; except on E: EResNotFound do begin Result := False; ShowMessage(E.Message); end end finally FreeLibrary(bib); end else ShowMessage(SysErrorMessage(GetLastError)); end; |
Ich behaupte nicht, dass das optimal ist, aber es ist auf jeden Fall kompakter und übersichtlicher.
Ein paar Erläuterungen:
- Windows API-Funktionen wie LoadLibraryEx und AddFontMemResourceEx werfen (in der Regel) keine Exceptions, daher brauchen sie keine try-finally/except Blöcke.
- Wenn innerhalb des Konstruktors eines Delphi-Objekts eine Exception auftritt, wird automatisch der Destruktor aufgerufen und der Speicher wieder freigegeben. Soll heißen: ja, TResourceStream.Create kann eine Exception werfen, aber es ist unnötig diese abzufangen um ein rs.Free aufzurufen.
- TResourceStream ist ein Nachfahre von TMemoryStream, es ist unnötig hier einen extra TMemoryStream zu erzeugen und Daten herumzukopieren.
- RT_RCDATA ist eigentlich eine vordefinierte Konstante, kein String.
- Im Exception-Handler steht absichtlicht nicht "on E: Exception do", denn das würde alle Exceptions abfangen. Kann man natürlich machen, aber eigentlich sollte man nicht blind alle Exceptions verschlucken, sondern gezielt spezielle Fälle behandeln und unvorhersehbare eben durchlassen.
Für diesen Beitrag haben gedankt: mandras, Mathematiker, OlafSt
|
|
Mathematiker
Beiträge: 2622
Erhaltene Danke: 1447
Win 7, 8.1, 10
Delphi 5, 7, 10.1
|
Verfasst: Do 28.05.15 15:10
Hallo SMO,
Dank für die vielen Hinweise. Ich werde sie genauer ansehen.
Alerdings ...
SMO hat folgendes geschrieben : | RT_RCDATA ist eigentlich eine vordefinierte Konstante, kein String. |
ist das bei meinem Problem nicht korrekt.
Der TResourceStream-Konstruktor verlangt als letzten Parameter bei Delphi 5
Delphi-Quelltext 1:
| constructor Create(Instance: THandle; const ResName: string; ResType: PChar); | einen String.
Dein Vorschlag
Delphi-Quelltext 1:
| rs := TResourceStream.Create(bib, name, RT_RCDATA); | wird zwar problemlos compiliert, führt aber zu der Meldung, dass die Ressource nicht gefunden wird. RT_RCDATA ist wahrscheinlich (als Pchar?) vordefiniert, funktioniert aber nicht. Leider.
Beste Grüße
Mathematiker
_________________ Töten im Krieg ist nach meiner Auffassung um nichts besser als gewöhnlicher Mord. Albert Einstein
|
|
|