Помещаем файл в корзину (Recycle Bin)

Этим занимается 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;
}