![]() |
Datei aus dem Papierkorb wiederherstellen
Über dieses Thema gibt es offenbar wenig, und noch weniger in Delphi. Nach vieler Mühe habe ich eine funktionsfähige Lösung zusammengebastelt.
Natürlich ist sie fast vollständig aus dem Internet zusammengeklaubt, im Wesentlichen von ![]() Eine fertige, funktionsfähige Lösung von ![]() Der folgende Code funktioniert unter Windows Vista und Windows 7, sehr wahrscheinlich auch unter Windows 8.1. Für die Lösung mit TList habe ich mich mit ![]() Der Parameter "Dateiname" muss den Namen und den vollständigen Pfad der gelöschten Datei an ihrem Ursprungsort enthalten.
Delphi-Quellcode:
uses COMObj,shlobj,ActiveX; type PPIDLItem = ^TPIDLItem; TPIDLItem = record Dateiname : string; IDL : PItemIDList; end; function PapierkorbDateiWiederherstellen(Dateiname:string):Boolean; var DeskDirI, RecycleI: IShellFolder; pReIDL, pItemIDL: PItemIDList; CmInfo: CMINVOKECOMMANDINFO; ContextI: IContextMenu; PIDLListe:TList; PPIDL:PPIDLItem; i:integer; //------------------------------------------------------------------------------------------------------------------------------------------ procedure PapierkorbDateienAuflisten; var DeskDirI, RecycleI: IShellFolder; pReIDL, pNextIDL: PItemIDList; EnumList: IENUMIDLIST; IsThere: Cardinal; StrRet: TStrRet; parName: String; PPIDL:PPIDLItem; begin OleCheck(SHGetDesktopFolder(DeskDirI)); OleCheck(SHGetSpecialFolderLocation(Application.Handle, CSIDL_BITBUCKET, pReIDL)); OleCheck(DeskDirI.BindToObject(pReIDL, nil, IShellFolder, RecycleI)); CoTaskMemFree(pReIDL); OleCheck(RecycleI.EnumObjects(Application.Handle, SHCONTF_FOLDERS or SHCONTF_NONFOLDERS or SHCONTF_INCLUDEHIDDEN, EnumList)); while EnumList.Next(1, pNextIDL, IsThere) = S_OK do begin If IsThere > 0 then begin try OleCheck(RecycleI.GetDisplayNameOf(pNextIDL, SHGDN_NORMAL, StrRet)); except CoTaskMemFree(pNextIDL); // Be sure to Free the memory end; case StrRet.uType of STRRET_CSTR: parName := StrRet.cStr; STRRET_OFFSET: parName := PChar(Cardinal(pNextIDL)+StrRet.uOffset); STRRET_WSTR: parName := StrRet.pOleStr; end; end; If pNextIDL <> nil then begin New(PPIDL); PPIDL^.Dateiname := parName; PPIDL^.IDL := pNextIDL; PIDLListe.Add(PPIDL); end; end; end; //------------------------------------------------------------------------------------------------------------------------------------------ procedure LeerePIDLListe; begin While PIDLListe.Count > 0 do begin PPIDL := PPIDLItem(PIDLListe[0]); PPIDL^.Dateiname := ''; PIDLListe.Delete(0); Dispose(PPIDL); end; FreeAndNil(PIDLListe); end; //------------------------------------------------------------------------------------------------------------------------------------------ begin Result := False; PIDLListe := TList.Create; Try OleCheck(SHGetDesktopFolder(DeskDirI)); OleCheck(SHGetSpecialFolderLocation(Application.Handle, CSIDL_BITBUCKET, pReIDL)); OleCheck(DeskDirI.BindToObject(pReIDL, nil, IShellFolder, RecycleI));// get Recycle shell folder CoTaskMemFree(pReIDL); // code above gets the Recycle Bin IShellFoldr Interface in RecycleI 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 LeerePIDLListe; exit; end; end; Except LeerePIDLListe; exit; End; If pItemIDL <> nil then begin try ZeroMemory(@CmInfo, SizeOf(CmInfo)); with CmInfo do begin cbSize:= SizeOf(CmInfo); fMask:= CMIC_MASK_FLAG_NO_UI; // this fmask of NO_UI is suppose to NOT show the "Do You want to?" dialog box hwnd:= Application.Handle; lpVerb:= 'undelete'; // restore the recycle bin item nShow:= SW_SHOWDEFAULT; end; OleCheck(RecycleI.GetUIObjectOf(Application.Handle, 1, pItemIDL, IID_IContextMenu, nil, ContextI)); OleCheck(ContextI.InvokeCommand(CmInfo)); Except CoTaskMemFree(pItemIDL); LeerePIDLListe; exit; end; end; LeerePIDLListe; // Result := True; Result := FileExists(Dateiname); end; |
AW: Datei aus dem Papierkorb wiederherstellen
Vorneweg: Top!
Konvertiere das doch in eine Klasse. Einfach die lokalen Prozeduren in private Methoden überführen und die Hauptprozedur als public deklarieren. Dann würde ich für das IDL-Zeugs kein Record nehmen, sondern eine Klasse und das in eine TObjectList packen. Damit ist das aufräumen erledigt (OwnsObject = true). Deine Prozedur muss aufgeräumt werden. Ein 'try..finally LeerePIDLListe end' um alles herum erspaart dir die einzelnen Aufruf von 'LeerePIDLListe', denn bei einem 'exit' wird vorher der finally-Abschnitt aufgerufen. Und dann würde ich jeden einzelnen Schritt in der Prozedur in eine eigene Methode packen, z.B. aus :
Delphi-Quellcode:
wird (was passiert denn den da?)
// code above gets the Recycle Bin IShellFoldr Interface in RecycleI
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;
Delphi-Quellcode:
und
Const
NotFound = -1; ... i := FindeNameInListe(Dateiname); if i = NotFound then exit; // pItemIDL := PItemIDList(PPIDLItem(PIDLListe[i])^.IDL); ...
Delphi-Quellcode:
Extrahiere die Einzelschritte so, das jeder Schritt genau einen kleinen Beitrag zur Gesamtlösung liefert. Hier: "Sucht einen Dateinamen in der Liste und liefert den Index oder -1, wenn der Name nicht gefunden wurde". Mehr macht das Teil nicht und mehr muss es auch nicht machen.
function TPapierkorb.FindeNameInListe (const Dateiname : String) : Integer;
Begin For result := 0 to PIDLListe.Count - 1 do If SameText(PPIDLItem(PIDLListe[result])^.Dateiname,Dateiname) then exit; Result := -1 // nicht gefunden End; Deine Routine sähe dann in etwa so aus:
Delphi-Quellcode:
Das ist immer noch komplex (aus Sicht eines pingeligen Softwarearchitekten), aber wenn Du soweit bist, kann man noch weiter am Code rumschrauben, damit er verständlicher wird. Ziel sollte es sein, den Code ohne Kommentare intuitiv zu verstehen.
Procedure TPapierkorb.StelleDateiWiederher(const Dateiname : String);
... begin Result := False; Try PIDLListe := TObjectList.Create(true); RecycleI := GetRecycleBinInterace(); PapierkorbDateienAuflisten (PIDLListe); i := FindeNameInListe(Dateiname); if i=-1 then exit; // pItemIDL := PItemIDList(PPIDLItem(PIDLListe[i])^.IDL); If pItemIDL <> nil then RecycleFile(RecycleI, pItemIDL); Result := FileExists(Dateiname); finally PIDLListe.Free; end end; Eins zum Schluss: Ich habe deutsche und englische Bezeichnernamen vermischt: Ganz großer Käse! Entscheide dich für eine Sprache und dann zieh das durch. Vermeide Abkürzungen, außer, sie entsprechen dem Fachjargon ('PIDL') |
AW: Datei aus dem Papierkorb wiederherstellen
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:
|
AW: Datei aus dem Papierkorb wiederherstellen
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:
Aber ist es nicht so, das der Compiler eine Warnung ausspuckt, i sei nach dem Ende der Schleife undefiniert??? ;-)
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 |
AW: Datei aus dem Papierkorb wiederherstellen
Bei einer Schleife
Delphi-Quellcode:
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.
For i := 0 to PIDLListe.Count - 1
|
AW: Datei aus dem Papierkorb wiederherstellen
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? Zitat:
|
AW: Datei aus dem Papierkorb wiederherstellen
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. |
AW: Datei aus dem Papierkorb wiederherstellen
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:
Delphi-Quellcode:
Ausprobieren kann man das mit einem neuen Projekt, dazu eine Listbox, drei Buttons und ein Edit auf die Form bringen, TPapierkorb einbinden und dann:
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.DLL' name '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.
Delphi-Quellcode:
Eine Sache ist mir unklar: Muss der Anwender
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;
Delphi-Quellcode:
aufrufen?
Free
|
AW: Datei aus dem Papierkorb wiederherstellen
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:
Heiko
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; |
AW: Datei aus dem Papierkorb wiederherstellen
Wir wäre es, wenn man einfach das prüft, was man prüfen wollte?
"Wurde nichts gefunden?" statt "Bin ich am Ende der Liste?"
Delphi-Quellcode:
pItemIDL := nil;
For i := 0 to PIDLListe.Count - 1 do If SameFileName(PPIDLItem(PIDLListe[i])^.Dateiname, Dateiname) then begin pItemIDL := PItemIDList(PPIDLItem(PIDLListe[i])^.IDL); break; end; If not Assigned(pItemIDL) then begin // LeerePIDLListe; end; ![]() Und die Fehlerbehandlung im ersten Post ist echt grausam.
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:55 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