C#中使用windows文件选择器选择多个文件
C#中使用windows文件选择器选择多个文件
- 调用windows的文件选择窗口需要使用Comdlg32.h的方法,详见GetOpenFileNameA,这个方法需要传递一个包含文件选择所需所有信息的类,详见OPENFILENAMEA,以下是这个类的定义:
using System;
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public class OpenFileName
{
public int structSize = 0;
public IntPtr dlgOwner = IntPtr.Zero;
public IntPtr instance = IntPtr.Zero;
public String filter = null;
public String customFilter = null;
public int maxCustFilter = 0;
public int filterIndex = 0;
public IntPtr file;
public int maxFile = 0;
public String fileTitle = null;
public int maxFileTitle = 0;
public String initialDir = null;
public String title = null;
public int flags = 0;
public short fileOffset = 0;
public short fileExtension = 0;
public String defExt = null;
public IntPtr custData = IntPtr.Zero;
public IntPtr hook = IntPtr.Zero;
public String templateName = null;
public IntPtr reservedPtr = IntPtr.Zero;
public int reservedInt = 0;
public int flagsEx = 0;
}
需要注意的是,file字段并不是用String类型存储,这个字段用于存储用户选择的文件名,当只选择一个文件时,这个字段返回选择文件的全路径,当选择多个文件时,会返回文件夹路径以及选择的多个文件名组成的字符串,如果是使用的资源管理器风格,会以 NULL 分割,如果使用的是旧式对话框风格时,会以 空格 分割。
一般而言,会使用资源管理器风格。(旧式风格我尝试过,界面和操作较为反人类)
使用IntPtr存储file字段的原因就在于此,因为String类型以 NULL 结尾,选中多个文件时,String类型的file字段只能获取到文件夹路径。
- 使用 DllImport 属性调用系统函数
using System.Runtime.InteropServices;
public class LocalDialog
{
//链接指定系统函数 打开文件对话框
[DllImport("Comdlg32.dll", SetLastError = true, ThrowOnUnmappableChar = true, CharSet = CharSet.Auto)]
public static extern bool GetOpenFileName([In, Out] OpenFileName ofn);
}
- 使用方法
public List<string> FileSelector()
{
List<string> fileFullNames = new List<string>();
OpenFileName openFileName = new OpenFileName();
openFileName.structSize = Marshal.SizeOf(openFileName);
openFileName.filter = "模型文件(*.fbx,*.obj)\0*.fbx;*.obj\0";
openFileName.fileTitle = new string(new char[64]);
openFileName.maxFileTitle = openFileName.fileTitle.Length;
openFileName.initialDir = Application.streamingAssetsPath.Replace('/', '\\');//默认路径
openFileName.title = "选择fbx文件";
openFileName.flags = 0x00000004 | 0x00080000 | 0x00001000 | 0x00000800 | 0x00000008 | 0x00000200;
// Create buffer for file names
string fileNames = new String(new char[2048]);
openFileName.file = Marshal.StringToBSTR(fileNames);
openFileName.maxFile = fileNames.Length;
if (LocalDialog.GetOpenFileName(openFileName))
{
List<string> selectedFilesList = new List<string>();
long pointer = (long)openFileName.file;
string file = Marshal.PtrToStringAuto(openFileName.file);
while (file.Length > 0)
{
selectedFilesList.Add(file);
pointer += file.Length * 2 + 2;
openFileName.file = (IntPtr)pointer;
file = Marshal.PtrToStringAuto(openFileName.file);
}
if (selectedFilesList.Count == 1)
{
fileFullNames = selectedFilesList;
}
else
{
string[] selectedFiles = new string[selectedFilesList.Count - 1];
for (int i = 0; i < selectedFiles.Length; i++)
{
selectedFiles[i] = selectedFilesList[0] + "\\" + selectedFilesList[i + 1];
}
fileFullNames = new List<string>(selectedFiles);
}
}
if (fileFullNames.Count > 0)
{
return fileFullNames;
}
else
{
return null;
}
}
在flag的设置中关键的几个为:
- 0x00000200:允许多选
- 0x00080000:使用资源管理器风格
其余选项详见OPENFILENAMEA中关于flag字段的枚举介绍
参考资料:
Unity中调用Windows窗口选择文件
GetOpenFileNameA function (commdlg.h)