Unity3D在PC端快速高效的调取系统窗口,获取本地文件和保存文件到本地

              Unity3D在PC端快速高效的调取系统窗口,获取本地文件和保存文件到本地


目录

1、博客介绍

2、内容

(1)效果展示

(2)核心方法

3、打开窗口获取本地文件

4、打开窗口保存

5、封装模块化

6、推送

7、结语


1、博客介绍

       之前有功能需要在PC端调取Windows的系统窗口来获取或者保存文件到本地,在网上扒了些资料,都是大同小异,基本都是利用win32的方式来调,这里抽取了一个比较好的方式来介绍,主要是网上扒出来的都没详细介绍,看不太懂,这里做些简短的介绍,并且最后把功能拢一拢,打成一个dll,方便以后用,功能会慢慢补充,更新到github上面,有兴趣可以收藏关注后续。


2、内容

(1)效果展示

(2)核心方法

我们先把核心代码放上来,再分析好吧,看的同学,先大致把下面代码浏览一下,代码后面再做分析。

using System.Runtime.InteropServices;
using System;

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public class FileDialogData
{
    public int structSize = 0;                  //结构的内存大小
    public IntPtr dlgOwner = IntPtr.Zero;       //设置对话框的句柄
    public IntPtr instance = IntPtr.Zero;       //根据flags标志的设置,确定instance是谁的句柄,不设置则忽略
    public String filter = null;                //调取文件的过滤方式
    public String customFilter = null;          //一个静态缓冲区 用来保存用户选择的筛选器模式
    public int maxCustFilter = 0;               //缓冲区的大小
    public int filterIndex = 0;                 //指向的缓冲区包含定义过滤器的字符串对
    public String file = null;                  //存储调取文件路径
    public int maxFile = 0;                     //存储调取文件路径的最大长度 至少256
    public String fileTitle = null;             //调取的文件名带拓展名
    public int maxFileTitle = 0;                //调取文件名最大长度
    public String initialDir = null;            //最初目录
    public String title = null;                 //打开窗口的名字
    public int flags = 0;                       //初始化对话框的一组位标志  参数类型和作用查阅官方API
    public short fileOffset = 0;                //文件名前的长度
    public short fileExtension = 0;             //拓展名前的长度
    public String defExt = null;                //默认的拓展名
    public IntPtr custData = IntPtr.Zero;       //传递给lpfnHook成员标识的钩子子程的应用程序定义的数据
    public IntPtr hook = IntPtr.Zero;           //指向钩子的指针。除非Flags成员包含OFN_ENABLEHOOK标志,否则该成员将被忽略。
    public String templateName = null;          //模块中由hInstance成员标识的对话框模板资源的名称
    public IntPtr reservedPtr = IntPtr.Zero;
    public int reservedInt = 0;
    public int flagsEx = 0;                     //可用于初始化对话框的一组位标志
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public class OpenDialogData : FileDialogData
{

}

/*这是C#引用非托管的C/C++的DLL的一种定义定义结构体的方式,主要是为了内存中排序,LayoutKind有两个属性Sequential和Explicit

Sequential表示顺序存储,结构体内数据在内存中都是顺序存放的Explicit表示精确布局,需要用FieldOffset()设置每个成员的位置这都是

为了使用非托管的指针准备的,知道什么意思就行,C#的CLR提供了更为灵活的自动管理方式,所以对C#来说可有可无。

CharSet=CharSet.Ansi表示编码方式
*/
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public class SaveDialogData : FileDialogData
{

}

public class OpenFileDialog
{
    [DllImport("Comdlg32.dll", SetLastError = true, ThrowOnUnmappableChar = true, CharSet = CharSet.Auto)]
    public static extern bool GetOpenFileName([In, Out] OpenDialogData ofd);
}
public class SaveFileDialog
{
    [DllImport("Comdlg32.dll", SetLastError = true, ThrowOnUnmappableChar = true, CharSet = CharSet.Auto)]
    public static extern bool GetSaveFileName([In, Out] SaveDialogData ofd);
}

 

       我们的核心思想是,通过C#去引用Comdlg32.dll这个Windows上C++写的的dll,这个dll就是windows上关于系统窗口的一个类库,引用方法我们用了[ DllImport ]的方法,该方法博主前一篇文章有介绍,不懂得可以跳过去,本文章结尾也有附赠的传送门,这里就不多做介绍了,代码中还有一个定义方法[ StructLayout ],该特性在博主的上一篇博文中也有介绍,不懂得先跳过去看一下,该特性主要就是保证我们定义的数据结构顺序不会被打乱。

核心1:FileDialogData ,这个是我们定义的一个数据结构,当然不是随便定义的,这个是Comdlg32.dll内和窗口相关的数据结构,我们定义的结构必须和库内的数据结构保持一致,里面包含了我们需要窗口的相关信息,这里面的数据,一部分是我们打开窗口前要提前设置的,一部分是我们获取或者保存本地文件后,才会被赋值的,注释我都写在上述代码里了,一条条看,还有不懂得,从下边官方解释里看。

核心2:OpenDialogData,SaveDialogData 这里我们通过继承的方式,分了两个不同的类出来,方便之后不同窗口的赋值。

核心3:OpenFileDialog,SaveFileDialog 这里,我们通过外部引用的方式,用C#调用的Comdlg32.dll内的C++方法,然后将窗口相关的信息,赋值到我们定义的数据结构上。

 

官方C++API:https://docs.microsoft.com/zh-cn/windows/win32/api/commdlg/ns-commdlg-openfilenamea


3、打开窗口获取本地文件

        提前赋值的数据 
     
        CustomOpenData = new OpenDialogData();
        CustomOpenData.structSize = Marshal.SizeOf(CustomOpenData);
        CustomOpenData.filter =  "All Files\0*.*\0\0";
        CustomOpenData.file = new string(new char[256]);
        CustomOpenData.maxFile = CustomOpenData.file.Length;
        CustomOpenData.fileTitle = new string(new char[1000]);
        CustomOpenData.maxFileTitle = CustomOpenData.fileTitle.Length;
        CustomOpenData.initialDir = Application.dataPath.Replace('/', '\\') + "\\aaa\\";
        CustomOpenData.title = "打开项目";
        CustomOpenData.flags = 0x00080000 | 0x00001000 | 0x00000800 | 0x00000200 | 0x00000008;

注意:这里面我们首先实例化了一个OpenDialogData,这个就是我们之前根据库内C++的数据结构自定义的那个数据结构,提前赋值的这些属性,这里不再单独介绍,上边代码每条都有注释,可以去看一眼。

    /// 
    /// 打开窗口
    /// 
    ///  和该文件相关的数据
    public OpenDialogData OpenFileDlg()
    {
        if (OpenFileDialog.GetOpenFileName(CustomOpenData))
        {
            return CustomOpenData;
        }
        return null;
    }

注意:这个是重中之重,就是该方法打开窗口,GetOpenFileName是C++的方法,我们将数据结构作为参数输入,如果我们在打开的窗口内选择了文件并打开,则会执行return CustomOpenData,将我们赋值好的数据结构传回来,如果取消窗口了就返回 null,我们可以在CustomOpenData内查看,选中文件的文件名,路径,拓展名等等的信息。

 


4、打开窗口保存

       保存和打开基本没啥区别,不同的就是数据结构初始赋值有些不一样,多了一个保存文件的拓展名defExt,然后调取的C++方式是GetSaveFileName

        提前赋值的数据
       
        CustomSaveData = new SaveDialogData();
        CustomSaveData.structSize = Marshal.SizeOf(CustomSaveData);
        CustomSaveData.filter = "All files (*.*)|*.*";
        CustomSaveData.file = new string(new char[256]);
        CustomSaveData.maxFile = CustomSaveData.file.Length;
        CustomSaveData.fileTitle = new string(new char[64]);
        CustomSaveData.maxFileTitle = CustomSaveData.fileTitle.Length;
        CustomSaveData.initialDir = Application.dataPath.Replace('/', '\\') ;  // default path  
        CustomSaveData.title = "保存项目";
        CustomSaveData.defExt = "txt";
        CustomSaveData.flags = 0x00080000 | 0x00001000 | 0x00000800 | 0x00000200 | 0x00000008;
    
    打开保存窗口的方法

    public SaveDialogData SaveFileDlg()
    {
        if (SaveFileDialog.GetSaveFileName(CustomSaveData))
        {
            return CustomSaveData;
        }
        return null;
    }

5、封装模块化

        根据上述的核心思想,博主准备将这个功能拓展拓展,打成Dll,用的时候更方便,目前比较简单,调用方式如下,后续博主还会慢慢的更新功能,比如直接打开一个文本或者Json直接可以读取内部数据等等的,dll和源码都在github上,有兴趣可以下一下。

    /// 
    /// 打开本地文件
    /// 
    public void OpenProject()
    {
        FileDialogMgr mgr = new FileDialogMgr();    //实例化窗口管理器
        mgr.SetFilteringWay(EnumFilteringWay.Png);  //设置过滤方式
        OpenDialogData mgrData = mgr.OpenFileDlg(); //获取本地文件的信息
    }
    /// 
    /// 保存文件到本地
    /// 
    public void SaveProject()
    {
        FileDialogMgr mgr = new FileDialogMgr();    //实例化窗口管理器
        mgr.SetFileExtension("txt");                //设置保存文件的后缀
        SaveDialogData mgrData = mgr.SaveFileDlg(); //获取保存文件的信息
    }

6、推送

github:https://github.com/KingSun5/FileDialogMgr

dllImport介绍:https://blog.csdn.net/Mr_Sun88/article/details/100626798

StructLayout介绍:https://blog.csdn.net/Mr_Sun88/article/details/101323222


7、结语

       若是觉得博主的文章写的不错,不妨关注一下博主,github点Star,star,star,点赞一下博文,另博主能力有限,若文中有出现什么错误的地方,欢迎各位评论指摘。

       QQ交流群:806091680(Chinar)

       该群为CSDN博主Chinar所创,推荐一下!我也在群里!

 

你可能感兴趣的:(Unity,系统窗口,win32)