1. 程式人生 > 其它 >C#中使用windows檔案選擇器選擇多個檔案

C#中使用windows檔案選擇器選擇多個檔案

C#中使用windows檔案選擇器選擇多個檔案

  1. 呼叫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欄位只能獲取到資料夾路徑。

  1. 使用 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);
}
  1. 使用方法
    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)

OPENFILENAMEA structure (commdlg.h)

GetOpenFileName for multiple files