2005. január 27., csütörtök

Restore files from the recycle bin and delete files present in the recycle bin


Problem/Question/Abstract:

How to restore files from the recycle bin and delete files present in the recycle bin.

Answer:

To restore as well as delete file from the bin you need to make use of the following functions.

function SHQueryRecycleBin(pszrtootpath: pchar; QUERYRBINFO: pshqueryrbinfo): integer;
  stdcall; external 'shell32' name 'SHQueryRecycleBinA';
  //used to get the number of files in the bin.

function _FindFirstChangeNotification(lpPathName: PChar; bWatchSubtree: TWinBool;
  dwNotifyFilter:
  DWORD): THandle; stdcall; external kernel32 name 'FindFirstChangeNotificationA';
// used to notify the program when user has deleted a file

function SHEmptyRecycleBin(hwnd: thandle; pszRootPath: pchar; dwFlags: integer):
  integer; stdcall; external 'shell32.dll' name 'SHEmptyRecycleBinA';
  //This function empties the recycle bin.

In delphi the function FindFirstChangeNotification is already declared but in Delphi 3 and above it does not work correctly.If the second parameter is true then the function always returns an invalid handle.(visit http://members.aye.net/~bstowers/delphi/bugs/ for more info).

So you need to redeclare the FindFirstChangeNotification function as shown below.

type
  TWinBool = (winFalse, winTrue);

function _FindFirstChangeNotification(lpPathName: PChar; bWatchSubtree: TWinBool;
  dwNotifyFilter:
  DWORD): THandle; stdcall; external kernel32 name 'FindFirstChangeNotificationA';

The above function is used to refresh the list of files in the recyclebin when the user deletes  a file.

To use this function we have to first create a thread.This thread checks continuosly checks whether any file was deleted and then refreshes the list of deleted items.

For more information see delphi tips 'SHQUERYBINFO', 'SHemptyrecycleBin',
'Get the list of files from bin'.

Here is the code of the thread.

unit Unit2;

interface

uses
  Classes, SysUtils, windows;

type
  TFileChangeNotify = class(TThread)
  private

  protected
    procedure Execute; override;
    procedure filenotify; //refreshes the list when user has deleted a file.
  end;
var
  qh1: thandle;
implementation

uses
  unit1;

procedure TFileChangeNotify.filenotify;
begin
  form1.refreshlist;
end;

procedure TFileChangeNotify.Execute;
var
  pdir: pchar;
  st: integer;
  tmp: boolean;
begin
  pdir := 'C:\';
  qh1 := 0;
  qh1 := _FindFirstChangeNotification(pdir, Twinbool(1),
    FILE_NOTIFY_CHANGE_LAST_WRITE);
  while true do
  begin
    st := WaitForSingleObject(qh1, INFINITE);
    if st = WAIT_OBJECT_0 then
    begin
      Synchronize(filenotify);
      SHUpdateRecycleBinIcon;
    end;
    tmp := findnextchangenotification(qh1);
    if tmp = false then
      Terminate;
  end;
end;

end.

You need to add a Tlistview control to your form, and add two columns to it.
Here is the code of main program.

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, Menus, ComCtrls, unit2; {unit2 is the unit in which the thread resides}

const
  SHERB_NOCONFIRMATION = $1;
const
  SHERB_NOPROGRESSUI = $2;
const
  SHERB_NOSOUND = $4;

type
  TWinBool = (winFalse, winTrue);

type
  Tfbuf = packed record
    data: array[0..255] of char;
    u1: array[0..3] of char;
    recno: smallint;
    u2: array[0..18] of char;
  end;

type
  SHQUERYRBINFO = packed record
    cbSize: integer;
    i64Size: int64;
    i64NumItems: int64;
  end;
  pshqueryrbinfo = ^SHQUERYRBINFO;

function SHQueryRecycleBin(pszrtootpath: pchar; QUERYRBINFO: pshqueryrbinfo): integer;
  stdcall; external 'shell32' name 'SHQueryRecycleBinA';
function _FindFirstChangeNotification(lpPathName: PChar; bWatchSubtree: TWinBool;
  dwNotifyFilter: DWORD): THandle; stdcall; external kernel32 name
  'FindFirstChangeNotificationA';
function SHUpdateRecycleBinIcon: integer; stdcall; external 'shell32.dll';
function SHEmptyRecycleBin(hwnd: thandle; pszRootPath: pchar; dwFlags: integer):
  integer; stdcall; external 'shell32.dll' name 'SHEmptyRecycleBinA';

type
  TForm1 = class(TForm)
    RBinList: TListView;
    MainMenu1: TMainMenu;
    file1: TMenuItem;
    View1: TMenuItem;
    Refresh1: TMenuItem;
    Edit1: TMenuItem;
    SelectAll1: TMenuItem;
    Restore1: TMenuItem;
    N1: TMenuItem;
    Delete1: TMenuItem;
    N2: TMenuItem;
    Close1: TMenuItem;
    InvertSelection1: TMenuItem;
    procedure FormCreate(Sender: TObject);
    procedure Refresh1Click(Sender: TObject);
    procedure FormResize(Sender: TObject);
    procedure file1Click(Sender: TObject);
    procedure SelectAll1Click(Sender: TObject);
    procedure InvertSelection1Click(Sender: TObject);
    procedure Restore1Click(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure Close1Click(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure Delete1Click(Sender: TObject);
  private
    { Private declarations }
  public
    qh: thandle;
    procedure refreshlist;
    function updateinfo(fname: string): boolean;
      //Makes appropriate changes to the INFO2 file
    //present in recycled folder.
    procedure Restorefiles; //restores the selected files from the recycle bin.
    procedure deletefiles; //deletes the selected files from the recycle bin.
  end;

var
  Form1: TForm1;
  rbinfo: SHQUERYRBINFO;
  reccount: integer;
  fhandle: integer;
  monitorthread: TFileChangeNotify;

implementation

{$R *.DFM}

procedure tform1.deletefiles;
var
  i: integer;
  sname: string;
  dname: string;
begin
  monitorthread.Suspend;
  for i := 0 to rbinlist.Items.Count - 1 do
  begin
    if rbinlist.Items[i].Selected = true then
    begin
      sname := ExtractFileDrive(rbinlist.Items[i].SubItems[0]) + '\Recycled\DC' +
        rbinlist.Items[i].SubItems[1] + ExtractFileExt(rbinlist.Items[i].caption);
      dname := rbinlist.Items[i].SubItems[0] + rbinlist.Items[i].caption;
      deleteFile(sname);
      updateinfo(dname);
    end;
  end;
  monitorthread.Resume;
end;

function tform1.updateinfo(fname: string): boolean;
var
  rbuff: Tfbuf;
  fread: integer;
  tsize: integer;
  aname: pchar;
  ch: char;
begin
  result := false;
  ch := #0;
  fhandle := fileopen('C:\recycled\info2', fmOpenReadWrite or fmShareDenyNone);
  if fhandle > 0 then
  begin
    tsize := GetFileSize(fhandle, nil);
    setfilepointer(fhandle, 20, nil, FILE_BEGIN);
    fread := 20;
    while (fread
      begin
        fread := fread + fileread(fhandle, rbuff, 280);
        if rbuff.data[0] <> #0 then
        begin
          aname := pchar(@rbuff.data[0]);
          if StrComp(aname, pchar(fname)) = 0 then
          begin
            setfilepointer(fhandle, -280, nil, FILE_CURRENT);
            filewrite(fhandle, ch, 1);
            result := true;
            break;
          end;
        end;
      end;
      fileclose(fhandle);
  end;
end;

procedure tform1.refreshlist;
var
  rbuff: Tfbuf;
  fread: integer;
  tsize: integer;
  aname: pchar;
  fitem: tlistitem;
  dname: pchar;
  iconhandle: thandle;
  tmp: word;
  iconid: integer;
  icon: ticon;
begin
  monitorthread.Suspend;
  zeromemory(@rbuff, sizeof(rbuff));
  rbinlist.Items.Clear;
  fhandle := fileopen('C:\recycled\info2', fmOpenRead);
  if fhandle > 0 then
  begin
    tsize := GetFileSize(fhandle, nil);
    setfilepointer(fhandle, 20, nil, FILE_BEGIN);
    fread := 20;
    while (fread
      begin
        fread := fread + fileread(fhandle, rbuff, 280);
        if rbuff.data[0] <> #0 then
        begin
          aname := pchar(@rbuff.data[0]);
          dname := pchar((ExtractFileDrive(aname) + '\Recycled\DC' + inttostr
            (rbuff.recno) + extractfileext(aname)));
          iconhandle := ExtractAssociatedIcon(HInstance, dname, tmp);
          icon.Handle := iconhandle;
          iconid := largeimagelist.AddIcon(icon);
          fitem := rbinlist.Items.add;
          fitem.ImageIndex := iconid;
          fitem.Caption := ExtractFileName(aname);
          fitem.SubItems.Add(ExtractFilePath(aname));
          fitem.SubItems.add(inttostr(rbuff.recno));
        end;
      end;
      fileclose(fhandle);
  end;
  rbinfo.cbSize := sizeof(rbinfo);
  rbinfo.i64NumItems := 0;
  rbinfo.i64Size := 0;
  SHQueryRecycleBin('C:\', @rbinfo);
  if (rbinlist.items.count = 0) and (rbinfo.i64Size <> 0) then
    SHEmptyRecycleBin(form1.handle, 'C:\', SHERB_NOCONFIRMATION or
      SHERB_NOPROGRESSUI);
  monitorthread.resume;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  monitorthread := TFileChangeNotify.Create(false);
end;

procedure TForm1.Refresh1Click(Sender: TObject);
begin
  refreshlist;
end;

procedure TForm1.FormResize(Sender: TObject);
begin
  rbinlist.width := form1.width - 8;
  rbinlist.height := form1.height - 48;
end;

procedure TForm1.file1Click(Sender: TObject);
begin
  if rbinlist.SelCount > 0 then
  begin
    restore1.enabled := true;
    Delete1.enabled := true;
  end
  else
  begin
    restore1.enabled := false;
    Delete1.enabled := false;
  end;
end;

procedure TForm1.SelectAll1Click(Sender: TObject);
var
  i: integer;
begin
  for i := 0 to rbinlist.Items.Count - 1 do
    rbinlist.Items[i].Selected := true;
end;

procedure TForm1.InvertSelection1Click(Sender: TObject);
var
  i: integer;
begin
  for i := 0 to rbinlist.Items.Count - 1 do
    rbinlist.Items[i].Selected := not (rbinlist.Items[i].Selected);
end;

procedure tform1.Restorefiles;
var
  i: integer;
  sname: string;
  dname: string;
begin
  monitorthread.Suspend;
  for i := 0 to rbinlist.Items.Count - 1 do
  begin
    if rbinlist.Items[i].Selected = true then
    begin
      sname := ExtractFileDrive(rbinlist.Items[i].SubItems[0]) + '\Recycled\DC' +
        rbinlist.Items[i].SubItems[1] + ExtractFileExt(rbinlist.Items[i].caption);
      dname := rbinlist.Items[i].SubItems[0] + rbinlist.Items[i].caption;
      MoveFile(pchar(sname), pchar(dname));
      updateinfo(dname);
    end;
  end;
  monitorthread.Resume;
end;

procedure TForm1.Restore1Click(Sender: TObject);
begin
  restorefiles;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  if qh <> INVALID_HANDLE_VALUE then
    FindCloseChangeNotification(qh);
  monitorthread.Terminate;
end;

procedure TForm1.Close1Click(Sender: TObject);
begin
  form1.close;
end;

procedure TForm1.FormShow(Sender: TObject);
begin
  refreshlist;
end;

procedure TForm1.Delete1Click(Sender: TObject);
begin
  deletefiles;
end;

end.

Nincsenek megjegyzések:

Megjegyzés küldése