using System; using System.ComponentModel; using System.IO; using System.Runtime.InteropServices; using UnityEngine; public class FileDialogHelper { public class FileOpMsg { public bool success; public string msg; } public const string IMAGEFILTER = "图片文件(*.jpg;*.png)\0*.jpg;*.png"; public const string ALLFILTER = "所有文件(*.*)\0*.*"; public bool CopySelectFile(string sourcePath, string targetPath) { try { File.Copy(sourcePath, targetPath + $"/{Path.GetFileName(sourcePath)}", true); return true; } catch (Exception) { Debug.LogError($"sourcePath = {sourcePath}"); Debug.LogError($"targetPath = {Path.GetFileName(sourcePath)}"); Debug.LogError("拷贝失败"); return false; } } public string SelectFile(Action callback, string filter = ALLFILTER) { Debug.Log("open 00"); FileOpMsg fileOpMsg = new FileOpMsg(); try { OpenFileName openFileName = new OpenFileName(); openFileName.structSize = Marshal.SizeOf(openFileName); //openFileName.hwndOwner = WindowsTools.ProcessHandle; openFileName.filter = filter; openFileName.file = new string(new char[256]); openFileName.maxFile = openFileName.file.Length; openFileName.fileTitle = new string(new char[64]); openFileName.maxFileTitle = openFileName.fileTitle.Length; openFileName.title = "选择文件"; openFileName.flags = 0x00080000 | 0x00001000 | 0x00000800 | 0x00000008; if (GetSaveFileName(openFileName)) { string filepath = openFileName.file; //选择的文件路径; if (File.Exists(filepath)) { fileOpMsg.success = true; fileOpMsg.msg = filepath; callback?.Invoke(fileOpMsg); return fileOpMsg.msg; } } } catch (Exception e) { fileOpMsg.success = false; fileOpMsg.msg = e.ToString(); callback?.Invoke(fileOpMsg); Debug.LogError(fileOpMsg.msg); return fileOpMsg.msg; } fileOpMsg.success = true; fileOpMsg.msg = string.Empty; callback?.Invoke(fileOpMsg); return fileOpMsg.msg; } [DllImport("Comdlg32.dll", SetLastError = true, ThrowOnUnmappableChar = true, CharSet = CharSet.Auto)] private static extern bool GetSaveFileName([In, Out] OpenFileName ofn); [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 String file = null; 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; } #region 隐藏 /// 用于显示Windows平台的标准文件对话框 /// /// 返回用户所选文件的路径信息 // 引入外部库,SetLastError = true将上一个错误代码设置为非零值,CharSet = CharSet.Auto字符集由运行时环境自动选择 ///[In, Out]是修饰符,用于指定参数是既输入又输出的(类似于引用传参) [DllImport("Comdlg32.dll", SetLastError = true, CharSet = CharSet.Auto)] private static extern bool GetOpenFileName([In, Out] FileDialogWin ofn); [DllImport("kernel32.dll")] private static extern uint GetLastError(); /// /// 打开文件对话框 /// /// 对话框标题 /// 对话框默认路径 /// 文件默认格式 /// public string OpenWindowsFileDialog(string title, string defaultPath, string extension) { #if UNITY_EDITOR return UnityEditor.EditorUtility.OpenFilePanel(title, defaultPath, extension); #else // 在运行时用原生对话框 FileDialogWin ofn = new FileDialogWin(); ofn.structSize = Marshal.SizeOf(ofn); ofn.filter = extension; ofn.file = new string(new char[256]); ofn.maxFile = ofn.file.Length; ofn.title = title; ofn.initialDir = defaultPath; ofn.flags = 0x00080000 | 0x00001000 | 0x00000800 | 0x00000008; bool result = GetOpenFileName(ofn); Debug.LogError($"ofn = {ofn}"); Debug.LogError($"result: {result}"); if (!result) { uint errorCode = GetLastError(); Debug.LogError($"errorCode = {errorCode}"); if (errorCode == 0) { // 用户取消了对话框,这是正常行为 Debug.Log("用户取消了文件选择"); return null; } else { string errorMessage = new Win32Exception((int)errorCode).Message; Debug.LogError($"打开文件对话框失败,错误代码: {errorCode}, 错误信息: {errorMessage}"); // 常见的错误代码 switch (errorCode) { case 0x00000002: // ERROR_FILE_NOT_FOUND Debug.LogError("文件未找到,请检查默认路径是否存在"); break; case 0x00000005: // ERROR_ACCESS_DENIED Debug.LogError("访问被拒绝,可能没有足够的权限"); break; case 0x00000057: // ERROR_INVALID_PARAMETER Debug.LogError("参数无效,请检查结构体字段设置"); break; } } return null; } return ofn.file; #endif } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] public class FileDialogWin { 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 string file = null; public int maxFile = 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; } #endregion }