Этим занимается API функция оболочки под названием SHFileOperation, объявленная в shellapi.h. Для того, чтобы воспользоваться этой функцией, необходимо заполнить специальную структуру SHFILEOPSTRUCT, которая указывает, какую операцию необходимо проделать, какой файл необходимо удалить, а так же другую важную информацию: int SHFileOperation(LPSHFILEOPSTRUCT lpFileOp);
struct SHFILEOPSTRUCT{ HWND hwnd; // NULL (диалога прогреса, не // используем) UINT wFunc; // FO_DELETE (операция удаления) LPCTSTR pFrom; // имя файла(ов) для удаления LPCTSTR pTo; // NULL (для удаления не используется) FILEOP_FLAGS fFlags; // см. ниже BOOL fAnyOperationsAborted; // (возвращает TRUE если пользователь // прервал) не используем LPVOID hNameMappings; // для удаления не используется LPCSTR lpszProgressTitle; // для удаления не используется };
// Используемые флаги #define FOF_SILENT 0x0004 // не показывать процесс удаления #define FOF_NOERRORUI 0x0400 // не выводить ошибки #define FOF_ALLOWUNDO 0x0040 // ОБЯЗАТЕЛЬНО для корзины!!! #define FOF_NOCONFIRMATION 0x0010 // Не спрашивать пользователя OK // для подтверждения удаления
SHFileOperation позволяет копировать, удалять, перемещать или переименовывать один или несколько файлов. Итак, теперь о главном. Для того, чтобы удалить файл с помещением его в корзину, необходимо использовать флаг FOF_ALLOWUNDO, присвоив значение pFrom равное FO_DELETE. Если же задать только имя файла без пути, то файл будет удалён без помещения в корзину. Прилагаемый пример содержит небольшой класс CRecycleFile, который призван упростить данную процедуру. Вот элементарный приём использования данного класса: LPCTSTR pszPathName = GetFileNameSomehow(); // полный путь с именем! CRecycleFile rf; rf.Recycle(pszPathName);
Что может быть проще этого? Исходный код RecycFile.h //////////////////////////////////////////////////////////////// // MSDN — April 2001 // If this code works, it was written by Paul DiLascia. // If not, I don't know who wrote it. // Compiles with Visual C++ 6.0. Runs on Windows 98 and probably Windows // 2000 too. // #include <shellapi.h>
////////////////// // CRecycleFile — sends a file to the Recycle Bin. // Note derived from SHFILEOPSTRUCT. // class CRecycleFile : public SHFILEOPSTRUCT { protected: public: CRecycleFile(); ~CRecycleFile() { } int Recycle(LPCTSTR pszPath, BOOL bDelete=FALSE); }; RecycFile.cpp //////////////////////////////////////////////////////////////// // MSDN — April 2001 // If this code works, it was written by Paul DiLascia. // If not, I don't know who wrote it. // Compiles with Visual C++ 6.0. Runs on Windows 98 and probably Windows // 2000 too. // #include <windows.h> #include <tchar.h> #include "RecycFile.h"
////////////////// // Constructor initializes SHFILEOPSTRUCT with reasonable // defaults. You can override if you like. Go ahead, make my day. // CRecycleFile::CRecycleFile() { memset((SHFILEOPSTRUCT*)this,0,sizeof(SHFILEOPSTRUCT)); fFlags |= FOF_SILENT; // don't report progress fFlags |= FOF_NOERRORUI; // don't report errors fFlags |= FOF_NOCONFIRMATION; // don't confirm delete }
////////////////// // Send a file to the recycle bin. Args: // - full pathname of file. // - bDelete: if TRUE, really delete file (no recycle bin) // int CRecycleFile::Recycle(LPCTSTR pszPath, BOOL bDelete) { // Copy pathname to double-NULL-terminated string. // TCHAR buf[_MAX_PATH + 1]; // allow one more character _tcscpy(buf, pszPath); // copy caller's path name buf[_tcslen(buf)+1]=0; // need two NULLs at end
// Set SHFILEOPSTRUCT params for delete operation // wFunc = FO_DELETE; // REQUIRED: delete operation pFrom = buf; // REQUIRED: which file(s) pTo = NULL; // MUST be NULL if (bDelete) { // if delete requested.. fFlags &= ~FOF_ALLOWUNDO; // ..don't use Recycle Bin } else { // otherwise.. fFlags |= FOF_ALLOWUNDO; // ..send to Recycle Bin } return SHFileOperation(this); // do it! } recycle.cpp //////////////////////////////////////////////////////////////// // MSDN — April 2001 // If this code works, it was written by Paul DiLascia. // If not, I don't know who wrote it. // Compiles with Visual C++ 6.0. Runs on Windows 98 and probably Windows // 2000 too. // #pragma once #pragma warning(disable:4786) // disable annoying C4786 #define VC_EXTRALEAN // Exclude rarely used stuff from // headers #include <windows.h> #include <stdio.h> #include <direct.h> // getcwd #include <conio.h> // getche #include <tchar.h> #include <assert.h> #include <string> // STL string class #include <list> // STL list class #include <sys/stat.h> // stat function #include "RecycFile.h"
using namespace std; // use STL typedef list<string> CStringList; // like MFC
// I'm really roughing it here—need my own TRACE and ASSERT! #define ASSERT assert #ifdef _DEBUG #define TRACE _tprintf #else #define TRACE 1 ? (void)0 : ::_tprintf #endif
// pre-declare functions void usage(); void help(); string GetCurrentDir(); string MakeAbsolute(const string& relname); BOOL confirm(LPCTSTR pFileName); LPCTSTR GetErrorMsg(int err);
// global command-line switches BOOL bPrompt=FALSE; // prompt each file BOOL bQuiet=FALSE; // don't display messages BOOL bDisplayOnly=FALSE; // display results only; don't actually recycle BOOL bZap=FALSE; // really delete (don't recycle)
// test if file exists inline fileexists(LPCTSTR pFilename) { struct stat st; return stat(pFilename, &st)==0; }
// check for switch: / or - inline BOOL isswitch(TCHAR c) { return c==L'/' || c==L'-'; }
////////////////// // Main function expects argv already expanded for wildcards. You must // link with setargv.obj!! // int main(int argc, TCHAR* argv[], TCHAR* envp[]) { // Parse command line, building list of file names. // Switches can come in any order. // CStringList files; for (int i=1; i<argc; i++) { if (isswitch(argv[i][0])) { switch(tolower(argv[i][1])) { case 'q': bQuiet=TRUE; break; case 'n': bDisplayOnly=TRUE; break; case 'p': bPrompt=TRUE; break; case 'z': bZap=TRUE; break; case '?': help(); return 0; default: usage(); return 0; } } else { // Got a file name. Make it absolute and add to list. files.push_back(MakeAbsolute(argv[i])); } }
if (files.empty()) { // No files specified: tell bozo user how to use this command. usage(); return 0; }
// Delete (recycle) all the files in the list int nDel=0; CStringList::iterator it;
// loop over list of files and recycle each one for (it=files.begin(); it!=files.end(); it++) { const string& filename = *it; LPCTSTR pFileName = filename.c_str();
// Only recycle if file exists. If the user types a wildcard—eg: // recycle foo.* and there are no files that match "foo.*", then // setargv passes the wildcard expression unexpanded—but obviously // there is no such file. // if (fileexists(pFileName)) {
if (!bQuiet && !bPrompt) { // tell user I'm recycling this file _ftprintf(stderr,_T("%s %s\n"), bZap ? _T("Deleting") : _T("Recycling"), pFileName); }
if (!bDisplayOnly) { if (!bPrompt || confirm(pFileName)) { // Finally! Recycle the file. Use CRecycleFile. CRecycleFile rf; int err = rf.Recycle(pFileName,bZap); if (err==0) { nDel++; } else { // Can't recycle: display error message _ftprintf(stderr,_T("Error %d: %s"), err, GetErrorMsg(err)); } } } } else { _ftprintf(stderr,_T("File not found \"%s\"\n"), pFileName); } } if (!bQuiet) { _ftprintf(stderr,_T("%d files recycled\n"),nDel); } return 0; }
//////////////// // Duh. // void usage() { _tprintf(_T("Usage: RECYCLE [/QNPZ?] file...\n")); }
//////////////// // Ditto duh. // void help() { _tprintf(_T("Purpose: Send one or more files to the recycle bin.\n")); _tprintf(_T("Format: RECYCLE [/Q /N /P /Z] file....\n")); _tprintf(_T(" /Q(uiet) no messages\n")); _tprintf(_T(" /N(othing) don't delete, just show files\n")); _tprintf(_T(" /P(rompt) confirm each file\n")); _tprintf(_T(" /Z(ap) really delete—same as del\n")); }
////////////////// // Make a file name absolute with respect to current directory. // string MakeAbsolute(const string& relname) { // Get current directory. Since cwd is static this happens only once. static const string cwd = GetCurrentDir();
string absname; if (relname[0] && relname[1] && relname[1]==':') { // relname is already absolute absname = relname;
} else if (relname[0]=='\\') { // relname begins with \ — add drive letter and colon absname = string(cwd,0,2); absname += relname;
} else { // file name begins with letter: // relname begins with a letter — prepend cwd absname = cwd; absname += relname; } return absname; }
////////////////// // Get current directory. For some reason unknown to mankind, getcwd // returns "C:\FOO" (no \ at end) if dir is NOT root; yet it returns "C:\" // (with \) if cwd is root. Go figure. To make the result consistent for // appending a file name, GetCurrentDir adds the missing \ if needed. // Result always has final \. // string GetCurrentDir() { TCHAR dir[MAX_PATH]; _tgetcwd(dir, sizeof(dir)/sizeof(TCHAR));
// Append '\' if needed int lastchar = _tcslen(dir)-1; if (lastchar>0 && dir[lastchar] != '\\') // if last char isn't \ .. _tcscat(dir,_T("\\")); // ..add one
return dir; // compiler will convert to string! }
////////////////// // Get user confirmation to recycle/delete a file // BOOL confirm(LPCTSTR pFileName) { while (TRUE) { _tprintf(_T("Recycle %s (Y/N/All)? "), pFileName); char c = _getche(); if (c=='') { _tprintf(_T("^C\n")); exit(0); } _tprintf(_T("\n")); switch (tolower(c)) { case 'a': bPrompt=FALSE; // fall through case 'y': return TRUE; case 'n': return FALSE; } } }
////////////////// // Get Windows system error message // LPCTSTR GetErrorMsg(int err) { const BUFSIZE = 512; static TCHAR buf[BUFSIZE]; buf[0]=0;
// Only Windows could have a function this confusing to get a simple // error message. FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, // source err, // error code 0, // language ID buf, // buffer to receive message BUFSIZE, // size of buf NULL); // arguments return buf; }
|