質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.31%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Q&A

解決済

2回答

359閲覧

C# SHFileOperationでフォルダコピーをしても進捗ダイアログが1回目だけしか表示されない

kurimu-tyo

総合スコア1

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

0グッド

0クリップ

投稿2025/05/15 11:27

編集2025/05/21 11:09

実現したいこと

SHFileOperationでフォルダコピーした際に、毎回進捗ダイアログが表示されるようにしたい。

発生している問題・エラーメッセージ

自作のエクスプローラを使用して、ファイル操作をおこなっていたのですが、
最近、 SHFileOperationでフォルダコピーした際に、以前は表示されていた進捗ダイアログが表示されなくなっていることに気づきました。(コピー処理は正常に行われている)

但し、自作のエクスプローラの起動直後でのフォルダコピーでは、1回目だけ進捗ダイアログが表示されて、正常にコピー処理されています。

どうすれば、進捗ダイアログが毎回表示されるようになるのでしょうか?

該当のソースコード

win11 C# visual Studio Community 2022

コード
public enum FileFuncFlags : uint
{
FO_MOVE = 0x1,
FO_COPY = 0x2,
FO_DELETE = 0x3,
FO_RENAME = 0x4
}
[Flags]
public enum FILEOP_FLAGS : ushort
{
FOF_MULTIDESTFILES = 0x1,
FOF_CONFIRMMOUSE = 0x2,
FOF_SILENT = 0x4,
FOF_RENAMEONCOLLISION = 0x8,
FOF_NOCONFIRMATION = 0x10,
FOF_WANTMAPPINGHANDLE = 0x20,
FOF_ALLOWUNDO = 0x40,
FOF_FILESONLY = 0x80,
FOF_SIMPLEPROGRESS = 0x100,
FOF_NOCONFIRMMKDIR = 0x200,
FOF_NOERRORUI = 0x400,
FOF_NOCOPYSECURITYATTRIBS = 0x800,
FOF_NORECURSION = 0x1000,
FOF_NO_CONNECTED_ELEMENTS = 0x2000,
FOF_WANTNUKEWARNING = 0x4000,
FOF_NORECURSEREPARSE = 0x8000
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct SHFILEOPSTRUCT
{
public IntPtr hwnd;
public FileFuncFlags wFunc;
[MarshalAs(UnmanagedType.LPWStr)]
public string pFrom;
[MarshalAs(UnmanagedType.LPWStr)]
public string pTo;
public FILEOP_FLAGS fFlags;
[MarshalAs(UnmanagedType.Bool)]
public bool fAnyOperationsAborted;
public IntPtr hNameMappings;
[MarshalAs(UnmanagedType.LPWStr)]
public string lpszProgressTitle;
}
[DllImport("shell32.dll", CharSet = CharSet.Unicode)]
static extern int SHFileOperation([In] ref SHFILEOPSTRUCT lpFileOp);

   //コピー実行
private void tsファイルコピー_Click(object sender, EventArgs e)
{
SHFILEOPSTRUCT shfos;
shfos.hwnd = this.Handle;
shfos.wFunc = FileFuncFlags.FO_COPY;
shfos.pFrom = @"C:\1\test"+ '\0' + '\0';
shfos.pTo = @"C:\2" + '\0' + '\0';
shfos.fFlags = FILEOP_FLAGS.FOF_ALLOWUNDO;
shfos.fAnyOperationsAborted = true;
shfos.hNameMappings = IntPtr.Zero;
shfos.lpszProgressTitle = null;
SHFileOperation(ref shfos);
}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

KOZ6.0

2025/05/15 15:51

コードに問題はなさそうですが、進捗表示する間もなくコピーが完了しているってことはないですか?
kurimu-tyo

2025/05/16 00:48

KOZ6.0さん 御回答ありがとうございます。 大きなフォルダをコピーしているので、エクスプローラなら10秒ほどかかるサイズなので 進捗表示する間もなくコピーが完了するという事ではないと思います。
KOZ6.0

2025/05/16 16:02

コピーが正常に行われているなら、気にする必要はないと思いますが、IFileOperation を使うと解消できるかもしれません。 「IFileOperationで進行ダイアログ付きでディレクトリ操作を行う」 https://tan.hatenadiary.jp/entry/20160111/1452441478 COM の解放が煩わしいので、Windows API Code Pack を nuget して ShellFileOperation クラスを使うという手もあります。
kurimu-tyo

2025/05/19 00:51

IFileOperation を試そうとしましたが、良くわからなっかったので確認できませんでした。 仕方がないので、今回はフォルダコピーが1回目だけ進捗ダイアログが表示されろので、 フォルダコピーの部分を実行ファイル(EXE)で作成して、 自作のエクスプローラから外部アプリとして毎回呼び出すことで対応することにしました。 KOZ6.0さん、今回はお付き合いくださり、どうもありがとうございました。
KOZ6.0

2025/05/19 12:39

解決おめでとうございます! 解決方法をまとめて、「自己解決」として回答してください! 同じように困っている人の貴重な情報になると思います。
guest

回答2

0

コード
win11 C# visual Studio Community 2022
コード
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace ConsoleApplication1
{
//呼び出し方法
//exePath mode 元フォルダ 先フォルダ
// procCMDR.exe "COPY" "C:\1\testFolder" "C:\2\testFolder"
// procCMDR.exe "MOVE" "C:\1\testFolder" "C:\2\testFolder"
// procCMDR.exe "RENAME" "C:\1\testFolder" "C:\1\testFolder_Rename"
//ゴミ箱へ移動するTRUE 移動しない FALSE 但しエクスプローラーの設定により、ゴミ箱へ移動しない場合もある
// procCMDR.exe "DELETE" "C:\1\testFolder" "TRUR or FALSE"
//コード例
//string exeName = "c:\test\procCMDR.exe";
//string cmdStr = "COPY C:\1\testFolder C:\2\testFolder";
//System.Diagnostics.Process.Start(exeName, cmdStr);

class Program { [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] // CharSet.Unicode が重要 public struct SHFILEOPSTRUCT { public IntPtr hwnd; public FO wFunc; [MarshalAs(UnmanagedType.LPWStr)] // LPTSTR の代わりに LPWStr を使用 public string pFrom; [MarshalAs(UnmanagedType.LPWStr)] // LPTSTR の代わりに LPWStr を使用 public string pTo; public FOF fFlags; [MarshalAs(UnmanagedType.Bool)] public bool fAnyOperationsAborted; public IntPtr hNameMappings; [MarshalAs(UnmanagedType.LPWStr)] // LPTSTR の代わりに LPWStr を使用 public string lpszProgressTitle; } public enum FO : uint { MOVE = 0x0001, COPY = 0x0002, DELETE = 0x0003, RENAME = 0x0004, } [Flags] public enum FOF : ushort { SILENT = 0x0004, // Progress UIを表示しない NOCONFIRMATION = 0x0010, // 確認ダイアログを表示しない (上書き確認など) MULTIPLEFILES = 0x0040, // pFrom, pTo が複数ファイルを指す場合 ALLOWUNDO = 0x0040, // 削除操作をUNDO可能にする (ごみ箱へ移動) FILESONLY = 0x0080, // ワイルドカード指定時、ファイルのみを対象とする SIMPLEPROGRESS = 0x0100, // 簡単なプログレスUIを表示する NOCONFIRMMKDIR = 0x0200, // 新規ディレクトリ作成時の確認ダイアログを表示しない NOERRORUI = 0x0400, // エラー発生時のUIを表示しない NORECURSION = 0x0800, // サブディレクトリを再帰的に処理しない NO_CONNECTED_ELEMENTS = 0x2000, // ネットワーク共有要素を処理しない //WANTMAPPINGHANDLE = 0x20000, // hNameMappings を使用する //ALLOWCACREAD = 0x200000, // キャッシュされたアクセス制御リスト (ACL) の作成を許可 NO_UI = NOERRORUI | SILENT | NOCONFIRMATION | NOCONFIRMMKDIR // UIを一切表示しない } [DllImport("shell32.dll", CharSet = CharSet.Unicode)] public static extern int SHFileOperation([In] ref SHFILEOPSTRUCT lpFileOp); public static void CopyFile(string source, string destination, bool showUI = true, bool allowUndo = false) { SHFILEOPSTRUCT fileOp = new SHFILEOPSTRUCT(); fileOp.wFunc = FO.COPY; fileOp.pFrom = source + '\0' + '\0'; fileOp.pTo = destination + '\0' + '\0'; // コピー先がディレクトリの場合、その中にコピーされる fileOp.fFlags = FOF.MULTIPLEFILES | (showUI ? 0 : FOF.NO_UI); if (!showUI) fileOp.fFlags |= FOF.NOCONFIRMATION; // UI非表示なら確認もしないことが多い if (allowUndo) fileOp.fFlags |= FOF.ALLOWUNDO; // コピー操作では通常あまり使われないが、一応 int result = SHFileOperation(ref fileOp); } public static void MoveFile(string source, string destination, bool showUI = true, bool allowUndo = false) { SHFILEOPSTRUCT fileOp = new SHFILEOPSTRUCT(); fileOp.wFunc = FO.MOVE; fileOp.pFrom = source + '\0' + '\0'; fileOp.pTo = destination + '\0' + '\0'; fileOp.fFlags = FOF.MULTIPLEFILES | (showUI ? 0 : FOF.NO_UI); if (!showUI) fileOp.fFlags |= FOF.NOCONFIRMATION; if (allowUndo) fileOp.fFlags |= FOF.ALLOWUNDO; int result = SHFileOperation(ref fileOp); } public static void DeleteFile(string path, bool moveToRecycleBin = true, bool showUI = true) { SHFILEOPSTRUCT fileOp = new SHFILEOPSTRUCT(); fileOp.wFunc = FO.DELETE; fileOp.pFrom = path + '\0' + '\0'; fileOp.pTo = null; // 削除操作では pTo は使用しない fileOp.fFlags = FOF.MULTIPLEFILES | (showUI ? 0 : FOF.NO_UI); if (!showUI) fileOp.fFlags |= FOF.NOCONFIRMATION; if (moveToRecycleBin) { fileOp.fFlags |= FOF.ALLOWUNDO; } // ALLOWUNDO を指定しない場合、完全に削除される(ごみ箱に入らない) int result = SHFileOperation(ref fileOp); } public static void RenameFile(string oldPath, string newPath, bool showUI = true) { SHFILEOPSTRUCT fileOp = new SHFILEOPSTRUCT(); fileOp.wFunc = FO.RENAME; fileOp.pFrom = oldPath + '\0' + '\0'; fileOp.pTo = newPath + '\0' + '\0'; // リネームの場合、pTo は新しいフルパスまたは新しいファイル名を指定 fileOp.fFlags = FOF.MULTIPLEFILES | (showUI ? 0 : FOF.NO_UI); if (!showUI) fileOp.fFlags |= FOF.NOCONFIRMATION; int result = SHFileOperation(ref fileOp); } static void Main(string[] args) { string cmdLine = System.Environment.CommandLine; // 引数確認用メッセージボックスを表示 DialogResult result = MessageBox.Show(cmdLine,//"表示メッセージ", "引数確認", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1); //return; //コマンドライン引数を配列で取得する string[] cmds = System.Environment.GetCommandLineArgs(); string source = cmds[2]; //コピー元 string dest = cmds[3]; //コピー先 bool showUI = true;//UIを表示するかどうか bool allowUndo = false;//ごみ箱に移動するかどうか if (cmds[1] == "COPY") { CopyFile(source, dest, showUI, allowUndo); } else if (cmds[1] == "MOVE") { MoveFile(source, dest, showUI, allowUndo); } else if (cmds[1] == "RENAME") { RenameFile(source, dest, showUI); } else if (cmds[1] == "DELETE") { source = cmds[2];//削除オブジェクト string str = cmds[3];//ごみ箱へ移動 if (str == "TRUE") { allowUndo = true; } else if (str == "FALSE") { allowUndo = false; } else { //メッセージボックスを表示する MessageBox.Show("ごみ箱へ移動 モード不明\n"+ str, "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } DeleteFile(source, allowUndo, showUI); } else { //メッセージボックスを表示する MessageBox.Show("操作モード不明", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } } }

}

投稿2025/05/22 07:00

kurimu-tyo

総合スコア1

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

最近、 SHFileOperationでフォルダコピーした際に、以前は表示されていた進捗ダイアログが表示されなくなっていることに気づきました。(コピー処理は正常に行われている)

但し、自作のエクスプローラの起動直後でのフォルダコピーでは、1回目だけ進捗ダイアログが表示されて、正常にコピー処理されています。

手元では(提示コードでも)再現できませんでした。

例えばVBのFileSystemやラッパーライブラリ、C#製のファイルマネージャ等でも同様なのでしょうか?(PCの問題??)

IFileOperation を試そうとしましたが、良くわからなっかったので確認できませんでした。

わたしも何もわかっていませんが、あまり情報がないので試してみました。

cs

1using System.Diagnostics; 2using System.Runtime.InteropServices; 3using Windows.Win32; 4using Windows.Win32.Foundation; 5using Windows.Win32.UI.Shell; 6 7namespace Q3hmackgmohifz7; 8 9public partial class Form1 : Form 10{ 11 public Form1() 12 { 13 InitializeComponent(); 14 15 Directory.CreateDirectory(@"1\test"); 16 Directory.CreateDirectory(@"2"); 17 18 Process.Start(new ProcessStartInfo 19 { 20 UseShellExecute = true, 21 FileName = "fsutil", 22 Arguments = @"file createnew 1\test\test.dat 10737418240", // 10GB 23 }); 24 } 25 26 private void Button1_Click(object sender, EventArgs e) 27 { 28 var fileOperation = (IFileOperation)new FileOperation(); 29 fileOperation.SetOwnerWindow(new HWND(Handle)); 30 31 PInvoke.SHCreateItemFromParsingName(Path.GetFullPath(@"1\test"), null, typeof(IShellItem).GUID, out var from); 32 PInvoke.SHCreateItemFromParsingName(Path.GetFullPath(@"2"), null, typeof(IShellItem).GUID, out var to); 33 34 fileOperation.CopyItem((IShellItem)from, (IShellItem)to, null, null); 35 try 36 { 37 fileOperation.PerformOperations(); 38 } 39 catch (COMException ex) 40 { 41 Debug.WriteLine(ex); 42 } 43 44 Marshal.ReleaseComObject(from); 45 Marshal.ReleaseComObject(to); 46 47 Marshal.FinalReleaseComObject(fileOperation); 48 49 50 //var from = Path.GetFullPath(@"1\test") + '\0'; 51 //var to = Path.GetFullPath(@"2") + '\0'; 52 //unsafe 53 //{ 54 // fixed (char* pFrom = from) 55 // fixed (char* pTo = to) 56 // { 57 // var shfos = new SHFILEOPSTRUCTW 58 // { 59 // hwnd = new HWND(Handle), 60 // wFunc = (uint)FileFuncFlags.FO_COPY, 61 // pFrom = new PCZZWSTR(pFrom), 62 // pTo = new PCZZWSTR(pTo), 63 // fFlags = (ushort)FILEOP_FLAGS.FOF_ALLOWUNDO, 64 // }; 65 66 // PInvoke.SHFileOperation(ref shfos); 67 // } 68 //} 69 } 70 71 //enum FileFuncFlags : uint { FO_COPY = 0x2, } 72 //[Flags] enum FILEOP_FLAGS : ushort { FOF_ALLOWUNDO = 0x40, } 73}

NativeMethods.txt

1IFileOperation 2FileOperation 3SHCreateItemFromParsingName 4 5// SHFileOperation

NuGet Gallery | Microsoft.Windows.CsWin32

アプリ動画


雑多な参考情報
IFileOperationで進行ダイアログ付きでディレクトリ操作を行う - プログラム系統備忘録ブログ

.NET Matters: IFileOperation in Windows Vista | Microsoft Learn

MSDN マガジンのバックナンバー | Microsoft Docs

CsWin32 で Win32 API や COM を使ったアプリケーション開発を効率化する - しばやん雑記

投稿2025/05/19 16:21

編集2025/05/19 16:26
TN8001

総合スコア10040

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

TN8001

2025/05/19 16:23

何もわかっていないので間違いがあれば指摘してください^^;
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.31%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問