🥦实现思路
在 Windows 上实现存档的导入功能, 可以直接调用系统的文件管理器, 选择我们指定类型的文件, 之后代码中进行处理即可
🌵调用文件管理器
系统提供了 GetOpenFileName
函数, 可以调用 Windows 文件管理器, 以下是此函数的使用步骤
新建数据类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] internal class OpenFileName { public int structSize; public IntPtr dlgOwner; public IntPtr instance; public string filter; public string customFilter; public int maxCustFilter; public int filterIndex; public IntPtr file; public int maxFile; public string fileTitle; public int maxFileTitle; public string initialDir; public string title; public int flags; public short fileOffset; public short fileExtension; public string defExt; public IntPtr custData; public IntPtr hook; public string templateName; public IntPtr reservedPtr; public int reservedInt; public int flagsEx; }
|
引入 DLL
1 2 3 4 5
|
[DllImport("ComDLG32.dll", SetLastError = true, ThrowOnUnmappableChar = true, CharSet = CharSet.Auto)] internal static extern bool GetOpenFileName([In, Out] OpenFileName openFileName);
|
准备数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| var openFileName = new OpenFileName();
openFileName.structSize = Marshal.SizeOf(openFileName); openFileName.filter = filter; openFileName.fileTitle = new string(new char[64]); openFileName.maxFileTitle = openFileName.fileTitle.Length; openFileName.initialDir = defaultFolder; openFileName.title = windowTitle; openFileName.templateName = string.Empty; openFileName.flags = 0x00000008 | 0x00000200 | 0x00000800 | 0x00001000 | 0x00080000;
const int BUFFER_SIZE = 2048; var fileBuffer = Marshal.AllocHGlobal(BUFFER_SIZE * Marshal.SystemDefaultCharSize);
for (var index = 0; index < BUFFER_SIZE; index++) { Marshal.WriteByte(fileBuffer, index, 0); }
openFileName.file = fileBuffer; openFileName.maxFile = BUFFER_SIZE;
|
打开管理器
1 2 3 4 5 6
| if (GetOpenFileName(openFileName) == false) { selectData = null; return false; }
|
处理返回值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| selectedFilesList.Clear();
var pointer = fileBuffer;
while (true) { var file = Marshal.PtrToStringAuto(pointer);
if (string.IsNullOrEmpty(file)) { break; }
selectedFilesList.Add(file);
pointer += (file.Length + 1) * Marshal.SystemDefaultCharSize; }
selectData = selectedFilesList; return selectedFilesList.Count > 0;
|
释放内存
1 2
| Marshal.FreeHGlobal(fileBuffer);
|
🦄总结
- 强烈建议使用新风格的文件管理器, 虽然旧风格的文件管理器返回的值用
空格
来分割, 处理起来更简单, 但是界面巨丑, 操作巨反人类
- 使用新风格的文件管理器时要注意, 一定不要使用
string
类型存储返回值, 因为返回值使用 \0
来分割, 而 C# 中的字符串认为 \0
是字符串的结束, 因此后面的信息就丢失了
- 记得及时释放申请的缓冲区内存
👀完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
|
using System; using System.Collections.Generic; using System.Runtime.InteropServices;
namespace Kuroha.Utility { public class FileUtilWindows { [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] internal class OpenFileName { public int structSize; public IntPtr dlgOwner; public IntPtr instance; public string filter; public string customFilter; public int maxCustFilter; public int filterIndex; public IntPtr file; public int maxFile; public string fileTitle; public int maxFileTitle; public string initialDir; public string title; public int flags; public short fileOffset; public short fileExtension; public string defExt; public IntPtr custData; public IntPtr hook; public string templateName; public IntPtr reservedPtr; public int reservedInt; public int flagsEx; }
[DllImport("ComDLG32.dll", SetLastError = true, ThrowOnUnmappableChar = true, CharSet = CharSet.Auto)] internal static extern bool GetOpenFileName([In, Out] OpenFileName openFileName);
private static readonly List<string> selectedFilesList = new List<string>();
public static bool SelectFiles(string windowTitle, string defaultFolder, string filter, out List<string> selectData) { var openFileName = new OpenFileName();
openFileName.structSize = Marshal.SizeOf(openFileName); openFileName.filter = filter; openFileName.fileTitle = new string(new char[64]); openFileName.maxFileTitle = openFileName.fileTitle.Length; openFileName.initialDir = defaultFolder; openFileName.title = windowTitle; openFileName.templateName = string.Empty; openFileName.flags = 0x00000008 | 0x00000200 | 0x00000800 | 0x00001000 | 0x00080000;
const int BUFFER_SIZE = 2048; var fileBuffer = Marshal.AllocHGlobal(BUFFER_SIZE * Marshal.SystemDefaultCharSize);
for (var index = 0; index < BUFFER_SIZE; index++) { Marshal.WriteByte(fileBuffer, index, 0); }
openFileName.file = fileBuffer; openFileName.maxFile = BUFFER_SIZE;
try { if (GetOpenFileName(openFileName) == false) { selectData = null; return false; }
#region 处理返回值
selectedFilesList.Clear();
var pointer = fileBuffer;
while (true) { var file = Marshal.PtrToStringAuto(pointer);
if (string.IsNullOrEmpty(file)) { break; }
selectedFilesList.Add(file);
pointer += (file.Length + 1) * Marshal.SystemDefaultCharSize; }
selectData = selectedFilesList; return selectedFilesList.Count > 0;
#endregion } finally { Marshal.FreeHGlobal(fileBuffer); } } } }
|