Unity3D在PC端快速高效的调取系统窗口,获取本地文件和保存文件到本地
目录
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++的数据结构自定义的那个数据结构,提前赋值的这些属性,这里不再单独介绍,上边代码每条都有注释,可以去看一眼。
/// <summary>
/// 打开窗口
/// </summary>
/// <returns></returns> 和该文件相关的数据
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上,有兴趣可以下一下。
/// <summary>
/// 打开本地文件
/// </summary>
public void OpenProject()
{
FileDialogMgr mgr = new FileDialogMgr(); //实例化窗口管理器
mgr.SetFilteringWay(EnumFilteringWay.Png); //设置过滤方式
OpenDialogData mgrData = mgr.OpenFileDlg(); //获取本地文件的信息
}
/// <summary>
/// 保存文件到本地
/// </summary>
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所创,推荐一下!我也在群里!