【unity】unity打开Windows系统文件对话框

目录

前言

UnityEditor

Windows

引入外部动态库

声明GetOpenFileName函数

封装OPENFILENAME对象

调用GetOpenFileName函数

整合

最终效果


前言

        最近项目中遇到了需要调用系统对话框选择文件的问题,上网搜索时发现很多教程不够详细,费了不少功夫才解决。在项目开发时,程序运行在UnityEditor环境中,而打包后的程序须在Windows环境下运行,这两种环境调用系统文件对话框的方法不同,以下是我的思路

UnityEditor

        当项目在运行在UnityEditor时,可调用EditorUtility.OpenFilePanel()方法打开对话框。这个函数通常用于编辑器扩展(Editor Extensions)或自定义编辑器窗口中,以便让开发者在编辑器中选择文件。

        以下是EditorUtility.OpenFilePanel()的基本用法和一些参数说明:

// 打开文件面板时显示的标题。
string title = "Open File"; 

// 打开文件面板时默认的目录。这应该是相对于项目文件夹的路径。
string directory = "Assets"; 

// 打开文件面板时默认选择的文件扩展名,可以是空字符串。
string extension = "txt"; 

 // 函数返回一个字符串,表示用户选择的文件路径。如果用户取消选择或关闭文件面板,返回的路径将为空字符串。
string path = EditorUtility.OpenFilePanel(title, directory, extension);

         注意,该方法只能在UnityEditor环境下使用,打包后的程序无法使用该方法。

Windows

        既然打包后无法使用EditorUtility.OpenFilePanel(),我们只能另寻他法。

引入外部动态库

        因此我们需要调用本机Windows系统所提供的动态链接库Comdlg32.dll,这个库是Windows操作系统中包含通用对话框函数(如文件对话框)的库。代码如下:

[DllImport("Comdlg32.dll", SetLastError = true, CharSet = CharSet.Auto)]

               其中SetLastError = true表示将上一个错误代码设置为非零值。这对于调试和错误处理非常有用。如果调用失败,可以通过检查 Marshal.GetLastWin32Error() 获取错误代码。 
        而CharSet = CharSet.Auto表示字符集由运行时环境自动选择。

声明GetOpenFileName函数

        然后我们需要调用GetOpenFileName函数。这个函数是 Windows 操作系统提供的一个 API 函数,用于显示标准的文件打开对话框。该对话框允许用户选择一个或多个文件,并返回用户所选文件的路径信息。代码如下:

private static extern bool GetOpenFileName([In, Out] FileDialogWin.OPENFILENAME ofn);

        从关键字extern可以看出,该函数是一个本机方法,其实现并不在C#中,而是在本机代码中。

        [In, Out]是修饰符,用于指定参数是既输入又输出的(类似于引用传参)。

封装OPENFILENAME对象

        该函数接受一个FileDialogWin.OPENFILENAME类型的参数ofn,因此我们需要封装一个该类型的对象:

public static class FileDialogWin
    {
        [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 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;
        }
    }

        该结构体需要满足WindowsAPI所期望的格式,所以可以将它当作固定用法,这里就不作解释。 

调用GetOpenFileName函数

        上述准备工作做完后就可以调用函数了,代码如下:

private string OpenWindowsFileDialog(string title, string defaultPath, string extension)
    {
        //Debug.Log("运行");
        FileDialogWin.OPENFILENAME ofn = new FileDialogWin.OPENFILENAME();
        ofn.structSize = System.Runtime.InteropServices.Marshal.SizeOf(ofn);
        ofn.filter =extension + "Files\0*.extension\0All Files\0*.*\0\0"; 
        ofn.file = new string(new char[256]);
        ofn.maxFile = ofn.file.Length;
        ofn.title = title;
        ofn.initialDir = defaultPath;

        //Debug.Log(GetOpenFileName(ofn));
        
        if (GetOpenFileName(ofn))
        {
            return ofn.file;
        }

        return null;
    }

        首先实例化一个 FileDialogWin.OPENFILENAME对象,并给其属性初始化,其中ofn.filter是通过正则表达式匹配文件扩展名。

        然后调用GetOpenFileName函数,该函数会返回一个bool值,通常用于表示用户是否成功选择了文件并点击了文件对话框的 "打开" 按钮。因此当返回值为true时,返回获取到的文件地址即可。

        注意,经过实际测试,想要正常打开文件对话框似乎需要确保以管理员权限运行

        

整合

        最后将上述两个方法整合一下,得到最终代码:

public class FileDialog : MonoBehaviour
{

    // 留出一个统一的调用接口
    public string OpenFilePanel()
    {
        //定义返回的文件路径
        string selectedFilePath = string.Empty;
#if UNITY_EDITOR
        // 编辑器环境
        selectedFilePath = EditorUtility.OpenFilePanel("Select a file", "", "");
#elif UNITY_STANDALONE_WIN
        // Windows环境
        selectedFilePath = OpenWindowsFileDialog("Select a file", "", "");
#endif

        // 处理选中的文件路径
        if (!string.IsNullOrEmpty(selectedFilePath))
        {
            Debug.Log("Selected file: " + selectedFilePath);
        }
        else
        {
            Debug.Log("File selection canceled.");
        }

        return selectedFilePath;
    }


#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
    // Windows平台的文件对话框
    [DllImport("Comdlg32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    private static extern bool GetOpenFileName([In, Out] FileDialogWin.OPENFILENAME ofn);

    private string OpenWindowsFileDialog(string title, string defaultPath, string extension)
    {
        //Debug.Log("运行");
        FileDialogWin.OPENFILENAME ofn = new FileDialogWin.OPENFILENAME();
        ofn.structSize = System.Runtime.InteropServices.Marshal.SizeOf(ofn);
        ofn.filter ="G-code Files\0*.gcode\0All Files\0*.*\0\0"; 
        ofn.file = new string(new char[256]);
        ofn.maxFile = ofn.file.Length;
        ofn.title = title;
        ofn.initialDir = defaultPath;

        //Debug.Log(GetOpenFileName(ofn));
        
        if (GetOpenFileName(ofn))
        {
            return ofn.file;
        }

        return null;
    }

    public static class FileDialogWin
    {
        [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 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;
        }
    }
#endif
}

         只需在其他脚本调用OpenFilePanel()函数,将根据运行环境自动选择方案。

最终效果

UnityEditor

Windows

猜你喜欢

转载自blog.csdn.net/m0_67636792/article/details/134996606
今日推荐