Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi TPath.Combine, TPath.GetTempPath threadsicher? (https://www.delphipraxis.net/183287-tpath-combine-tpath-gettemppath-threadsicher.html)

zeras 28. Dez 2014 15:43

Delphi-Version: XE5

TPath.Combine, TPath.GetTempPath threadsicher?
 
Ich nutzte folgenden Code, um eine Resource in das Filesystem zu schreiben.
Da alles im Hintergrund laufen soll, habe ich das in einen Thread gepackt.
Leider bricht mir dann mein Tablet mit Meldung "Anwendung wurde beendet" nach einiger Zeit ab.
Ich habe dann testweise TPath.GetTempPath gegen den Pfad als String getauscht und nun scheint es zu laufen.
Ist denn TPath eine visuelle Komponente, oder wie kann man sich das Problem erklären?

Delphi-Quellcode:
//kopiert einen Song als Resource in eine Datei
Function CopyStream2File(ResourceID: string; var fName: String): boolean;
var
  ResStream: TResourceStream;

begin
  Result := False;

  ResStream := TResourceStream.Create(HInstance, ResourceID, RT_RCDATA);

  try

    //MP3 File aus Resource laden und in Filesystem ablegen
    fName := TPath.Combine(TPath.GetTempPath, 'tmp.mp3');
//    fName := TPath.Combine('/data/data/com.embarcadero.Meinprogram/files/', 'tmp.mp3');

    ResStream.Position := 0;
    ResStream.SaveToFile(fName);
    Result := True;
  finally
    ResStream.Free;
  end;
end;

himitsu 28. Dez 2014 21:37

AW: TPath.Combine, TPath.GetTempPath threadsicher?
 
TPath.GetTempPath selber ist threadsave, aber wie es mit MSDN-Library durchsuchenGetTempPath und MSDN-Library durchsuchenGetLongPathName aussieht, kann ich nicht sagen.

Aber warum kommt keiner auf die Idee diesen Wert in einer globalen Veriable zwischenzuspeichern
oder kann es denn passieren, daß sich dieser Wert jemals ändern wird? :stupid:



Und was sind das für Threads und wie behandelst du die Exceptions darin?


PS:
Warum ist fName ein VAR-Parameter?
Und Result=False wird es niemals geben.

Mavarik 29. Dez 2014 11:37

AW: TPath.Combine, TPath.GetTempPath threadsicher?
 
Zitat:

Zitat von himitsu (Beitrag 1284870)

PS:
Warum ist fName ein VAR-Parameter?
Und Result=False wird es niemals geben.

GlaskugelMode.Active := true;

Damit man außen in einem Log FName ausgeben kann.
Klar gibt es Result=false... bei einer Exception.

GlaskugelMode.Active := false;

zeras 29. Dez 2014 11:52

AW: TPath.Combine, TPath.GetTempPath threadsicher?
 
Zitat:

Zitat von himitsu (Beitrag 1284870)
Aber warum kommt keiner auf die Idee diesen Wert in einer globalen Veriable zwischenzuspeichern
oder kann es denn passieren, daß sich dieser Wert jemals ändern wird? :stupid:

Das war eine Funktion, die ich erst mal aus einem Beispiel genommen hatte. Die Idee, den Wert in eine globalen Variable zu speichern, ist gar nicht so schlecht, aber ich wollte erst mal das Beispiel zum Fliegen bringen. Danach wäre ich (oder vielleicht auch nicht) auf die Idee gekommen, das ganze zu optimieren.

Zitat:

Zitat von Mavarik (Beitrag 1284891)
Damit man außen in einem Log FName ausgeben kann.

Nicht ganz, aber ich möchte die Datei dann über den Mediaplayer abspielen und so brauche ich den Namen mit Pfadangabe.

Zitat:

Zitat von Mavarik (Beitrag 1284891)
Klar gibt es Result=false... bei einer Exception.

So ganz falsch ist doch dieser Teil nicht oder? Wenn ich den Stream nicht speichern kann, ist die Abfrage false und es wird dann in der aufrufenden Funktion nicht weiter gemacht. Es nützt ja nichts, wenn keine MP3 Datei da ist, dass ich dann den Mediaplayer anwerfe.

Mavarik 29. Dez 2014 12:18

AW: TPath.Combine, TPath.GetTempPath threadsicher?
 
Zitat:

Zitat von zeras (Beitrag 1284893)
Zitat:

Zitat von Mavarik (Beitrag 1284891)
Klar gibt es Result=false... bei einer Exception.

So ganz falsch ist doch dieser Teil nicht oder? Wenn ich den Stream nicht speichern kann, ist die Abfrage false und es wird dann in der aufrufenden Funktion nicht weiter gemacht. Es nützt ja nichts, wenn keine MP3 Datei da ist, dass ich dann den Mediaplayer anwerfe.

Nein, das ist richtig, aber ein Try Finally fängt keine Exception ab...
Also könntest Du außen die Exception abfangen und Result weg lassen oder must noch ein try except drum setzen.

Mavarik

Uwe Raabe 29. Dez 2014 12:24

AW: TPath.Combine, TPath.GetTempPath threadsicher?
 
Zitat:

Zitat von zeras (Beitrag 1284893)
Zitat:

Zitat von Mavarik (Beitrag 1284891)
Klar gibt es Result=false... bei einer Exception.

So ganz falsch ist doch dieser Teil nicht oder? Wenn ich den Stream nicht speichern kann, ist die Abfrage false und es wird dann in der aufrufenden Funktion nicht weiter gemacht. Es nützt ja nichts, wenn keine MP3 Datei da ist, dass ich dann den Mediaplayer anwerfe.

Eben nicht! Da die Exception innerhalb der Function nicht durch ein except abgefangen wird, gelangt sie an den nächsthöheren try-finally bzw. try-except Block. Damit wird die Auswertung der Funktionsrückgabe gar nicht ausgeführt.

Soll also bei einer auftretenden Exception lediglich ein false zurückgegeben werden, würde sich dieses Konstrukt anbieten:

Delphi-Quellcode:
//kopiert einen Song als Resource in eine Datei
Function CopyStream2File(ResourceID: string; var fName: String): boolean;
var
  ResStream: TResourceStream;

begin
  try
    ResStream := TResourceStream.Create(HInstance, ResourceID, RT_RCDATA);

    try

      //MP3 File aus Resource laden und in Filesystem ablegen
      fName := TPath.Combine(TPath.GetTempPath, 'tmp.mp3');

      ResStream.Position := 0;
      ResStream.SaveToFile(fName);
      Result := True;
    finally
      ResStream.Free;
    end;
  except
    result := false;
  end;
end;
Roter Kasten: Wie Mavarik korrekt bemerkt.

himitsu 29. Dez 2014 12:32

AW: TPath.Combine, TPath.GetTempPath threadsicher?
 
@GlaskugelMode:

Wie gerade schon jemand sagte:

Es gibt entweder True oder eine Exception.
Bei einer Exception wird mit der Panzerfaust reingeschossen, man fliegt an der Result-Rückgabe vorbei, direkt zum nächsten Try-Except und es gibt somit kein Result.

Im Prinzip funktioniert die gezeigte Lösung, auch wenn es nicht wirklich "optiomal" ist, wenn die Exception so abgefangen, dann einfach verworfen und in einen nichtssagenden Boolean verwandelt wird.

* Exception abfangen und als Boolean zurückgeben, dann aber bitte mit Zusatzinfox ala GetLastError
* oder als Fehlercode zurückgeben (die Fehler/Exception sprechend umwandeln)
* oder eben ohne Fehlerrückgabe-Wert und dafür mit Exceptions arbeiten

[add]
Lach .... die RedBox lebt mal wieder :thumb:

Mavarik 29. Dez 2014 12:34

AW: TPath.Combine, TPath.GetTempPath threadsicher?
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1284897)
Eben nicht!

eben doch :stupid:
Zum Beispiel so:

Delphi-Quellcode:
Function CopyStream2File(ResourceID: string; var fName: String): boolean;
var
   ResStream: TResourceStream;

begin
  Result := False;
   
  try

   ResStream := TResourceStream.Create(HInstance, ResourceID, RT_RCDATA);

   try

     //MP3 File aus Resource laden und in Filesystem ablegen
     fName := TPath.Combine(TPath.GetTempPath, 'tmp.mp3');
// fName := TPath.Combine('/data/data/com.embarcadero.Meinprogram/files/', 'tmp.mp3');

     ResStream.Position := 0;
     ResStream.SaveToFile(fName);
     Result := True;
   finally
     ResStream.Free;
   end;
  except
  end
end;
oder
Delphi-Quellcode:
var
  FName : String;
  error : boolean;
begin
  ...
  try
    error := CopyStream2File('42',FName);
  except
    MyError('Fehler...');
  end;

  if not Error then
    Play(FName)
 
end;

zeras 29. Dez 2014 12:36

AW: TPath.Combine, TPath.GetTempPath threadsicher?
 
Ich habe nun eure Meinungen verstanden und etwas dazugelernt.
Aber was hat es mit dem roten Kasten auf sich?

Mavarik 29. Dez 2014 12:38

AW: TPath.Combine, TPath.GetTempPath threadsicher?
 
Zitat:

Zitat von zeras (Beitrag 1284902)
Aber was hat es mit dem roten Kasten auf sich?

Immer wenn einer schneller war mit seinem Posting... Während Du noch geschrieben hast.
(Na, wer hatte Ihn dieses mal?)


So wäre wahrscheinlich die bevorzugte Lösung:
Delphi-Quellcode:
Function CopyStream2File(ResourceID: string): String;
var
    ResStream: TResourceStream;
begin
    ResStream := TResourceStream.Create(HInstance, ResourceID, RT_RCDATA);
    try
      //MP3 File aus Resource laden und in Filesystem ablegen
      Result := TPath.Combine(TPath.GetTempPath, 'tmp.mp3');

      ResStream.Position := 0;
      ResStream.SaveToFile(fName);
    finally
      ResStream.Free;
    end;
end;


var
   FName : String;
begin
   try
     FName := CopyStream2File('42');
     Play(FName);
   except
   end;
end;
Auch wenn ich es immer so mache wie in meinen 1. Posting...

Mavarik

Sir Rufo 29. Dez 2014 13:22

AW: TPath.Combine, TPath.GetTempPath threadsicher?
 
Sehr grausam, wie hier manche mit den Exceptions umgehen ... indem man versucht diese mit aller Gewalt zu umgehen :roll:

Das ist doch absoluter Schwachfug und führt einzig und alleine dazu, dass irgendwas nicht funktioniert, aber keiner merkt es.
Delphi-Quellcode:
Function CopyStream2File(ResourceID: string): String;
var
    ResStream: TResourceStream;
begin
    ResStream := TResourceStream.Create(HInstance, ResourceID, RT_RCDATA);
    try
      //MP3 File aus Resource laden und in Filesystem ablegen
      Result := TPath.Combine(TPath.GetTempPath, 'tmp.mp3');

      ResStream.Position := 0;
      ResStream.SaveToFile(fName);
    finally
      ResStream.Free;
    end;
end;

begin
  Play( CopyStream2File('42') );
end;
Wann passiert es denn, dass ich hier eine Exception bekomme?
  • Die ResourceID ist falsch, weil nicht vorhanden -> Fehler beim Programmierer
  • Das Temp-Verzeichnis ist nicht beschreibbar -> Fehler beim Anwender
Aber anstatt sich den Hinweis hier um die Ohren zu hauen, wird der einfach hübsch versteckt, weil das sieht ja dann "besser" aus. Voll profimässig :wall:

Exceptions sind nicht böse, sondern sehr hilfreich.

Und wenn man die Exceptions wirklich nicht anzeigen möchte, dann geht das ganz simpel, indem man Delphi-Referenz durchsuchenTApplication.OnException mit einem Event belegt und dort wenigstens diese Meldungen protokolliert.

Mavarik 29. Dez 2014 14:45

AW: TPath.Combine, TPath.GetTempPath threadsicher?
 
Zitat:

Zitat von Sir Rufo (Beitrag 1284912)
Sehr grausam, wie hier manche mit den Exceptions umgehen ...

Ach ja was gibt es denn hieran

Delphi-Quellcode:
var
   FName : String;
   error : boolean;
begin
   ...
   try
     error := CopyStream2File('42',FName);
   except
     MyError('Fehler...');
   end;

   if not Error then
     Play(FName)
   
end;
aus zu setzen?

Von mir aus auch

Delphi-Quellcode:
var
   FName : String;
   error : integer;
begin
   ...
   try
     error := CopyStream2File('42',FName);
   except
     MyError('Fehler...',error);
   end;

   if Error=0 then
     Play(FName)
   
end;

Sir Rufo 29. Dez 2014 15:00

AW: TPath.Combine, TPath.GetTempPath threadsicher?
 
Ganz einfach das hier:
Delphi-Quellcode:
var
   FName : String;
   error : boolean;
begin
   ...
  error := False; // damit das auf jeden Fall initialisiert ist
   try
     error := CopyStream2File('42',FName); // <- das erzeugt eine Exception
   except
     MyError('Fehler...'); // <- Ja wird ausgeführt
   end;

   if not Error then // <- Was für einen Wert hat jetzt Error? Richtig: False - äh, aber das ist doch Falsch! Eben darum
     Play(FName)
   
end;
Und wenn wir diese initiale Zuweisung von
Delphi-Quellcode:
error := False;
weglassen, dann haben wir einen Zufallsgenerator. Mal wird
Delphi-Quellcode:
Play(FName)
ausgeführt und mal nicht, obwohl es immer einen Fehler gibt. Dadurch kommen dann solche Gerüchte über Auswirkungen von Erdstrahlen oder Sonnenflecken auf die Programme auf.

ringli 29. Dez 2014 15:01

AW: TPath.Combine, TPath.GetTempPath threadsicher?
 
Ohne es nachgeprüft zu haben, aber könnte es sein das TPath.GetTempPath den Pfad ohne abschliessenden Slash bzw. Backslash zurückgibt?

Sir Rufo 29. Dez 2014 15:09

AW: TPath.Combine, TPath.GetTempPath threadsicher?
 
Zitat:

Zitat von ringli (Beitrag 1284922)
Ohne es nachgeprüft zu haben, aber könnte es sein das TPath.GetTempPath den Pfad ohne abschliessenden Slash bzw. Backslash zurückgibt?

Dem ist zwar so (ja, nur nicht von dem Namen
Delphi-Quellcode:
Get...Path
verwirren lassen, es wird trotzdem ein Directory zurückgeliefert und eben kein Path), spielt aber keine Geige, denn es wird ja
Delphi-Quellcode:
TPath.Combine
benutzt, und das sorgt dafür, dass der richtige Trenner dazwischen kommt.

Mavarik 29. Dez 2014 15:25

AW: TPath.Combine, TPath.GetTempPath threadsicher?
 
Zitat:

Zitat von Sir Rufo (Beitrag 1284921)
Und wenn wir diese initiale Zuweisung... weglassen, dann haben wir einen Zufallsgenerator.

Logisch... hatte ich übersehen...

Deswegen so

Delphi-Quellcode:
Function CopyStream2File(ResourceID: string; var fName: String): boolean;
var
    ResStream: TResourceStream;

begin
   Result := False;
   
   try
    ResStream := TResourceStream.Create(HInstance, ResourceID, RT_RCDATA);
    try
      //MP3 File aus Resource laden und in Filesystem ablegen
      fName := TPath.Combine(TPath.GetTempPath, 'tmp.mp3');
// fName := TPath.Combine('/data/data/com.embarcadero.Meinprogram/files/', 'tmp.mp3');

      ResStream.Position := 0;
      ResStream.SaveToFile(fName);
      Result := True;
    finally
      ResStream.Free;
    end;
   except
   end
end;
Schon habe ich ein Error-Free Programm/Procedure und ob und wie ich den Fehler ausgeben will kann ich außerhalb anhand meines Boolean entscheiden...

DeddyH 29. Dez 2014 15:34

AW: TPath.Combine, TPath.GetTempPath threadsicher?
 
Und da steht dann "Es ist ein Fehler aufgetreten", da man die Exception einfach geschluckt hat und somit nicht weiß, welche überhaupt geworfen wurde, geschweige denn ihren ggf. vorhandenen Fehlertext gespeichert hat.

Sir Rufo 29. Dez 2014 15:51

AW: TPath.Combine, TPath.GetTempPath threadsicher?
 
Und wenn man so etwas mit aller Gewalt haben will, dann sollte man das auch sprachlich entsprechend hervorheben, damit man auch später noch weiß, dass da etwas in die Hose gehen kann, ohne dass man eine Exception bekommt:
Delphi-Quellcode:
Function CopyResourceToFile( const ResourceID: string ) :String);
var
    ResStream: TResourceStream;
begin
    ResStream := TResourceStream.Create(HInstance, ResourceID, RT_RCDATA);
    try
      //MP3 File aus Resource laden und in Filesystem ablegen
      fName := TPath.Combine(TPath.GetTempPath, 'tmp.mp3');

      ResStream.Position := 0;
      ResStream.SaveToFile(fName);
    finally
      ResStream.Free;
    end;
end;

function TryCopyResourceToFile( const ResourceID: string; out AFileName : string ) : Boolean;
begin
  try
    AFileName := CopyResourceToFile( ResourceId );
    Result := True;
  except
    Result := False;
  end;
end;
Jetzt kann man das wahlweise mit oder ohne Exception-Behandlung benutzen:
Delphi-Quellcode:
// mit Exception
Play( CopyResourceToFile( '42' ) );
// oder ganz bewusst im Code auf die Exception verzichten
if TryCopyResourceToFile( '42', FName ) then
  Play( FName );

Stevie 29. Dez 2014 16:01

AW: TPath.Combine, TPath.GetTempPath threadsicher?
 
Zitat:

Zitat von Mavarik (Beitrag 1284926)
Schon habe ich ein Error-Free Programm/Procedure und ob und wie ich den Fehler ausgeben will kann ich außerhalb anhand meines Boolean entscheiden...

Und mit welcher magischen Funktion wandel ich den Zustand False wieder in eine aussagekräftige Fehlerinformation um?
Indem ich mir genau wie die WinAPI nen GetMyLastError baue oder was? :kotz:

DeddyH 29. Dez 2014 16:05

AW: TPath.Combine, TPath.GetTempPath threadsicher?
 
Meine Rede. Ich freu mich auch immer in der Hotline, wenn solche ominösen Fehlermeldungen auftauchen, weil man ja ganz schlau mit Exceptions umgegangen ist.

Uwe Raabe 29. Dez 2014 16:06

AW: TPath.Combine, TPath.GetTempPath threadsicher?
 
Bevor das hier ganz in Richtung Off-Topic abdriftet - hat noch irgendjemand Erkenntnisse zu der eigentlichen Frage?

Insbesondere wäre vom Fragesteller ein Hinweis nützlich, welche Umgebung hier eigentlich vorliegt: Windows, OSX, iOS, Android?

zeras 29. Dez 2014 18:16

AW: TPath.Combine, TPath.GetTempPath threadsicher?
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1284932)
Bevor das hier ganz in Richtung Off-Topic abdriftet - hat noch irgendjemand Erkenntnisse zu der eigentlichen Frage?

Sieht fast so aus, aber ich habe auf jeden Fall was dazu gelernt.
Danke nochmals für eure Erklärungen.



Zitat:

Zitat von Uwe Raabe (Beitrag 1284932)
Insbesondere wäre vom Fragesteller ein Hinweis nützlich, welche Umgebung hier eigentlich vorliegt: Windows, OSX, iOS, Android?

Das ganze nutze ich für Android.


Alle Zeitangaben in WEZ +1. Es ist jetzt 09:58 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz