-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathNativeFileDialog.cs
More file actions
166 lines (153 loc) · 7.21 KB
/
Copy pathNativeFileDialog.cs
File metadata and controls
166 lines (153 loc) · 7.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
using System;
using System.Runtime.InteropServices;
using System.Text;
using UnityEngine;
namespace VNyanWeightStudio
{
/// <summary>
/// Thin wrapper around the Win32 common "Save As" dialog (comdlg32!GetSaveFileNameW).
/// VNyan ships as a Windows standalone, so this is available at runtime. On any
/// non-Windows / unavailable platform it simply returns null and the caller falls
/// back to a default path.
/// </summary>
internal static class NativeFileDialog
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct OpenFileName
{
public int lStructSize;
public IntPtr hwndOwner;
public IntPtr hInstance;
public IntPtr lpstrFilter; // double-null-terminated; manual buffer
public IntPtr lpstrCustomFilter;
public int nMaxCustFilter;
public int nFilterIndex;
public IntPtr lpstrFile; // in/out buffer, manual
public int nMaxFile;
public IntPtr lpstrFileTitle; // out buffer, manual
public int nMaxFileTitle;
[MarshalAs(UnmanagedType.LPWStr)] public string lpstrInitialDir;
[MarshalAs(UnmanagedType.LPWStr)] public string lpstrTitle;
public int Flags;
public short nFileOffset;
public short nFileExtension;
[MarshalAs(UnmanagedType.LPWStr)] public string lpstrDefExt;
public IntPtr lCustData;
public IntPtr lpfnHook;
[MarshalAs(UnmanagedType.LPWStr)] public string lpTemplateName;
public IntPtr pvReserved;
public int dwReserved;
public int FlagsEx;
}
[DllImport("comdlg32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool GetSaveFileNameW(ref OpenFileName ofn);
[DllImport("comdlg32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool GetOpenFileNameW(ref OpenFileName ofn);
[DllImport("user32.dll")]
private static extern IntPtr GetActiveWindow();
const int OFN_OVERWRITEPROMPT = 0x00000002;
const int OFN_NOCHANGEDIR = 0x00000008;
const int OFN_FILEMUSTEXIST = 0x00001000;
const int OFN_PATHMUSTEXIST = 0x00000800;
const int OFN_EXPLORER = 0x00080000;
/// <summary>
/// Show a Save As dialog. Returns the chosen full path, or null on cancel /
/// unavailable platform. <paramref name="filterPattern"/> is e.g. "*.vrm".
/// Must be called on the main (UI) thread.
/// </summary>
public static string SaveFile(string title, string initialDir, string defaultFileName,
string filterLabel, string filterPattern, string defExt)
{
if (Application.platform != RuntimePlatform.WindowsPlayer &&
Application.platform != RuntimePlatform.WindowsEditor)
return null;
const int bufChars = 4096;
IntPtr fileBuf = Marshal.AllocHGlobal(bufChars * 2);
IntPtr titleBuf = Marshal.AllocHGlobal(512 * 2);
// filter is "Label\0pattern\0\0" — embedded nulls, so build it by hand.
IntPtr filterBuf = Marshal.StringToHGlobalUni((filterLabel ?? "Files") + "\0" + (filterPattern ?? "*.*") + "\0");
try
{
// seed the file buffer with the suggested name
byte[] seed = Encoding.Unicode.GetBytes((defaultFileName ?? "") + "\0");
if (seed.Length > bufChars * 2) Array.Resize(ref seed, bufChars * 2);
Marshal.Copy(seed, 0, fileBuf, seed.Length);
Marshal.WriteInt16(titleBuf, 0, 0);
var ofn = new OpenFileName();
ofn.lStructSize = Marshal.SizeOf(typeof(OpenFileName));
ofn.hwndOwner = GetActiveWindow();
ofn.lpstrFilter = filterBuf;
ofn.nFilterIndex = 1;
ofn.lpstrFile = fileBuf;
ofn.nMaxFile = bufChars;
ofn.lpstrFileTitle = titleBuf;
ofn.nMaxFileTitle = 512;
ofn.lpstrInitialDir = initialDir;
ofn.lpstrTitle = title;
ofn.lpstrDefExt = defExt;
ofn.Flags = OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST | OFN_NOCHANGEDIR | OFN_EXPLORER;
bool ok = GetSaveFileNameW(ref ofn);
if (!ok) return null; // cancelled (or error)
return Marshal.PtrToStringUni(fileBuf);
}
catch (Exception e)
{
Debug.LogWarning("[WeightStudio] Save dialog unavailable, using default path: " + e.Message);
return null;
}
finally
{
Marshal.FreeHGlobal(fileBuf);
Marshal.FreeHGlobal(titleBuf);
Marshal.FreeHGlobal(filterBuf);
}
}
/// <summary>
/// Show an Open dialog. Returns the chosen full path, or null on cancel /
/// unavailable platform. <paramref name="filterPattern"/> is e.g. "*.vbx".
/// Must be called on the main (UI) thread.
/// </summary>
public static string OpenFile(string title, string initialDir,
string filterLabel, string filterPattern)
{
if (Application.platform != RuntimePlatform.WindowsPlayer &&
Application.platform != RuntimePlatform.WindowsEditor)
return null;
const int bufChars = 4096;
IntPtr fileBuf = Marshal.AllocHGlobal(bufChars * 2);
IntPtr titleBuf = Marshal.AllocHGlobal(512 * 2);
IntPtr filterBuf = Marshal.StringToHGlobalUni((filterLabel ?? "Files") + "\0" + (filterPattern ?? "*.*") + "\0");
try
{
Marshal.WriteInt16(fileBuf, 0, 0); // empty initial file name
Marshal.WriteInt16(titleBuf, 0, 0);
var ofn = new OpenFileName();
ofn.lStructSize = Marshal.SizeOf(typeof(OpenFileName));
ofn.hwndOwner = GetActiveWindow();
ofn.lpstrFilter = filterBuf;
ofn.nFilterIndex = 1;
ofn.lpstrFile = fileBuf;
ofn.nMaxFile = bufChars;
ofn.lpstrFileTitle = titleBuf;
ofn.nMaxFileTitle = 512;
ofn.lpstrInitialDir = initialDir;
ofn.lpstrTitle = title;
ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_NOCHANGEDIR | OFN_EXPLORER;
bool ok = GetOpenFileNameW(ref ofn);
if (!ok) return null; // cancelled (or error)
return Marshal.PtrToStringUni(fileBuf);
}
catch (Exception e)
{
Debug.LogWarning("[WeightStudio] Open dialog unavailable: " + e.Message);
return null;
}
finally
{
Marshal.FreeHGlobal(fileBuf);
Marshal.FreeHGlobal(titleBuf);
Marshal.FreeHGlobal(filterBuf);
}
}
}
}