Verschieben von Dateien in den Papierkorb

Veröffentlicht: 28. Jan 2002 | Aktualisiert: 15. Jun 2004

Von Paul DiLascia

Die API-Funktion SHFileOperation ermöglicht die Verwendung des Papierkorbs auf einfache Weise. Dieser Artikel stellt eine einfache Klasse vor, mit der diese Funktion gekapselt wird.

Diesen Artikel können Sie hier lesen dank freundlicher Unterstützung der Zeitschrift:

Bild02

Frage

Ich möchte mit CFile::Remove (oder etwas Ähnlichem) eine Datei löschen. Allerdings soll die Datei nicht spurlos verschwinden, sondern in den Papierkorb wandern, damit der Anwender sie bei Bedarf rekonstruieren kann. Geht das ohne großen Aufwand? Wenn nicht, würde ich meine Datei einfach in den Papierkorb kopieren. Meine Frage lautet also im Kern, wie man vom Programm aus auf den Papierkorb zugreifen kann.

 

Antwort

Die Antwort auf Ihr Problem ist eine Funktion namens SHFileOperation, die in shellapi.h deklariert wird. Vor dem Aufruf von SHFileOperation füllen Sie eine spezielle SHFILEOPSTRUCT-Struktur aus (Listing L1), der Windows entnimmt, welcher Auftrag auszuführen ist, um welche Datei es sich handelt und derlei Dinge mehr. Mit SHFileOperation können Sie eine oder mehrere Dateien kopieren, löschen, verschieben oder umbenennen. Das Besondere an SHFileOperation ist, dass die Funktion auf einer relativ hohen Ebene angesiedelt ist, nämlich in der Shell und nicht in den Tiefen des Betriebssystems, in denen sich Dateifunktionen normalerweise herumtreiben. Wenn Sie mit SHFileOperation Dateien kopieren, werden die entsprechenden Kopierfunktionen der Shell aufgerufen, sofern vorhanden (genauer gesagt, die zuständigen "shell copy handler"). Und wie heißt es so schön in der Dokumentation? "Wird sie zum Löschen einer Datei benutzt, versucht SHFileOperation, die gelöschte Datei in den Papierkorb zu werfen."

L1 Die Funktion SHFileOperation

///////////////////////////////////////////////////////// 
// Mit SHFileOperation/LPSHFILEOPSTRUCT können Dateien  
// verschoben, kopiert, gelöscht oder umbenannt werden. 
// Ich zeige im Folgenden nur, wie man Dateien in den 
// Papierkorb wirft. Einzelheiten finden Sie in der Datei 
// RecycFile.h,cpp - PD 
// 
int SHFileOperation(LPSHFILEOPSTRUCT lpFileOp); 
struct SHFILEOPSTRUCT{  
   HWND hwnd;           // NULL (Fortschrittsdialog, 
                        // unbenutzt) 
   UINT wFunc;          // FO_DELETE (Löschung) 
   LPCTSTR pFrom;       // Dateiname(n) 
   LPCTSTR pTo;         // NULL (bei Löschung unbenutzt) 
   FILEOP_FLAGS fFlags;         // siehe unten 
   BOOL fAnyOperationsAborted;  // gibt bei Abbruch TRUE 
                                // zurück (unbenutzt) 
   LPVOID hNameMappings;        // bei Löschung unbenutzt 
   LPCSTR lpszProgressTitle;    // bei Löschung unbenutzt 
}; 
// Die benutzten Flags 
#define FOF_SILENT      0x0004     // keine Angabe über  
                                   // Fortschritt 
#define FOF_NOERRORUI   0x0400     // keine Fehler-UI 
#define FOF_ALLOWUNDO   0x0040     // für Papierkorb  
                                   // erforderlich! 
#define FOF_NOCONFIRMATION 0x0010  // keine Rückfrage

Wenn Sie das zu Hause ausprobieren, wird es beim ersten Versuch vermutlich nicht funktionieren. In der Dokumentation übersieht man nämlich leicht, dass auch das mysteriöse Flag FOF_ALLOWUNDO gesetzt werden muss. Im Rückblick ist die Begründung dafür natürlich einleuchtend. Die Verschiebung einer Datei in den Papierkorb macht es dem Anwender natürlich möglich, den Löschvorgang rückgängig zu machen. Versucht man aber zum ersten Mal, sich in diese Funktion hineinzudenken, ist die Dokumentation doch ziemlich kryptisch. Man muss sie ungefähr so wie eine Ankündigung von Alan Greenspan über die ökonomische Entwicklung lesen. Sehr, sehr sorgfältig.
In diesem Fall ist der entscheidende Hinweis eine Bemerkung über nicht vollständig qualifizierte (relative) Dateinamen, in der es heißt: "Wenn pFrom auf einen Dateinamen gesetzt wird, führt die Löschung der Datei mit FO_DELETE nicht zu deren Verschiebung in den Papierkorb, selbst wenn das Flag FOF_ALLOWUNDO gesetzt ist. Sie müssen den vollständigen Pfadnamen angeben." Woraus ein hinreichend sorgfältiger Leser schließen könnte: Wenn das Flag FOF_ALLOWUNDO gesetzt und der Pfadname vollständig ist, wird SHFileOperation die Datei in den Papierkorb verschieben.
In der Tat. Um eine Datei nach dem Löschen rekonstruieren zu können, müssen Sie deren vollständigen Pfadnamen angeben und natürlich das Flag FOF_ALLOWUNDO. Damit Sie sich ansehen können, wie das alles in der Praxis funktioniert, habe ich eine kleine Klasse namens CRecycleFile geschrieben, die eine Datei löscht und in den Papierkorb verfrachtet (Listing L2), sowie ein Konsolenprogramm mit dem Namen RECYCLE, das mit dieser Klasse arbeitet. RECYCLE gefällt mir eigentlich ziemlich gut. Damit kann man in der DOS-Box Dateien so löschen, dass sie sich später bei Bedarf wiederbeleben lassen. Bild B1 zeigt das Programm bei der Arbeit. Wenn Sie eine Befehls-Shell wie 4DOS oder 4NT benutzen, können Sie sogar den Löschbefehl auf "recycle" setzen, wodurch sich der Del-Befehl von MS-DOS vom Löscher zum Recycler wandelt. Gelegentlich spart das eine Menge Arbeit.

L2 Die Klasse CRecycleFile

RecycFile.h 
///////////////////////////////////////////////////////// 
// System Journal, August/September 2001 
// Autor: Paul DiLascia. 
// 
// Lässt sich mit Visual C++ 6.0 kompilieren und läuft  
// unter Windows 98 (und wohl auch unter Windows NT). 
#include <shellapi.h> 
////////////////// 
// CRecycleFile  -  schickt eine Datei in den Papierkorb. 
// Beachten Sie die Ableitung von SHFILEOPSTRUCT. 
// 
class CRecycleFile : public SHFILEOPSTRUCT { 
protected: 
public: 
   CRecycleFile(); 
   ~CRecycleFile() { } 
   int Recycle(LPCTSTR pszPath, BOOL bDelete=FALSE); 
}; 
RecycFile.cpp 
///////////////////////////////////////////////////////// 
// System Journal, August/September 2001 
// Autor: Paul DiLascia. 
// 
// Lässt sich mit Visual C++ 6.0 kompilieren und läuft  
// unter Windows 98 (und wohl auch unter Windows NT). 
#include <windows.h> 
#include <tchar.h> 
#include "RecycFile.h" 
////////////////// 
// Der Konstruktor initialisiert SHFILEOPSTRUCT mit sinn- 
// vollen Werten. Bei Bedarf können Sie natürlich andere 
// Werte vorgeben. 
// 
CRecycleFile::CRecycleFile() 
{ 
   memset((SHFILEOPSTRUCT*)this,0,sizeof(SHFILEOPSTRUCT)); 
   fFlags |= FOF_SILENT;     // keine Fortschrittsanggabe 
   fFlags |= FOF_NOERRORUI;  // keine Fehlermeldung 
   fFlags |= FOF_NOCONFIRMATION; // keine Bestätigung 
} 
////////////////// 
// Schicke die Datei in den Papierkorb. Argumente: 
//  - vollständiger Pfadname der Datei. 
//  - bDelete: bei TRUE unwiederbringlich löschen 
//             (kein Papierkorb) 
// 
int CRecycleFile::Recycle(LPCTSTR pszPath, BOOL bDelete) 
{ 
   // Kopiere den Pfadnamen in einen String, der mit  
   // einer doppelten Null abgeschlossen wird. 
   // 
   TCHAR buf[_MAX_PATH + 1]; // ein Zeichen mehr zulassen 
   _tcscpy(buf, pszPath);    // Pfadnamen kopieren 
   buf[_tcslen(buf)+1]=0;    // zweite Null anhängen 
   // Initiallisiere SHFILEOPSTRUCT für die Löschung 
   // 
   wFunc = FO_DELETE;             // löschen 
   pFrom = buf;                   // Datei(en) 
   pTo = NULL;                    // muss NULL sein 
   if (bDelete) {                 // Echte Löschung? 
      fFlags &= ~FOF_ALLOWUNDO;   // OK, kein Papierkorb. 
   } else {                       // ...andernfalls... 
      fFlags |= FOF_ALLOWUNDO;    // in den Papierkorb. 
   } 
   return SHFileOperation(this);  // Tu es! 
}

Bild01

B1 RECYCLE bei der Arbeit.

Ich habe die Klasse CRecycleFile so konzipiert, dass sie sich relativ einfach benutzen lässt. Und so geht's:

LPCTSTR pszPathName = GetFileNameSomehow(); // voller Pfadname! 
CRecycleFile rf; 
rf.Recycle(pszPathName);

Einfacher geht's kaum. CRecycleFile wird von der Struktur SHFILEOPSTRUCT abgeleitet. Bei Bedarf können Sie natürlich andere Werte vorgeben. Für die normale Löschung via Papierkorb ist das aber nicht erforderlich, weil der Konstruktor SHFILEOPSTRUCT mit brauchbaren Werten initialisiert. CRecycleFile::Recycle erledigt die Arbeit und wirft die Datei in den Papierkorb. Der schwierigste Teil, nachdem man sich mit dem magischen Flag FOF_ALLOWUNDO vertraut gemacht hat, ist das Umkopieren des Pfadnamens in einen zusätzlichen Puffer, damit man eine zusätzliche Null anhängen kann, als Kennzeichnung des Endes der Namensliste. SHFileOperation kann nämlich im selben Arbeitsgang auch mehrere Dateien löschen, wobei die Pfadnamen der einzelnen Dateien aneinandergehängt werden. Eine zusätzliche Null kennzeichnet dann das Ende dieser Liste, auch wenn die Liste vielleicht nur einen einzigen Eintrag enthält. Aber damit brauchen Sie sich nicht zu langweilen, weil sich CRecycleFile darum kümmert. Mit CRecycleFile wird das Löschen und Rekonstruieren von gelöschten Dateien zum Kinderspiel!