Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Delphi IniFile ReadBinaryStream bekomme ich nicht in Gange (https://www.delphipraxis.net/198422-inifile-readbinarystream-bekomme-ich-nicht-gange.html)

KodeZwerg 1. Nov 2018 08:11

IniFile ReadBinaryStream bekomme ich nicht in Gange
 
Hallo Community,
ich bekomme immer einen "Stream write error" beim Versuch einen Stream aus einer Ini-Datei zu lesen.

Woran kann das liegen? Ich bin mehreren Beispielen gefolgt aber entdecke einfach nicht was da falsch läuft.
Vielleicht hat jemand Rat für mich.

Hier meine bisherige Bastelei:
Delphi-Quellcode:
//unit IniHelper;
uses
  Windows, SysUtils, Classes, SHFolder, IniFiles;

...

function GetSpecialFolderPath( Folder: Integer ): string;
//const SHGFP_TYPE_CURRENT = 0;
var
  Path: Array [0..MAX_PATH] of WideChar;
begin
  if Windows.Succeeded( SHFolder.SHGetFolderPathW( 0, Folder, 0, SHGFP_TYPE_CURRENT, @Path[ 0 ] ) ) then
    Result := Path
   else
    Result := '';
end;

function ReadIniStream( const Filename, Section, Ident: String; const FolderOverride: String = '' ): TStream;
var
  FIniFile: TIniFile;
  Check: Integer;
  FName: String;
  MS: TStream;
begin
  FName := GetSpecialFolderPath( CSIDL_LOCAL_APPDATA );
  if FName = '' then
    Exit;
  if FolderOverride = '' then
    FName := FName + '\' + SysUtils.ChangeFileExt( SysUtils.ExtractFilename( ParamStr( 0 ) ), '.' ) + '\' + Filename
   else
    FName := FName + '\' + FolderOverride + '\' + Filename;
  if not FileExists( FName ) then
    Exit;
  FIniFile := TIniFile.Create( FName );
  try
    MS := TMemoryStream.Create();
    try
      FIniFile.ReadBinaryStream( Section, Ident, MS );
      MS.Position := 0;
      Result.CopyFrom( MS, MS.Size );
      Result.Position := 0;
    finally
      MS.Free;
    end;
  finally
    FIniFile.Free;
  end;
end;

function WriteIniStream( const Filename, Section, Ident: String; const Value: TStream; const FolderOverride: String = '' ): Boolean;
var
  FIniFile: TIniFile;
  FName: String;
begin
  Result := False;
  FName := GetSpecialFolderPath( CSIDL_LOCAL_APPDATA );
  if FName = '' then
    Exit;
  if FolderOverride = '' then
    FName := FName + '\' + SysUtils.ChangeFileExt( SysUtils.ExtractFilename( ParamStr( 0 ) ), '.' )
   else
    FName := FName + '\' + FolderOverride;
  if not SysUtils.ForceDirectories( FName ) then
    Exit;
  if Value.Size = 0 then
    Exit;
  FIniFile := TIniFile.Create( FName + '\' + Filename );
  try
    Value.Position := 0;
    FIniFile.WriteBinaryStream( Section, Ident, Value );
  finally
    FIniFile.Free;
    Result := True;
  end;
end;
und entsprechende Aufrufe
Delphi-Quellcode:
procedure TForm1.btnReadClick(Sender: TObject);
var
  S: String;
  ms: TStream;
begin
  ms := TMemoryStream.Create();
  try
    S := 'Stream-Error';
    ms := IniHelper.ReadIniStream( 'Testfile.cfg', 'Setup', 'StreamEntry' );
    S := IntToStr( ms.Size );
    Memo1.Lines.Add( 'Stream-Size: ' + S );
    ms.Free;
  except
    Memo1.Lines.Add( 'Stream-Size: ' + S );
    ms.Free;
  end;
end;

procedure TForm1.btnWriteClick(Sender: TObject);
var
  s: String;
  ms: TStream;
begin
  try
    ms := TMemoryStream.Create();
    try
      S := 'This is a Stream.';
      ms.WriteBuffer( S[1], Length( S ) * 2 );
      ms.Position := 0;
      if WriteIniStream( 'Testfile.cfg', 'Setup', 'StreamEntry', ms ) then
        begin
          Memo1.Lines.Add( 'StreamEntry Saved.' )
        end
        else
        begin
          Memo1.Lines.Add( 'Error: Testfile.cfg Stream could not be created.' )
        end;
    finally
      ms.Free;
    end;
  except
    Memo1.Lines.Add( 'Error: Fatal Error.' )
  end;
end;
Danke fürs Lesen.

Ps: Speichern klappt, hat nur eine Leseschwäche, genau wie ich :-]

TiGü 1. Nov 2018 08:17

AW: IniFile ReadBinaryStream bekomme ich nicht in Gange
 
Wenn du jetzt noch das Result in ReadIniStream initialisierst/erzeugst/den Konstruktor aufrufst, haste es.
Nimm ruhig noch einen Kaffee, es kann nicht schaden! :cheers:

KodeZwerg 1. Nov 2018 08:24

AW: IniFile ReadBinaryStream bekomme ich nicht in Gange
 
Lol, jupp Danke klappt!

/edit
für die Nachwelt, so isses nu :]
Delphi-Quellcode:
function ReadIniStream( const Filename, Section, Ident: String; const FolderOverride: String = '' ): TStream;
var
  FIniFile: TIniFile;
  FName: String;
begin
  FName := GetSpecialFolderPath( CSIDL_LOCAL_APPDATA );
  if FName = '' then
    Exit;
  if FolderOverride = '' then
    FName := FName + '\' + SysUtils.ChangeFileExt( SysUtils.ExtractFilename( ParamStr( 0 ) ), '.' ) + '\' + Filename
   else
    FName := FName + '\' + FolderOverride + '\' + Filename;
  if not FileExists( FName ) then
    Exit;
  FIniFile := TIniFile.Create( FName );
  try
    Result := TMemoryStream.Create();
    try
      FIniFile.ReadBinaryStream( Section, Ident, Result );
    finally
      Result.Position := 0;
    end;
  finally
    FIniFile.Free;
  end;
end;

p80286 1. Nov 2018 09:38

AW: IniFile ReadBinaryStream bekomme ich nicht in Gange
 
Und Du bist Dir sicher, daß das richtigfunktionier?

verkürzt hast Du:
Delphi-Quellcode:
Funktion Read:TStream;
begin
  if abbruch then Exit;
  Result:=TStream.Create;
end;

//Tuwas
MyStream:TStream;

MyStream:=TStream.Create;
MyStream:=Read;
...
MyStream.Free;
Wenn Deine er Funktion erfolgreich ist, dann nagelst Du einen zweiten Stream über den ersten oder was?
Das sieht mir sehr nach einem Memory-leak aus.

Mach es mit einer Procedure (
Delphi-Quellcode:
read(const mystream:TStream)
) und Du bist auf der sicheren Seite.

Gruß
K-H

KodeZwerg 1. Nov 2018 12:03

AW: IniFile ReadBinaryStream bekomme ich nicht in Gange
 
Liste der Anhänge anzeigen (Anzahl: 1)
Ich hab in dem btnReadClick() Aufruf das Create weggelassen. Ich lade bald hoch, will noch ne verschlüßelung einbauen.

für interessierte, das ist es bis jetzt.

/edit
habs im anhang als komplettes demo projekt zusammengeschnürrt.
beim entschlüsseln klappt irgendwas noch nicht, bin an der sache dran.

Ps: keine speicherlecks gefunden

Hobbycoder 1. Nov 2018 13:22

AW: IniFile ReadBinaryStream bekomme ich nicht in Gange
 
Trotzdem hätte ich das so gemacht (wie p80286 schon sagte):
Delphi-Quellcode:
function ReadIniStream( const Filename, Section, Ident: String; const IniStream: TStream; const FolderOverride: String = '' ): Boolean;
var
  FIniFile: TIniFile;
  FName: String;
  FMemStream: TMemoryStream;
begin
  Result:=False;
  if IniStream<>nil then
  begin
    FMemStream := TMemoryStream.Create();
    try
      FName := GetIniFilename( Filename, FolderOverride );
      if not SysUtils.FileExists( FName ) then
        Exit;
      FIniFile := TIniFile.Create( FName );
      FIniFile.ReadBinaryStream( Section, Ident, FMemStream );
      FMemStream.Position := 0;
      IniStream.Position := 0;
      IniStream.CopyFrom(FMemStream, SizeOf(FMemStream));
      Result:=True;
    finally
      FMemStream.Free;
      IniStream.Position := 0;
    end;
  end;
end;
Zumindest ungefähr so (hab ich nicht in Delphi getestet).

Der Grund ist folgender:
Zum einen vertrete ich die Einstellung: Wer etwas erzeugt, der gibt's auch frei.
Zum anderen wird zwar in deiner jetzigen Version der Rssultstream auf jeden Fall erzeugt, aber sollte in beim Lesen aus der Ini was schief gehen, gibt sie trotzdem irgendwas zurück. Die aufrufende Methode erfährt davon nichts, und muss dann mit den fehlerhaften Daten weiterarbeiten.

Mit o.g. Lösung hätte man schon mal die Info, ob das Lesen des Streams fehlerfrei war. Nun muss man zwar in der aufrufenden Methode den Stream erzeugen (wieder eine Zeile mehr), aber damit erhöht sich m.M.n. die Übersichtlichkeit an der Stelle, wo ich den Stream dann auch wieder freigeben muss.

Ist aber nur meine Meinung ;-)

KodeZwerg 1. Nov 2018 14:15

AW: IniFile ReadBinaryStream bekomme ich nicht in Gange
 
Dann ist ja fast wie beim ersten mal ^_^
Habe Ratschlag angenommen und umgesetzt:

so in der IniHelper.pas
Delphi-Quellcode:
function ReadIniStream( const Filename, Section, Ident: String; const Value: TStream; const FolderOverride: String = '' ): Boolean;
var
  FIniFile: TIniFile;
  FName: String;
  FMS: TMemoryStream;
begin
  Result := False;
  if ( Value <> nil ) then
    begin
      FMS := TMemoryStream.Create();
      try
        FName := GetIniFilename( Filename, FolderOverride );
        if not SysUtils.FileExists( FName ) then
          Exit;
        FIniFile := TIniFile.Create( FName );
        try
          FIniFile.ReadBinaryStream( Section, Ident, FMS );
          FMS.Position := 0;
          Value.Position := 0;
          Value.CopyFrom( FMS, FMS.Size );
        finally
          FIniFile.Free;
          Value.Position := 0;
          Result := True;
        end;
      finally
        FMS.Free;
      end;
    end;
end;
und so in der demo
Delphi-Quellcode:
  ms := TMemoryStream.Create();
  try
    S := 'Stream-Error';
    if IniHelper.ReadIniStream( 'Testfile.cfg', 'Setup', 'StreamEntry', ms ) then
      begin
        S := IntToStr( ms.Size );
        Memo1.Lines.Add( 'Stream-Size: ' + S );
        S := '';
        SetLength( S, ms.Size div 2 );
        ms.Read( S[1], ms.Size );
        Memo1.Lines.Add( 'Stream: ' + S );
        ms.Free;
      end
      else
        Memo1.Lines.Add( 'Stream-Error: ' + S );
  except
    Memo1.Lines.Add( 'Stream-Error: ' + S );
    ms.Free;
  end;

Fritzew 1. Nov 2018 14:32

AW: IniFile ReadBinaryStream bekomme ich nicht in Gange
 
Zitat:

Zitat von KodeZwerg (Beitrag 1417248)
Habe Ratschlag angenommen und umgesetzt:
und so in der demo

Wenn schon dann so.
Ansonsten gibst Du Deinen Stream nicht frei wenn IniHelper.ReadIniStream mit false zurückkommt
Delphi-Quellcode:
  ms := TMemoryStream.Create();
      try
         try
            S := 'Stream-Error';
            if IniHelper.ReadIniStream('Testfile.cfg', 'Setup', 'StreamEntry', ms) then
               begin
                  S := IntToStr(ms.Size);
                  Memo1.Lines.Add('Stream-Size: ' + S);
                  S := '';
                  SetLength(S, ms.Size div 2);
                  ms.Read(S[1], ms.Size);
                  Memo1.Lines.Add('Stream: ' + S);

               end
            else
               Memo1.Lines.Add('Stream-Error: ' + S);
         finally
            ms.Free;
         end;
      except
         Memo1.Lines.Add('Stream-Error: ' + S);
      end;

KodeZwerg 1. Nov 2018 17:45

AW: IniFile ReadBinaryStream bekomme ich nicht in Gange
 
Liste der Anhänge anzeigen (Anzahl: 1)
Oh ja, wie wahr! Nun noch mal gefixte version, verschlüßelung funktioniert nun auch :-)

Danke fürs drauf Aufmerksam machen!

Luckie 1. Nov 2018 17:54

AW: IniFile ReadBinaryStream bekomme ich nicht in Gange
 
Wohl eher Base64 codiert oder? Zumindest hatte ich bei dir im Codecwas mit base64 gesehen.

KodeZwerg 1. Nov 2018 17:57

AW: IniFile ReadBinaryStream bekomme ich nicht in Gange
 
Zitat:

Zitat von Luckie (Beitrag 1417260)
Wohl eher Base64 codiert oder?

Verschlüßelung ist XOR, Base64 sitzt oben drauf.

Luckie 1. Nov 2018 18:09

AW: IniFile ReadBinaryStream bekomme ich nicht in Gange
 
Ah. Ok.

mkinzler 1. Nov 2018 18:10

AW: IniFile ReadBinaryStream bekomme ich nicht in Gange
 
XOR ist aber keine echte Verschlüsselung. Aber wohl besser als nichts.

Hobbycoder 1. Nov 2018 18:25

AW: IniFile ReadBinaryStream bekomme ich nicht in Gange
 
Warum überhaupt die Verschlüsselung?
XOR ist ja nur sinnvoll um sich vor "neugierigen" Blicken unwissender Benutzer zu schützen. Von daher schon okay. Aber dann müsste man im Grund auch eine "Verschleierung" im xxxIniDateTime, xxxIniFloat, xxxIniInteger und auch xxxIniStream anbieten, damit's rund ist.
Es könnte ja auch dort Daten abgelegt werden, die nicht intuitiv lesbar sein sollen. (z.B. Trail-Ablaufdatum, PIN-Codes, etc). Und der Stream könnte auch TStringStream sein und wäre somit leicht lesbar.

Wär ja kein großer Mehraufwand.

KodeZwerg 1. Nov 2018 19:32

AW: IniFile ReadBinaryStream bekomme ich nicht in Gange
 
Ja das mit XOR ist nur als Bonus gedacht gewesen, und wenn XOR keine Verschlüsselung darstellt, was ist XOR denn dann?
Mir fällt eigentlich nur ein ein CryptedStream mit anzubieten.
So kann man generell zwischen Binär und String mit XOR rumspielen. (wobei Streams in Ini... dafür sind die ja eigentlich nicht entwickelt worden, ja ich weiß das man da bilder oder gar ganze programme ablegen kann aber macht das viel sinn... hmmmm)
Boolean DateTime Integer da kann man bereits die String-Variante für benutzen.
(es passt ja nach XOR eh nicht mehr in den Felddatentyp, deswegen gleich String, oder später Stream)

Hobbycoder 1. Nov 2018 19:50

AW: IniFile ReadBinaryStream bekomme ich nicht in Gange
 
man kann natürlich Integer verschlüsselt über Write/ReadIniString speichern.
Aber da es eine Helperclass sein soll, denke ich, dass man dann gleich ein ReadCryptInteger/WriteCryptInteger (und andere Typen) einbauen könnte, was dann intern in String wandelt und cryoted, bzw. Umgekehrt wieder lesen.
Wär ja nur eine Vereinfachung für den späteren Benutzer der HelperClass.

War auch nur so ein Gedanke.:-D:-D

KodeZwerg 2. Nov 2018 07:48

AW: IniFile ReadBinaryStream bekomme ich nicht in Gange
 
Dein Gedanke hat mich Inspiriert.
Gefällt mir und bin schon dabei es umzusetzen.

Zum Thema Sicherheit:
Da ich in Selbsttests feststellen musste wie schnell man doch XOR knacken kann, habe ich mich entschlossen AES128 einzuführen.

XOR ist trotzdem noch enthalten nur an anderer Stelle.
Mein Gedanke ist nun, wenn man sich für eine der "Crypt" Varianten entscheiden sollte, wird auch der Keyname bearbeitet.

Keyname = XOR (plus Base64 onTop)
Values = AES128 (plus Base64 onTop)

Dann hat man als Beispiel sowas in der .ini
Zitat:

zBO+IGouWI5S6hPr3xdpLG=HavlDJP8McbhH4nEHMTsIp1NQaW pDrLESMbvTt12McTbRKDcI651TJ1uRczXUa5OTq5ZRpOtC3T7Q 3LaGM5KTpSuTsjjKa5bPc5wM45AJIzwQbHiHtSzFG
Was übersetzt heißen soll
Zitat:

CryptStringEntry=This is a Crypted String.
Ist das besser oder mach ich es mir zu schwer?


Alle Zeitangaben in WEZ +1. Es ist jetzt 18:16 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