AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Win32/Win64 API (native code) Delphi Datei aus dem Papierkorb wiederherstellen
Thema durchsuchen
Ansicht
Themen-Optionen

Datei aus dem Papierkorb wiederherstellen

Ein Thema von Benmik · begonnen am 26. Aug 2014 · letzter Beitrag vom 28. Aug 2014
Antwort Antwort
Benmik

Registriert seit: 11. Apr 2009
578 Beiträge
 
Delphi 12 Athens
 
#1

AW: Datei aus dem Papierkorb wiederherstellen

  Alt 27. Aug 2014, 12:41
Hallo Dejan Vu, vielen Dank für deinen wirklich guten Kommentar. Auch der ist top!

Fast alle deine Anmerkungen hatte ich mir auch vorher durch den Kopf gehen lassen und ich finde sie richtig. Ich muss aber erklärend anmerken, dass nach dem vielen Herumsuchen und -probieren ich nachts um eins wenig Lust hatte, den Code einzustellen. Da ich aber schon so viel von der DP profitiert habe und wusste, du machst es gleich oder gar nicht, habe ich den Code einfach rasch reingehauen. Er löst das eigentliche Problem und es ist für jedermann kinderleicht, ihn den eigenen Wünschen und Bedürfnissen anzupassen.

Zu deinen Anmerkungen:
  1. Klasse: Das war sonnenklar, dass der Profi (bin ich ja nicht) alles mit Klassen macht (machen muss, sonst fühlt er sich nicht gut!). Ich bleibe (mit schlechtem Gewissen) gern bei meinen schlichten Prozedurchen und Funktiönchen, weil sie einfach und praktisch sind (vor allem beim Reinkopieren in den eigenen Code).
  2. Records 1: Das Gleiche! Records sind out und der Profi nimmt Klassen. Nach mehreren Diskussionen hierüber in der DP (siehe vor allem hier) liebe ich immer noch meine kleinen bescheidenen Recordchen und bin da anscheinend auch nicht der einzige.
  3. Records 2: Ich hatte stark mit einer TObjectList geliebäugelt, insbesondere wegen OwnsObject. Dann habe ich aber einen (englischen) Beitrag gelesen, wo jemand behauptete, er habe den Quelltext von TObjectList durchgesehen und dort werde trotz OwnsObject gar nichts freigegeben. Da habe ich mir gedacht, wenn du sowieso das altmodische Zeugs nimmst, dann baust du dir auch eine idiotensichere Lösung (daher auch PPIDL^.Dateiname := ''; ).
  4. Einzelschritte: Unbedingt! Mache ich eigentlich sonst immer. Insbesondere, weil ich Code Folding für die segensreichste Neuerung der IDE halte und Einzelschritte das Debuggen so erleichtern. Was mich nur irritiert, ist, dass der Code der Profis hier diesen Grundsatz eigentlich so gut wie nie beherzigt. Und der Sourcecode von großen kommerziellen Programmen tut das auch nicht, da sieht man ellenlangen Spaghetticode, allenfalls unterbrochen von hinweisenden Kommentaren.
  5. "...das ist eh falsch, denn i ist = PIDLListe.Count, wenn nichts gefunden wurde" stimmt meiner Meinung nach nicht, der Code ist richtig. Erster Kandidat dennoch für eine Zerlegung in Einzelschritte.
  6. "...denn bei einem 'exit' wird vorher der finally-Abschnitt aufgerufen." Echt? Tatsächlich! Da erhebt sich nebenbei die Frage, wie man den langgehegten Wunsch vieler Delphi-Anwender nach einer try..except..finally-Konstruktion am besten realisiert. Doppeltes "try"?
  7. Deutsch/Englisch: Stimmt auch. Ich habe das englische Original (hier!) zum Teil gelassen, weil ich mich genierte, den fremden Code so total zu vereinnahmen. Ist vielleich Quatsch.
Ausblick: Vielleicht setze ich mich dran und bastle ein paar Klassen, und sei es nur zum Üben. Ein Vorteil wäre (sogar für mich), dass man dann noch ein paar Funktionalitäten einbauen könnte (beispielsweise glaube ich, dass die Auflistung der Dateien im Papierkorb für sich allein für viele interessant ist). Ansonsten tut es vielleicht der Nächste, der den Code benutzt?

Geändert von Benmik (27. Aug 2014 um 13:11 Uhr)
  Mit Zitat antworten Zitat
Dejan Vu
(Gast)

n/a Beiträge
 
#2

AW: Datei aus dem Papierkorb wiederherstellen

  Alt 27. Aug 2014, 13:11
Ich glaube, das das mit dem kleinen Fehler stimmt(e). Ich mach seit einiger Zeit nix mit Delphi, aber soweit ich mich erinnere gilt folgendes:

Delphi-Quellcode:
for i:=0 to Grenze do begin ... end;
   
// hier ist i>Grenze
// Es wäre denkbar, das i auch <0 ist. wenn die Reihenfolge keine Rolle spielt.
// Aber i sollte nicht =Grenze sein. Probiere es mal aus
Aber ist es nicht so, das der Compiler eine Warnung ausspuckt, i sei nach dem Ende der Schleife undefiniert???
  Mit Zitat antworten Zitat
Benmik

Registriert seit: 11. Apr 2009
578 Beiträge
 
Delphi 12 Athens
 
#3

AW: Datei aus dem Papierkorb wiederherstellen

  Alt 27. Aug 2014, 13:52
Bei einer Schleife For i := 0 to PIDLListe.Count - 1 kann i maximal den Wert PIDLListe.Count - 1 erreichen. i bleibt definiert, da innerhalb der For-Schleife; dafür muss das Erreichen von PIDLListe.Count - 1 unschöner Weise bei jedem Durchlauf abgeprüft werden.
  Mit Zitat antworten Zitat
Dejan Vu
(Gast)

n/a Beiträge
 
#4

AW: Datei aus dem Papierkorb wiederherstellen

  Alt 27. Aug 2014, 13:59
Jaha, aber wie sieht denn i *hinter* der Schleife aus? Ist verboten, ich weiß, aber trotzdem
Delphi-Quellcode:
for i:=1 to Grenze do begin
  foo(i)
  end;
// Welchen Wert hat 'i' hier?
...
// ist äquivalent zu
...
i := 1;
while i<=Grenze do begin
  foo(i);
  inc(i);
end;
// Welchen Wert hat 'i' hier?
dafür muss das Erreichen von PIDLListe.Count - 1 unschöner Weise bei jedem Durchlauf abgeprüft werden.
Es bleibt ja auch nichts anderes übrig. Nur das das Schleifenende einmalig ausgerechnet wird. Also wird nicht jedesmal geschaut, wie lang die Liste denn nun ist und dann 1 abgezogen...

Geändert von Dejan Vu (27. Aug 2014 um 14:02 Uhr)
  Mit Zitat antworten Zitat
Benmik

Registriert seit: 11. Apr 2009
578 Beiträge
 
Delphi 12 Athens
 
#5

AW: Datei aus dem Papierkorb wiederherstellen

  Alt 27. Aug 2014, 19:28
1. Hinter For..end ist die Schleifenvariable definitionsgemäß undefiniert.
2. Bei einer While-Konstruktion ist i immer definiert, da es sich um eine initialisierte Variable handelt. Hier dürfte man übrigens auch eine globale Variable verwenden, was bei For nicht erlaubt ist.
  Mit Zitat antworten Zitat
Benmik

Registriert seit: 11. Apr 2009
578 Beiträge
 
Delphi 12 Athens
 
#6

AW: Datei aus dem Papierkorb wiederherstellen

  Alt 27. Aug 2014, 19:45
So, jetzt hat es mich natürlich doch gejuckt und ich habe das Ganze OOP-mäßig in Klassen verpackt.
Wie erwähnt, hat mich die Erweiterbarkeit gereizt.
Das Ganze hat jetzt 5 Funktionalitäten:
  1. Anzahl der Dateien im Papierkorb ermitteln
  2. Liste aller Dateien im Papierkorb erstellen (mit Filterfunktion!)
  3. Datei in den Papierkorb verschieben
  4. Datei aus dem Papierkorb wiederherstellen
  5. Papierkorb leeren

Delphi-Quellcode:
unit Papierkorb;

interface

uses Windows,Contnrs,Forms,Classes,SysUtils,ShellAPI,Masks,COMObj,shlobj,ActiveX;

type
  TPIDLItem = class
  private
    FDateiname: String;
    FIDL : PItemIDList;
  protected
  public
end;

type
  TPapierkorb = class
  private
    FPIDLListe: TObjectList;
    FDeskDirI : IShellFolder;
    FRecycleI : IShellFolder;
    FpReIDL : PItemIDList;
    FpNextIDL : PItemIDList;
    FpItemIDL : PItemIDList;
    FEnumList : IENUMIDLIST;
    FCmInfo : CMINVOKECOMMANDINFO;
    FContextI : IContextMenu;
    FIsThere : Cardinal;
    FStrRet : TStrRet;
    FparName : String;
    FPIDLItem : TPIDLItem;
    procedure SetzePapierkorbInterface;
    procedure NeueDatei(var PPIDLItem:TPIDLItem);
    function ListePapierkorbDateienAuf(Maske:string = ''):Boolean;
    function PKDateiWiederhergestellt(ListNr:integer;Dateiname:string):Boolean;
    function DateiInPKGefunden(Dateiname:string;var ListNr:integer):Boolean;
    function VerschiebeDateiInPK(var Dateiname: string;PlusNull:Boolean):Boolean;
  protected
  public
    constructor Create();
    destructor Destroy(); override;
    function ErstellePKDateiListe(const DateiListe:TStringList;Maske:string = ''):Boolean;
    function StellePKDateiWiederHer(Dateiname:string):Boolean;
    function ErmittleAnzPKDateien(Maske:string = ''):integer;
    function LeerePapierkorb:Boolean;
    function DateiInPapierkorb(Dateiname: string): Boolean;
end;
function SHEmptyRecycleBin(Wnd:HWnd; LPCTSTR:PChar; DWORD:Word):Integer; stdcall;
function SHEmptyRecycleBin; external 'SHELL32.DLLname 'SHEmptyRecycleBinA';

implementation

constructor TPapierkorb.Create;
begin
  inherited Create;
  FPIDLListe := TObjectList.Create;
end;

destructor TPapierkorb.Destroy;
begin
  FPIDLListe.Free;
  FPIDLListe := nil;
  inherited;
end;

function TPapierkorb.ErstellePKDateiListe(const DateiListe:TStringList;Maske:string = ''):Boolean;
var i:integer;
begin
  SetzePapierkorbInterface;
  Result := ListePapierkorbDateienAuf(Maske);
  If Result then begin
    For i := 0 to FPIDLListe.Count - 1 do
      DateiListe.Add(TPIDLItem(FPIDLListe[i]).FDateiname);
  end;
end;

function TPapierkorb.StellePKDateiWiederHer(Dateiname:string):Boolean;
var ListNr:integer;
begin
  SetzePapierkorbInterface;
  ListePapierkorbDateienAuf;
  Result := DateiInPKGefunden(Dateiname,ListNr) and PKDateiWiederhergestellt(ListNr,Dateiname);
end;

function TPapierkorb.ErmittleAnzPKDateien(Maske:string = ''):integer;
begin
  SetzePapierkorbInterface;
  If ListePapierkorbDateienAuf(Maske)
    then Result := FPIDLListe.Count
    else Result := -1;
end;

function TPapierkorb.DateiInPKGefunden(Dateiname:string;var ListNr:integer):Boolean;
var i:integer;
begin
  ListNr := -1;
  Try
    For i := 0 to FPIDLListe.Count - 1 do begin
      If SameFileName(TPIDLItem(FPIDLListe[i]).FDateiname,Dateiname) then begin
        ListNr := i;
        break;
      end;
    end;
  Finally
    Result := (ListNr > -1);
  End;
end;

function TPapierkorb.PKDateiWiederhergestellt(ListNr:integer;Dateiname:string):Boolean;
begin
  Try
    FpItemIDL := TPIDLItem(FPIDLListe[ListNr]).FIDL;
    If FpItemIDL <> nil then begin
      ZeroMemory(@FCmInfo, SizeOf(FCmInfo));
      FCmInfo.cbSize:= SizeOf(FCmInfo);
      FCmInfo.fMask:= CMIC_MASK_FLAG_NO_UI;
      FCmInfo.hwnd:= Application.Handle;
      FCmInfo.lpVerb:= 'undelete';
      FCmInfo.nShow:= SW_SHOWDEFAULT;
      OleCheck(FRecycleI.GetUIObjectOf(Application.Handle, 1, FpItemIDL, IID_IContextMenu, nil, FContextI));
      OleCheck(FContextI.InvokeCommand(FCmInfo));
    end;
  Except
    CoTaskMemFree(FpItemIDL);
  end;
  // Result := True;
  Result := FileExists(Dateiname);
end;

function TPapierkorb.ListePapierkorbDateienAuf(Maske:string = ''):Boolean;
begin
  Result := True;
  Try
    OleCheck(FRecycleI.EnumObjects(Application.Handle, SHCONTF_FOLDERS or SHCONTF_NONFOLDERS or SHCONTF_INCLUDEHIDDEN, FEnumList));
    While FEnumList.Next(1, FpNextIDL, FIsThere) = S_OK do begin
      If FIsThere > 0 then begin
        OleCheck(FRecycleI.GetDisplayNameOf(FpNextIDL, SHGDN_NORMAL, FStrRet));
        case FStrRet.uType of
          STRRET_CSTR: FparName := FStrRet.cStr;
          STRRET_OFFSET: FparName := PChar(Cardinal(FpNextIDL) + FStrRet.uOffset);
          STRRET_WSTR: FparName := FStrRet.pOleStr;
        end;
      end;
      If FpNextIDL <> nil then begin
        If (Maske = '') or MatchesMask(FparName,Maske) then begin
          FPIDLItem := TPIDLItem.Create;
          FPIDLItem.FDateiname := FparName;
          FPIDLItem.FIDL := FpNextIDL;
          NeueDatei(FPIDLItem);
        end;
      end;
    end;
  except
    Result := False;
  end;
  CoTaskMemFree(FpNextIDL);
end;

procedure TPapierkorb.NeueDatei(var PPIDLItem:TPIDLItem);
begin
  FPIDLListe.Add(PPIDLItem);
end;

function TPapierkorb.LeerePapierkorb:Boolean;
const
  SHERB_NOCONFIRMATION = $00000001;
  SHERB_NOPROGRESSUI = $00000002;
  SHERB_NOSOUND = $00000004;
begin
  Result := (SHEmptyRecycleBin(0, nil, SHERB_NOCONFIRMATION or SHERB_NOPROGRESSUI or SHERB_NOSOUND) = 0);
end;

function TPapierkorb.DateiInPapierkorb(Dateiname: string): Boolean;
begin
  Result := FileExists(Dateiname);
  If Result then begin
    // Erst kein, dann ein, und dann zwei Nullzeichen hinter den Dateinamen setzen -> drei Mal ist Bremer Recht!
    Result := VerschiebeDateiInPK(Dateiname,False);
    If not Result then begin
      Result := VerschiebeDateiInPK(Dateiname,True);
      If not Result
        then Result := VerschiebeDateiInPK(Dateiname,True);
    end;
  end;
end;

function TPapierkorb.VerschiebeDateiInPK(var Dateiname: string;PlusNull:Boolean):Boolean;
var DatStrukt: TSHFileOpStruct; Ergebnis:integer;
begin
  // Es müssen ZWEI Nullzeichen am Dateiende sein, das klappt nicht immer
  If PlusNull
    then Dateiname := Dateiname + #0;
  FillChar(DatStrukt, SizeOf(DatStrukt), 0);
  DatStrukt.wFunc := FO_DELETE;
  DatStrukt.pFrom := PChar(Dateiname);
  DatStrukt.fFlags := FOF_ALLOWUNDO or FOF_NOCONFIRMATION or FOF_SILENT;
  Ergebnis := ShFileOperation(DatStrukt);
  Result := (Ergebnis = 0);
end;

procedure TPapierkorb.SetzePapierkorbInterface;
begin
  OleCheck(SHGetDesktopFolder(FDeskDirI));
  OleCheck(SHGetSpecialFolderLocation(Application.Handle, CSIDL_BITBUCKET, FpReIDL));
  OleCheck(FDeskDirI.BindToObject(FpReIDL, nil, IShellFolder, FRecycleI));
  CoTaskMemFree(FpReIDL);
end;

end.
Ausprobieren kann man das mit einem neuen Projekt, dazu eine Listbox, drei Buttons und ein Edit auf die Form bringen, TPapierkorb einbinden und dann:
Delphi-Quellcode:
uses Papierkorb;

procedure TForm1.Button1Click(Sender: TObject);
var PK:TPapierkorb; Liste:TStringList;
begin
  ListBox1.Clear;
  Liste := TStringList.Create;
  Liste.Sorted := True;
  Form1.Position := poDesktopCenter;
  PK := TPapierkorb.Create;
  PK.ErstellePKDateiListe(Liste,Edit1.Text);
  ListBox1.Items.Assign(Liste);
  PK.LeerePapierkorb;
  PK.Free;
end;

procedure TForm1.Button2Click(Sender: TObject);
var PK:TPapierkorb;
begin
  ListBox1.Clear;
  Form1.Position := poDesktopCenter;
  PK := TPapierkorb.Create;
  ListBox1.Items.Add('Es sind ' + IntToStr(PK.ErmittleAnzPKDateien(Edit1.Text)) + ' Dateien gemäß Ihren Kriterien im Papierkorb vorhanden.');
  PK.Free;
end;

procedure TForm1.Button3Click(Sender: TObject);
var PK:TPapierkorb;
begin
  If ListBox1.ItemIndex = -1 then begin
    ShowMessage('Scherzkeks! Kein Eintrag ausgewählt! ');
  end else begin
    PK := TPapierkorb.Create;
    If PK.StellePKDateiWiederHer(ListBox1.Items[ListBox1.ItemIndex])
      then Showmessage('Die Datei' + Chr(13) + Chr(13) + ListBox1.Items[ListBox1.ItemIndex] + Chr(13) + Chr(13) + 'wurde wiederhergestellt. ')
      else Showmessage('Die Datei' + Chr(13) + Chr(13) + ListBox1.Items[ListBox1.ItemIndex] + Chr(13) + Chr(13) + 'konnte nicht wiederhergestellt werden. ');
    PK.Free;
  end;
end;
Eine Sache ist mir unklar: Muss der Anwender Free aufrufen?

Geändert von Benmik (27. Aug 2014 um 23:39 Uhr) Grund: Anregungen eingearbeitet
  Mit Zitat antworten Zitat
hoika

Registriert seit: 5. Jul 2006
Ort: Magdeburg
8.277 Beiträge
 
Delphi 10.4 Sydney
 
#7

AW: Datei aus dem Papierkorb wiederherstellen

  Alt 27. Aug 2014, 21:04
Hallo,

ja, solange nicht mit Interfaces gearbeitet wird.

Die Sache mit dem i hinter der For-Schleife ist leicht erklärt.
Deine Code-Formatierung sah so aus, als ob du auf die Schleifenvariable nach der For-Schleife zugreifst,
hast du aber nicht.

Delphi-Quellcode:
PapierkorbDateienAuflisten;
For i := 0 to PIDLListe.Count - 1 do
begin
  If SameText(PPIDLItem(PIDLListe[i])^.Dateiname,Dateiname) then
  begin
    pItemIDL := PItemIDList(PPIDLItem(PIDLListe[i])^.IDL);
    break;
  end;
  If i = PIDLListe.Count - 1 then
  begin
    // das ist eh falsch, denn i ist = PIDLListe.Count, wenn nichts gefunden wurde
    // LeerePIDLListe;
    exit;
  end;
Heiko
Heiko

Geändert von hoika (27. Aug 2014 um 21:07 Uhr)
  Mit Zitat antworten Zitat
Antwort Antwort

 

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 07:54 Uhr.
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz