C# Programming

Image

ファイルを削除してゴミ箱へ

開発環境: Visual Studio 2003 

1.目次

2.目的

ファイルを削除してゴミ箱に入れたいと言う質問があったので、まとめました。

3.参考書

(1) MSDN(US) SHFileOperation
(2) Code Project

4.ファイルを削除する方法

ファイルを削除するには、普通 System.IO.File.Delete(ファイル名) です。
ですが、この場合ファイルはゴミ箱には入らず、削除されてしまいます。
また、System.IO.File.Delete のオプションを指定することにより、ゴミ箱にいれるようなこともできません。

これは、ゴミ箱のようなプラットフォームに依存したフレームワークは提供しないと言うポリシーなのかもしれません。
では、どうすればファイルを削除したときにゴミ箱に行くのかと言うと、ゴミ箱がどのようなメカニズムで提供されているのか
を考えれば想像がつきます。
OS 自体は、そんなゴミ箱の面倒なんか見てくれません。
Windows シェルがゴミ箱の面倒を見てくれています。
ということで、Windows シェルを提供しているベースの ”シェルAPI” が思い当たると思います。

関連するシェルAPIには次のものがあります。
Shell API説明
SHFileOperation()ファイルシステムオブジェクトのコピー、移動、リネーム、削除。
SHQueryRecycleBin()指定ドライブのゴミ箱のサイズ、アイテムの数の取得.
SHEmptyRecycleBin()指定ドライブのゴミ箱を空にする。.
ということで、SHFileOperation を使えば、ファイルを削除してゴミ箱に入れることができます。
でも、これらを使うには、プラットフォーム呼び出しをしなければなりません。

5.SHFileOperation のプラットフォーム呼び出し

必要となる定数、構造体、メソッドは次のとおりです。

定数
                public enum FOFunc : uint 
                {               
                        FO_MOVE = 0x0001,
                        FO_COPY = 0x0002,
                        FO_DELETE = 0x0003,
                        FO_RENAME = 0x0004
                }

                public enum FOFlags : ushort
                {
                        FOF_MULTIDESTFILES = 0x0001,
                        FOF_CONFIRMMOUSE = 0x0002,
                        FOF_SILENT = 0x0004,  // don't create progress/report
                        FOF_RENAMEONCOLLISION = 0x0008,
                        FOF_NOCONFIRMATION = 0x0010,  // Don't prompt the user.
                        FOF_WANTMAPPINGHANDLE = 0x0020,  // Fill in SHFILEOPSTRUCT.hNameMappings
                        // Must be freed using SHFreeNameMappings
                        FOF_ALLOWUNDO = 0x0040,
                        FOF_FILESONLY = 0x0080,  // on *.*, do only files
                        FOF_SIMPLEPROGRESS = 0x0100,  // means don't show names of files
                        FOF_NOCONFIRMMKDIR = 0x0200,  // don't confirm making any needed dirs
                        FOF_NOERRORUI = 0x0400,  // don't put up error UI
                        FOF_NOCOPYSECURITYATTRIBS = 0x0800,  // dont copy NT file Security Attributes
                        FOF_NORECURSION = 0x1000,  // don't recurse into directories.
                        FOF_NO_CONNECTED_ELEMENTS = 0x2000,  // don't operate on connected elements.
                        FOF_WANTNUKEWARNING = 0x4000,  // during delete operation, warn if nuking instead of recycling (partially overrides FOF_NOCONFIRMATION)
                        FOF_NORECURSEREPARSE = 0x8000  // treat reparse points as objects, not containers
                }
構造体
                // typedef struct _SHFILEOPSTRUCT 
                //              {
                //                      HWND hwnd;
                //                      UINT wFunc;
                //                      LPCTSTR pFrom;
                //                      LPCTSTR pTo;
                //                      FILEOP_FLAGS fFlags;
                //                      BOOL fAnyOperationsAborted;
                //                      LPVOID hNameMappings;
                //                      LPCTSTR lpszProgressTitle;
                //              } SHFILEOPSTRUCT, *LPSHFILEOPSTRUCT;
                [StructLayout(LayoutKind.Sequential)]
                        public struct SHFILEOPSTRUCT 
                {
                        public IntPtr hwnd;
                        public FOFunc wFunc;
                        [MarshalAs(UnmanagedType.LPWStr)]
                        public string pFrom;
                        [MarshalAs(UnmanagedType.LPWStr)]
                        public string pTo;
                        public FOFlags fFlags;
                        public bool fAnyOperationsAborted;
                        public IntPtr hNameMappings;
                        [MarshalAs(UnmanagedType.LPWStr)]
                        public string lpszProgressTitle;
                }
プロトタイプ宣言
                // int SHFileOperation(          LPSHFILEOPSTRUCT lpFileOp
                //      );
                // Unicode を明示的に指定する必要があります。
                [DllImport("Shell32.dll", CharSet = CharSet.Unicode)]
                public static extern int SHFileOperation(
                        ref SHFILEOPSTRUCT lpFileOp
                        );

5.1 SHFileOperation のソースコード

これらをクラスにまとめたのが次のソースコードです。
using System;
using System.Runtime.InteropServices;

namespace Uchukamen.util
{
        /// <summary>
        /// Shell の概要の説明です。
        /// </summary>
        public class Shell
        {
                public Shell()
                {
                }

                public enum FOFunc : uint 
                {               
                        FO_MOVE = 0x0001,
                        FO_COPY = 0x0002,
                        FO_DELETE = 0x0003,
                        FO_RENAME = 0x0004
                }

                public enum FOFlags : ushort
                {
                        FOF_MULTIDESTFILES = 0x0001,
                        FOF_CONFIRMMOUSE = 0x0002,
                        FOF_SILENT = 0x0004,  // don't create progress/report
                        FOF_RENAMEONCOLLISION = 0x0008,
                        FOF_NOCONFIRMATION = 0x0010,  // Don't prompt the user.
                        FOF_WANTMAPPINGHANDLE = 0x0020,  // Fill in SHFILEOPSTRUCT.hNameMappings
                        // Must be freed using SHFreeNameMappings
                        FOF_ALLOWUNDO = 0x0040,
                        FOF_FILESONLY = 0x0080,  // on *.*, do only files
                        FOF_SIMPLEPROGRESS = 0x0100,  // means don't show names of files
                        FOF_NOCONFIRMMKDIR = 0x0200,  // don't confirm making any needed dirs
                        FOF_NOERRORUI = 0x0400,  // don't put up error UI
                        FOF_NOCOPYSECURITYATTRIBS = 0x0800,  // dont copy NT file Security Attributes
                        FOF_NORECURSION = 0x1000,  // don't recurse into directories.
                        FOF_NO_CONNECTED_ELEMENTS = 0x2000,  // don't operate on connected elements.
                        FOF_WANTNUKEWARNING = 0x4000,  // during delete operation, warn if nuking instead of recycling (partially overrides FOF_NOCONFIRMATION)
                        FOF_NORECURSEREPARSE = 0x8000  // treat reparse points as objects, not containers
                }


                // typedef struct _SHFILEOPSTRUCT 
                //              {
                //                      HWND hwnd;
                //                      UINT wFunc;
                //                      LPCTSTR pFrom;
                //                      LPCTSTR pTo;
                //                      FILEOP_FLAGS fFlags;
                //                      BOOL fAnyOperationsAborted;
                //                      LPVOID hNameMappings;
                //                      LPCTSTR lpszProgressTitle;
                //              } SHFILEOPSTRUCT, *LPSHFILEOPSTRUCT;
                [StructLayout(LayoutKind.Sequential)]
                        public struct SHFILEOPSTRUCT 
                {
                        public IntPtr hwnd;
                        public FOFunc wFunc;
                        [MarshalAs(UnmanagedType.LPWStr)]
                        public string pFrom;
                        [MarshalAs(UnmanagedType.LPWStr)]
                        public string pTo;
                        public FOFlags fFlags;
                        public bool fAnyOperationsAborted;
                        public IntPtr hNameMappings;
                        [MarshalAs(UnmanagedType.LPWStr)]
                        public string lpszProgressTitle;
                }

                // int SHFileOperation(          LPSHFILEOPSTRUCT lpFileOp
                //      );
                // Unicode を明示的に指定する必要があります。
                [DllImport("Shell32.dll", CharSet = CharSet.Unicode)]
                public static extern int SHFileOperation(
                        ref SHFILEOPSTRUCT lpFileOp
                        );
        }
}

6.SHFileOperation 関数の呼び出し。

上記クラスを使って、ファイルを削除してゴミ箱に入れるには、例えば次のように呼び出します。
ファイルを削除してゴミ箱に入れるコード。
using Uchukamen.util;
....
Shell.SHFILEOPSTRUCT sh = new Shell.SHFILEOPSTRUCT();
                        
sh.hwnd = this.Handle;
sh.wFunc = Shell.FOFunc.FO_DELETE;
sh.pFrom = ファイルパス + '\0' + '\0';
sh.pTo = null;
sh.fFlags = Shell.FOFlags.FOF_ALLOWUNDO;
sh.fAnyOperationsAborted = false;
sh.hNameMappings = IntPtr.Zero;
sh.lpszProgressTitle = "Hello";
int result = Shell.SHFileOperation(ref sh);

sh.wFunc では、FO_DELETE で削除を指定します。
sh.fFlags では、FOF_ALLOWUNDO でアンドゥできるようにします。
このときのパラメータを変更することにより、ダイアログを出す、出さないなどを設定できます。

注意: ファイル名の指定方法
それから、ちょっとトリッキーなのが、ファイル名の指定方法です。

MSDN のヘルプには次のように記載されています。
"Each file name must be terminated by a single NULL character. An additional NULL character must be appended to the end of the final name to indicate the end of pFrom. " ということで、ヌル文字がファイル名とファイル名のターミネータとして使われ、 さらにpForm の最後をあらわすためにもう1つヌル文字を追加する必要があります。

例: sh.pFrom = ファイルパス + '\0' + '\0'; sh.pFrom = @"c:\temp\test" + '\0' + @"c:\temp\test\test.txt" + '\0' + '\0';
これで、実行すると、次のようなダイアログが出て、ゴミ箱行きになります。
Image

6.1 テスト用ソースコード

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.IO;
using Uchukamen.util;

namespace Uchukamen
{
        /// <summary>
        /// Form1 の概要の説明です。
        /// </summary>
        public class Form1 : System.Windows.Forms.Form
        {
                private System.Windows.Forms.Button button1;
                private System.Windows.Forms.OpenFileDialog openFileDialog1;
                /// <summary>
                /// 必要なデザイナ変数です。
                /// </summary>
                private System.ComponentModel.Container components = null;

                public Form1()
                {
                        //
                        // Windows フォーム デザイナ サポートに必要です。
                        //
                        InitializeComponent();

                        //
                        // TODO: InitializeComponent 呼び出しの後に、コンストラクタ コードを追加してください。
                        //
                }

                /// <summary>
                /// 使用されているリソースに後処理を実行します。
                /// </summary>
                protected override void Dispose( bool disposing )
                {
                        if( disposing )
                        {
                                if (components != null) 
                                {
                                        components.Dispose();
                                }
                        }
                        base.Dispose( disposing );
                }

                #region Windows フォーム デザイナで生成されたコード 
                /// <summary>
                /// デザイナ サポートに必要なメソッドです。このメソッドの内容を
                /// コード エディタで変更しないでください。
                /// </summary>
                private void InitializeComponent()
                {
            this.button1 = new System.Windows.Forms.Button();
            this.openFileDialog1 = new System.Windows.Forms.OpenFileDialog();
            this.SuspendLayout();
            // 
            // button1
            // 
            this.button1.Location = new System.Drawing.Point(160, 128);
            this.button1.Name = "button1";
            this.button1.TabIndex = 0;
            this.button1.Text = "ゴミ箱へ";
            this.button1.Click += new System.EventHandler(this.button1_Click);
            // 
            // Form1
            // 
            this.AutoScaleBaseSize = new System.Drawing.Size(5, 12);
            this.ClientSize = new System.Drawing.Size(292, 266);
            this.Controls.Add(this.button1);
            this.Name = "Form1";
            this.Text = "Form1";
            this.ResumeLayout(false);

        }
                #endregion

                /// <summary>
                /// アプリケーションのメイン エントリ ポイントです。
                /// </summary>
                [STAThread]
                static void Main() 
                {
                        Application.Run(new Form1());
                }


                private void button1_Click(object sender, System.EventArgs e)
                {
                
                        if (DialogResult.OK == this.openFileDialog1.ShowDialog())
                        {
                                //      System.IO.File.Delete(this.openFileDialog1.FileName);

                                Shell.SHFILEOPSTRUCT sh = new Shell.SHFILEOPSTRUCT();
                        
                                sh.hwnd = this.Handle;
                                sh.wFunc = Shell.FOFunc.FO_DELETE;
                                sh.pFrom = this.openFileDialog1.FileName + '\0' + '\0';
                                sh.pTo = null;
                                sh.fFlags = Shell.FOFlags.FOF_ALLOWUNDO;
                                sh.fAnyOperationsAborted = false;
                                sh.hNameMappings = IntPtr.Zero;
                                sh.lpszProgressTitle = "Hello";
                                int result = Shell.SHFileOperation(ref sh);
                        }
                }
        }
}

7.変更履歴

2003/6/25 新規作成