Autor Beitrag
Mathematiker
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 2622
Erhaltene Danke: 1447

Win 7, 8.1, 10
Delphi 5, 7, 10.1
BeitragVerfasst: Fr 22.05.15 17:40 
Hallo,
in einer Funktion zum Laden temporärer Schriftarten gebe ich true zurück, wenn alles problemlos funktionierte.
ausblenden 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
//  result:=false;                <-- hier ist das Problem
  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



BeitragVerfasst: 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:

ausblenden 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); // oder was auch immer du als Fehlerbehandlung benötigst ...
    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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 2622
Erhaltene Danke: 1447

Win 7, 8.1, 10
Delphi 5, 7, 10.1
BeitragVerfasst: Fr 22.05.15 18:04 
Hallo Perlsau,
Danke für die Antwort. Genau das habe ich gesucht.
user profile iconPerlsau hat folgendes geschrieben Zum zitierten Posting springen:
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
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 486
Erhaltene Danke: 99

Win7, Win81, Win10
Tokyo, VS2017
BeitragVerfasst: 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:
ausblenden 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
        //Tritt eine Exception auf,dann wird sie hier behandelt
        //Und wenn man nicht mit exit rausspringt...
        Result := False;
        ShowMessage(e.message);
      End;
    End;
    //...geht es hier ganz normal weiter.
    ms.Free;
    ms1.Free;
    FreeLibrary(bib);
end;

_________________
Lies, was da steht. Denk dann drüber nach. Dann erst fragen.
Mathematiker Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 2622
Erhaltene Danke: 1447

Win 7, 8.1, 10
Delphi 5, 7, 10.1
BeitragVerfasst: 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.
user profile iconOlafSt hat folgendes geschrieben Zum zitierten Posting springen:
Hmmm... Zuerst wird empfohlen, keine Creates in einem Try-Block zu machen und dann im gleichen Atemzug stehts im Code.

user profile iconPerlsau hatte abgeraten try-finally zu verwenden und dafür try-except empfohlen. Dann müsste es doch korrekt sein.
Was geschieht bei deinem Vorschlag
ausblenden 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



BeitragVerfasst: Sa 23.05.15 08:07 
user profile iconOlafSt hat folgendes geschrieben Zum zitierten Posting springen:
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:
ausblenden 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:

ausblenden 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:

ausblenden 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



BeitragVerfasst: 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



BeitragVerfasst: Sa 23.05.15 08:48 
Ich glaub, ich hab's jetzt:

ausblenden volle Höhe 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:
function fontladen(const name:string;i:integer):boolean;
var ms1: TResourcestream;
    ms: TMemoryStream;
    bib :HINST;
    cn: dword;
begin
  Try // LoadLibrary
    bib := LoadLibraryex(pchar('.....dll'),0,LOAD_LIBRARY_AS_DATAFILE); // wenn das schiefläuft, dann Exception auslösen

    Try // TResourceStream
     ms1 := TResourceStream.Create(bib,name,'RT_RCDATA');

     Try // TMemoryStream
       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 // TMemoryStream
        on e:exception Do
        Begin
          ms1.Free;         // und das wurde ebenfalls bereits erzeugt, FreeLibrary wird im nächstäußeren Exception-Block ausgeführt.
          Result := False;
          ShowMessage('Fehler bei TMemoryStream: ' + e.message);
        End;
     End;

    Except // TResourceStream
      on e:exception Do
      Begin
        FreeLibrary(bib); // das wurde ja bereits erzeugt
        Result := False;
        ShowMessage('Fehler bei TResourceStream: ' + e.message);
      End;
    End;

  Except // LoadLibrary
    on e:exception Do
    Begin
      Result := False;
      ShowMessage('Fehler bei LoadLibrary: ' + e.message);
      // nichts muß freigegeben werden, denn es wurde noch nichts erzeugt
    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
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 1600
Erhaltene Danke: 232


Delphi 2 - RAD-Studio 10.1 Berlin
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 2622
Erhaltene Danke: 1447

Win 7, 8.1, 10
Delphi 5, 7, 10.1
BeitragVerfasst: So 24.05.15 12:08 
Hallo Perlsau,
vielen Dank für deine ausführliche Hilfe.
user profile iconPerlsau hat folgendes geschrieben Zum zitierten Posting springen:
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.

user profile iconPerlsau hat folgendes geschrieben Zum zitierten Posting springen:
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.

user profile iconDelphi-Laie hat folgendes geschrieben Zum zitierten Posting springen:
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



BeitragVerfasst: 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 :o
mandras
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 429
Erhaltene Danke: 107

Win 10
Delphi 6 Prof, Delphi 10.4 Prof
BeitragVerfasst: 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



BeitragVerfasst: So 24.05.15 17:44 
user profile iconmandras hat folgendes geschrieben Zum zitierten Posting springen:
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.

user profile iconmandras hat folgendes geschrieben Zum zitierten Posting springen:
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 ... :gruebel:

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).

user profile iconmandras hat folgendes geschrieben Zum zitierten Posting springen:
(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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 2622
Erhaltene Danke: 1447

Win 7, 8.1, 10
Delphi 5, 7, 10.1
BeitragVerfasst: So 24.05.15 19:35 
Hallo,
user profile iconmandras hat folgendes geschrieben Zum zitierten Posting springen:
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 user profile iconPerlsaus 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
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 3661
Erhaltene Danke: 604

Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
BeitragVerfasst: 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 :lol:

_________________
"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
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 429
Erhaltene Danke: 107

Win 10
Delphi 6 Prof, Delphi 10.4 Prof
BeitragVerfasst: 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):

ausblenden 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:

ausblenden 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 2622
Erhaltene Danke: 1447

Win 7, 8.1, 10
Delphi 5, 7, 10.1
BeitragVerfasst: Mo 25.05.15 11:26 
Hallo,
user profile iconmandras hat folgendes geschrieben Zum zitierten Posting springen:
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.
user profile iconmandras hat folgendes geschrieben Zum zitierten Posting springen:
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. :lol:

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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Mo 25.05.15 12:05 
user profile iconMathematiker hat folgendes geschrieben Zum zitierten Posting springen:
Auf jeden Fall muss ich mich wohl von dem "Glauben" verabschieden, dass der Compiler stets korrekt arbeitet. :lol:
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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 120
Erhaltene Danke: 18


D2005 Personal
BeitragVerfasst: Do 28.05.15 14:36 
user profile iconmandras hat folgendes geschrieben Zum zitierten Posting springen:
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):

ausblenden 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!


user profile iconmandras hat folgendes geschrieben Zum zitierten Posting springen:
> Wäre der Rückgabewert der Function undefiniert, würde der Compiler genau diesen Umstand als Warnung ausgeben.
Stimmt auch nicht immer:

ausblenden 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.



user profile iconMathematiker hat folgendes geschrieben Zum zitierten Posting springen:
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:

ausblenden volle Höhe 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:
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;
      // if not Result then ShowMessage('AddFontMemResourceEx fehlgeschlagen...');
      fonthandle[i] := h;
    except
      on E: EResNotFound do
      begin
        Result := False;
        ShowMessage(E.Message);
      end
    end
  finally
    FreeLibrary(bib);
  end
  else // LoadLibraryEx fehlgeschlagen:
    ShowMessage(SysErrorMessage(GetLastError)); // Fehlermeldung anzeigen
    // oder Exception auslösen per RaiseLastOSError;
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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 2622
Erhaltene Danke: 1447

Win 7, 8.1, 10
Delphi 5, 7, 10.1
BeitragVerfasst: Do 28.05.15 15:10 
Hallo SMO,
Dank für die vielen Hinweise. Ich werde sie genauer ansehen.
Alerdings ...
user profile iconSMO hat folgendes geschrieben Zum zitierten Posting springen:
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
ausblenden Delphi-Quelltext
1:
constructor Create(Instance: THandle; const ResName: string; ResType: PChar);					
einen String.
Dein Vorschlag
ausblenden 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