本文將和大家介紹如何在 dotnet C# 裡面使用 SHFileOperation 呼叫 Windows 自帶的檔案複製對話方塊進行檔案複製
本文核心程式碼複製自 C#中使用SHFileOperation呼叫Windows的複製檔案對話方塊 - 季風哥 - 部落格園 文章,特別感謝大佬提供的方法
實現的效果圖如下
詳細實現邏輯還請大家參閱: C#中使用SHFileOperation呼叫Windows的複製檔案對話方塊 - 季風哥 - 部落格園
其中我遇到的坑是多個檔案之間需要使用 \0
字元分割,我使用了 dotnet 新 API 對其進行更新,程式碼如下
pm.pFrom = string.Join(FILE_SPLITER, sourceFiles) + $"{FILE_SPLITER}{FILE_SPLITER}";
pm.pTo = string.Join(FILE_SPLITER, targetFiles) + $"{FILE_SPLITER}{FILE_SPLITER}";
上述程式碼的 FILE_SPLITER
和 pm
都是從 C#中使用SHFileOperation呼叫Windows的複製檔案對話方塊 部落格裡面抄的
所有程式碼如下
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace KachelearnemChurjawenikall;
internal class Program
{
[SkipLocalsInit]
static void Main(string[] args)
{
var sourceFile = @"F:\temp\1";
var targetFile = @"F:\temp\2";
if (!File.Exists(sourceFile))
{
var buffer = new byte[1024 * 1024];
for (int i = 0; i < 1000; i++)
{
Random.Shared.NextBytes(buffer);
File.AppendAllBytes(sourceFile, buffer);
}
}
string[] sourceFiles = [sourceFile];
string[] targetFiles = [targetFile];
SHFILEOPSTRUCT pm = new SHFILEOPSTRUCT();
pm.wFunc = wFunc.FO_COPY;
//設定對話方塊標題,在win7中無效
pm.lpszProgressTitle = "複製檔案";
pm.pFrom = string.Join(FILE_SPLITER, sourceFiles) + $"{FILE_SPLITER}{FILE_SPLITER}";
pm.pTo = string.Join(FILE_SPLITER, targetFiles) + $"{FILE_SPLITER}{FILE_SPLITER}";
pm.fFlags = FILEOP_FLAGS.FOF_NOCONFIRMATION | FILEOP_FLAGS.FOF_MULTIDESTFILES | FILEOP_FLAGS.FOF_ALLOWUNDO;
SHFileOperation(pm);
}
/// <summary>
/// 對映API方法
/// </summary>
/// <param name="lpFileOp"></param>
/// <returns></returns>
[DllImport("shell32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern int SHFileOperation(SHFILEOPSTRUCT lpFileOp);
/// <summary>
/// 多個檔案路徑的分隔符
/// </summary>
private const string FILE_SPLITER = "\0";
/// <summary>
/// Shell檔案運算元據型別
/// </summary>
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private class SHFILEOPSTRUCT
{
public IntPtr hwnd;
/// <summary>
/// 設定操作方式
/// </summary>
public wFunc wFunc;
/// <summary>
/// 原始檔路徑
/// </summary>
public string pFrom;
/// <summary>
/// 目標檔案路徑
/// </summary>
public string pTo;
/// <summary>
/// 允許恢復
/// </summary>
public FILEOP_FLAGS fFlags;
/// <summary>
/// 監測有無中止
/// </summary>
public bool fAnyOperationsAborted;
public IntPtr hNameMappings;
/// <summary>
/// 設定標題
/// </summary>
public string lpszProgressTitle;
}
/// <summary>
/// 檔案操作方式
/// </summary>
private enum wFunc
{
/// <summary>
/// 移動
/// </summary>
FO_MOVE = 0x0001,
/// <summary>
/// 複製
/// </summary>
FO_COPY = 0x0002,
/// <summary>
/// 刪除
/// </summary>
FO_DELETE = 0x0003,
/// <summary>
/// 重新命名
/// </summary>
FO_RENAME = 0x0004
}
/// <summary>
/// fFlags列舉值,
/// 參見:http://msdn.microsoft.com/zh-cn/library/bb759795(v=vs.85).aspx
/// </summary>
private enum FILEOP_FLAGS
{
///<summary>
///pTo 指定了多個目標檔案,而不是單個目錄
///The pTo member specifies multiple destination files (one for each source file) rather than one directory where all source files are to be deposited.
///</summary>
FOF_MULTIDESTFILES = 0x1,
///<summary>
///不再使用
///Not currently used.
///</summary>
FOF_CONFIRMMOUSE = 0x2,
///<summary>
///不顯示一個進度對話方塊
///Do not display a progress dialog box.
///</summary>
FOF_SILENT = 0x4,
///<summary>
///碰到有牴觸的名字時,自動分配字首
///Give the file being operated on a new name in a move, copy, or rename operation if a file with the target name already exists.
///</summary>
FOF_RENAMEONCOLLISION = 0x8,
///<summary>
///不對使用者顯示提示
///Respond with "Yes to All" for any dialog box that is displayed.
///</summary>
FOF_NOCONFIRMATION = 0x10,
///<summary>
///填充 hNameMappings 欄位,必須使用 SHFreeNameMappings 釋放
///If FOF_RENAMEONCOLLISION is specified and any files were renamed, assign a name mapping object containing their old and new names to the hNameMappings member.
///</summary>
FOF_WANTMAPPINGHANDLE = 0x20,
///<summary>
///允許撤銷
///Preserve Undo information, if possible. If pFrom does not contain fully qualified path and file names, this flag is ignored.
///</summary>
FOF_ALLOWUNDO = 0x40,
///<summary>
///使用 *.* 時, 只對檔案操作
///Perform the operation on files only if a wildcard file name (*.*) is specified.
///</summary>
FOF_FILESONLY = 0x80,
///<summary>
///簡單進度條,意味著不顯示檔名。
///Display a progress dialog box but do not show the file names.
///</summary>
FOF_SIMPLEPROGRESS = 0x100,
///<summary>
///建新目錄時不需要使用者確定
///Do not confirm the creation of a new directory if the operation requires one to be created.
///</summary>
FOF_NOCONFIRMMKDIR = 0x200,
///<summary>
///不顯示出錯使用者介面
///Do not display a user interface if an error occurs.
///</summary>
FOF_NOERRORUI = 0x400,
///<summary>
/// 不復制 NT 檔案的安全屬性
///Do not copy the security attributes of the file.
///</summary>
FOF_NOCOPYSECURITYATTRIBS = 0x800,
///<summary>
/// 不遞迴目錄
///Only operate in the local directory. Don't operate recursively into subdirectories.
///</summary>
FOF_NORECURSION = 0x1000,
///<summary>
///Do not move connected files as a group. Only move the specified files.
///</summary>
FOF_NO_CONNECTED_ELEMENTS = 0x2000,
///<summary>
///Send a warning if a file is being destroyed during a delete operation rather than recycled. This flag partially overrides FOF_NOCONFIRMATION.
///</summary>
FOF_WANTNUKEWARNING = 0x4000,
///<summary>
///Treat reparse points as objects, not containers.
///</summary>
FOF_NORECURSEREPARSE = 0x8000,
}
}
本文程式碼放在 github 和 gitee 上,可以使用如下命令列拉取程式碼。我整個程式碼倉庫比較龐大,使用以下命令列可以進行部分拉取,拉取速度比較快
先建立一個空資料夾,接著使用命令列 cd 命令進入此空資料夾,在命令列裡面輸入以下程式碼,即可獲取到本文的程式碼
git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 9685aaf325b6d7deee15fd6488cd7533f7052ea2
以上使用的是國內的 gitee 的源,如果 gitee 不能訪問,請替換為 github 的源。請在命令列繼續輸入以下程式碼,將 gitee 源換成 github 源進行拉取程式碼。如果依然拉取不到程式碼,可以發郵件向我要程式碼
git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin 9685aaf325b6d7deee15fd6488cd7533f7052ea2
獲取程式碼之後,進入 Workbench/KachelearnemChurjawenikall 資料夾,即可獲取到原始碼
更多技術部落格,請參閱 部落格導航