Files
App-Installer-For-Windows-8…/DataUtils/Storage.cs
2026-02-06 18:12:21 +08:00

717 lines
20 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace DataUtils
{
[ComVisible (true)]
[ClassInterface (ClassInterfaceType.AutoDual)]
public class _I_Path
{
public string Current
{
get
{
try { return Directory.GetCurrentDirectory (); }
catch { return string.Empty; }
}
set
{
try
{
if (!string.IsNullOrEmpty (value)) Directory.SetCurrentDirectory (value);
}
catch { /* ignore */ }
}
}
public string Program
{
get { return Utilities.GetCurrentProgramPath (); }
}
public string Root
{
get
{
try
{
string prog = Utilities.GetCurrentProgramPath ();
return Path.GetDirectoryName (prog) ?? string.Empty;
}
catch { return string.Empty; }
}
}
public string Combine (string l, string r)
{
if (string.IsNullOrEmpty (l)) return r ?? string.Empty;
if (string.IsNullOrEmpty (r)) return l ?? string.Empty;
try { return Path.Combine (l, r); }
catch { return l + Path.DirectorySeparatorChar + r; }
}
public string GetName (string path)
{
if (string.IsNullOrEmpty (path)) return string.Empty;
try
{
return Path.GetFileName (path);
}
catch { return string.Empty; }
}
public string GetDirectory (string path)
{
if (string.IsNullOrEmpty (path)) return string.Empty;
try
{
return Path.GetDirectoryName (path) ?? string.Empty;
}
catch { return string.Empty; }
}
public string GetDir (string path) { return GetDirectory (path); }
public bool Exist (string path)
{
if (string.IsNullOrEmpty (path)) return false;
return File.Exists (path) || Directory.Exists (path);
}
public bool FileExist (string filepath)
{
if (string.IsNullOrEmpty (filepath)) return false;
return File.Exists (filepath);
}
public bool DirectoryExist (string dirpath)
{
if (string.IsNullOrEmpty (dirpath)) return false;
return Directory.Exists (dirpath);
}
public bool DirExist (string dirpath) { return DirectoryExist (dirpath); }
public string GetEnvironmentString (string str)
{
if (string.IsNullOrEmpty (str)) return string.Empty;
try
{
return Environment.ExpandEnvironmentVariables (str);
}
catch { return str; }
}
// Valid Windows filename?
public bool ValidName (string filename)
{
if (string.IsNullOrEmpty (filename)) return false;
char [] invalid = Path.GetInvalidFileNameChars ();
return filename.IndexOfAny (invalid) < 0;
}
// filter may be e.g. "*.txt;*.md" or using "\" separators per legacy code
public string EnumFilesToJson (string dir, string filter, bool withpath, bool sort, bool includesub)
{
var arr = EnumFiles (dir, filter, withpath, sort, includesub);
return Utilities.StringArrayToJson (arr);
}
public string EnumDirsToJson (string dir, bool withpath, bool sort, bool includesub)
{
var arr = EnumDirs (dir, withpath, sort, includesub);
return Utilities.StringArrayToJson (arr);
}
public string EnumSubDirsToJson (string dir, bool withpath)
{
var arr = EnumSubDirs (dir, withpath);
return Utilities.StringArrayToJson (arr);
}
public string [] EnumFiles (string dir, string filter, bool withpath, bool sort, bool includesub)
{
if (string.IsNullOrEmpty (dir)) return new string [0];
var patterns = Utilities.SplitFilters (filter);
var list = new List<string> (100);
try
{
var searchOption = includesub ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
foreach (var pat in patterns)
{
try
{
foreach (var f in Directory.EnumerateFiles (dir, pat, searchOption))
{
list.Add (withpath ? f : Path.GetFileName (f));
}
}
catch (UnauthorizedAccessException) { /* skip */ }
catch (DirectoryNotFoundException) { /* skip */ }
catch (IOException) { /* skip */ }
}
if (sort)
{
list.Sort (StringComparer.OrdinalIgnoreCase);
}
}
catch
{
// fallback: empty
}
return list.ToArray ();
}
public string [] EnumDirs (string dir, bool withpath, bool sort, bool includesub)
{
if (string.IsNullOrEmpty (dir)) return new string [0];
var list = new List<string> (100);
try
{
var searchOption = includesub ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
foreach (var d in Directory.EnumerateDirectories (dir, "*", searchOption))
{
list.Add (withpath ? d : Path.GetFileName (d));
}
if (sort) list.Sort (StringComparer.OrdinalIgnoreCase);
}
catch { }
return list.ToArray ();
}
public string [] EnumSubDirs (string dir, bool withpath)
{
return EnumDirs (dir, withpath, true, true);
}
public string CommonPrefix (string path1, string path2)
{
if (string.IsNullOrEmpty (path1) || string.IsNullOrEmpty (path2)) return string.Empty;
try
{
string a = Utilities.NormalizeFullPath (path1);
string b = Utilities.NormalizeFullPath (path2);
string [] asplit = a.Split (new char [] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries);
string [] bsplit = b.Split (new char [] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries);
int min = Math.Min (asplit.Length, bsplit.Length);
var sb = new StringBuilder ();
for (int i = 0; i < min; i++)
{
if (!string.Equals (asplit [i], bsplit [i], StringComparison.OrdinalIgnoreCase)) break;
sb.Append (asplit [i]);
sb.Append (Path.DirectorySeparatorChar);
}
return sb.ToString ().TrimEnd (Path.DirectorySeparatorChar);
}
catch { return string.Empty; }
}
public string EnsureDirSlash (string dir)
{
if (string.IsNullOrEmpty (dir)) return string.Empty;
try
{
if (!dir.EndsWith (Path.DirectorySeparatorChar.ToString ()))
dir += Path.DirectorySeparatorChar;
return dir;
}
catch { return dir; }
}
public string Normalize (string path)
{
if (string.IsNullOrEmpty (path)) return string.Empty;
try { return Path.GetFullPath (path); }
catch { return path; }
}
public string FullPathName (string path) { return Normalize (path); }
public string FullPath (string path) { return FullPathName (path); }
public string Expand (string path) { return GetEnvironmentString (path); }
// GetFolder via SHGetFolderPath (preserves the original csidl param usage)
public string GetFolder (int csidl)
{
try
{
// try P/Invoke to SHGetFolderPath
return ShellHelpers.GetFolderPath (csidl);
}
catch
{
return string.Empty;
}
}
// KnownFolder by GUID string (wraps SHGetKnownFolderPath)
public string KnownFolder (string guidString)
{
if (string.IsNullOrWhiteSpace (guidString)) return string.Empty;
Guid guid;
try
{
guid = new Guid (guidString);
}
catch
{
return string.Empty;
}
try
{
return ShellHelpers.GetKnownFolderPath (guid);
}
catch
{
return string.Empty;
}
}
public bool PEquals (string l, string r)
{
if (l == null && r == null) return true;
if (l == null || r == null) return false;
string a = Utilities.NormalizeFullPath (l);
string b = Utilities.NormalizeFullPath (r);
return string.Equals (a, b, StringComparison.OrdinalIgnoreCase);
}
public bool Open (string path)
{
if (string.IsNullOrEmpty (path)) return false;
try
{
if (File.Exists (path))
{
Process.Start (path);
return true;
}
if (Directory.Exists (path))
{
Process.Start ("explorer.exe", path);
return true;
}
}
catch
{
}
return false;
}
}
// Basic entry object
[ComVisible (true)]
[ClassInterface (ClassInterfaceType.AutoDual)]
public class _I_Entry
{
protected string path;
public _I_Entry (string path)
{
this.path = path ?? string.Empty;
}
public _I_Entry ()
{
this.path = string.Empty;
}
public virtual string Path
{
get { return path; }
set { path = value ?? string.Empty; }
}
public virtual string Name
{
get
{
try
{
return System.IO.Path.GetFileName (path) ?? string.Empty;
}
catch { return string.Empty; }
}
}
public virtual string Directory
{
get
{
try { return System.IO.Path.GetDirectoryName (path) ?? string.Empty; }
catch { return string.Empty; }
}
}
public virtual string Root { get { return Directory; } }
public virtual bool Exist
{
get
{
return File.Exists (path) || System.IO.Directory.Exists (path);
}
}
public virtual string Uri
{
get
{
try
{
Uri uri;
if (System.Uri.TryCreate (path, UriKind.Absolute, out uri))
{
return uri.AbsoluteUri;
}
else
{
Uri u = new Uri (System.IO.Path.GetFullPath (path));
return u.AbsoluteUri;
}
}
catch
{
return string.Empty;
}
}
}
public virtual string FullPath
{
get
{
try { return System.IO.Path.GetFullPath (path); }
catch { return path; }
}
}
// Return relative path from frontdir to this.Path; similar semantics to C++ code
public string RelativePath (string frontdir)
{
if (string.IsNullOrEmpty (path) || string.IsNullOrEmpty (frontdir)) return string.Empty;
try
{
string fullFile = System.IO.Path.GetFullPath (path);
string fullDir = System.IO.Path.GetFullPath (frontdir);
if (!fullDir.EndsWith (System.IO.Path.DirectorySeparatorChar.ToString ()))
fullDir += System.IO.Path.DirectorySeparatorChar;
if (!string.Equals (System.IO.Path.GetPathRoot (fullFile), System.IO.Path.GetPathRoot (fullDir), StringComparison.OrdinalIgnoreCase))
return string.Empty;
if (!fullFile.StartsWith (fullDir, StringComparison.OrdinalIgnoreCase)) return string.Empty;
return fullFile.Substring (fullDir.Length);
}
catch
{
return string.Empty;
}
}
}
[ComVisible (true)]
[ClassInterface (ClassInterfaceType.AutoDual)]
public class _I_File: _I_Entry
{
// last encoding used when reading
protected Encoding lastEncoding;
public _I_File () : base (string.Empty) { }
public _I_File (string filepath) : base (filepath) { }
// Read file contents; detect BOM if present by using StreamReader with detectEncodingFromByteOrderMarks = true
public string Get ()
{
if (string.IsNullOrEmpty (path)) return string.Empty;
try
{
using (FileStream fs = new FileStream (path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (StreamReader sr = new StreamReader (fs, Encoding.UTF8, true))
{
string text = sr.ReadToEnd ();
lastEncoding = sr.CurrentEncoding;
return text;
}
}
catch
{
return null;
}
}
public void Set (string content)
{
if (string.IsNullOrEmpty (path)) return;
try
{
string dir = System.IO.Path.GetDirectoryName (path);
if (!string.IsNullOrEmpty (dir) && !System.IO.Directory.Exists (dir))
{
System.IO.Directory.CreateDirectory (dir);
}
Encoding enc = lastEncoding ?? Encoding.UTF8;
using (FileStream fs = new FileStream (path, FileMode.Create, FileAccess.Write, FileShare.Read))
using (StreamWriter sw = new StreamWriter (fs, enc))
{
sw.Write (content ?? string.Empty);
sw.Flush ();
}
}
catch
{
// ignore write errors
}
}
public string Content
{
get { return Get (); }
set { Set (value); }
}
public override bool Exist
{
get { return File.Exists (path); }
}
public string FilePath
{
get { return this.Path; }
set { this.Path = value; }
}
}
[ComVisible (true)]
[ClassInterface (ClassInterfaceType.AutoDual)]
public class _I_Directory: _I_Entry
{
public _I_Directory () : base (string.Empty) { }
public _I_Directory (string dirpath) : base (dirpath) { }
public _I_Directory (_I_Entry file) : base (file != null ? file.Directory : string.Empty) { }
public string DirectoryPath
{
get { return this.Path; }
set { this.Path = value; }
}
public string DirPath { get { return DirectoryPath; } set { DirectoryPath = value; } }
public override bool Exist { get { return System.IO.Directory.Exists (path); } }
public string EnumFilesToJson (string filter, bool withpath, bool sort, bool includesub)
{
_I_Path p = new _I_Path ();
string [] arr = p.EnumFiles (DirPath, filter, withpath, sort, includesub);
return Utilities.StringArrayToJson (arr);
}
public string EnumDirsToJson (bool withpath, bool sort, bool includesub)
{
_I_Path p = new _I_Path ();
string [] arr = p.EnumDirs (DirPath, withpath, sort, includesub);
return Utilities.StringArrayToJson (arr);
}
public string EnumSubDirsToJson (bool withpath)
{
_I_Path p = new _I_Path ();
string [] arr = p.EnumSubDirs (DirPath, withpath);
return Utilities.StringArrayToJson (arr);
}
public string [] EnumFiles (string filter, bool withpath, bool sort, bool includesub)
{
_I_Path p = new _I_Path ();
return p.EnumFiles (DirPath, filter, withpath, sort, includesub);
}
public string [] EnumDirs (bool withpath, bool sort, bool includesub)
{
_I_Path p = new _I_Path ();
return p.EnumDirs (DirPath, withpath, sort, includesub);
}
public string [] EnumSubDirs (bool withpath)
{
_I_Path p = new _I_Path ();
return p.EnumSubDirs (DirPath, withpath);
}
}
[ComVisible (true)]
[ClassInterface (ClassInterfaceType.AutoDual)]
public class _I_Explorer
{
[DllImport ("user32.dll")]
private static extern IntPtr GetForegroundWindow ();
class WindowWrapper: IWin32Window
{
private IntPtr _hwnd;
public WindowWrapper (IntPtr handle) { _hwnd = handle; }
public IntPtr Handle { get { return _hwnd; } }
}
private static IWin32Window GetActiveWindowOwner ()
{
IntPtr hWnd = GetForegroundWindow ();
return hWnd != IntPtr.Zero ? new WindowWrapper (hWnd) : null;
}
private static void CallJS (object jsFunc, params object [] args)
{
if (jsFunc == null) return;
try
{
object [] realArgs = new object [args.Length + 1];
realArgs [0] = jsFunc; // thisArg
Array.Copy (args, 0, realArgs, 1, args.Length);
jsFunc.GetType ().InvokeMember (
"call",
System.Reflection.BindingFlags.InvokeMethod,
null,
jsFunc,
realArgs
);
}
catch { }
}
public void File (string filter, string initDir, object jsCallback)
{
IWin32Window owner = GetActiveWindowOwner ();
Thread t = new Thread (() =>
{
string result = string.Empty;
try
{
using (OpenFileDialog dlg = new OpenFileDialog ())
{
dlg.Filter = filter;
dlg.InitialDirectory = string.IsNullOrEmpty (initDir)
? Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments)
: initDir;
dlg.Multiselect = false;
if (dlg.ShowDialog (owner) == DialogResult.OK)
result = dlg.FileName;
}
}
catch { }
CallJS (jsCallback, result);
});
t.IsBackground = true;
t.SetApartmentState (ApartmentState.STA);
t.Start ();
}
public void Files (string filter, string initDir, object jsCallback)
{
IWin32Window owner = GetActiveWindowOwner ();
Thread t = new Thread (() =>
{
string result = "[]";
try
{
using (OpenFileDialog dlg = new OpenFileDialog ())
{
dlg.Filter = filter;
dlg.InitialDirectory = string.IsNullOrEmpty (initDir)
? Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments)
: initDir;
dlg.Multiselect = true;
if (dlg.ShowDialog (owner) == DialogResult.OK)
result = Newtonsoft.Json.JsonConvert.SerializeObject (dlg.FileNames);
}
}
catch { }
CallJS (jsCallback, result);
});
t.IsBackground = true;
t.SetApartmentState (ApartmentState.STA);
t.Start ();
}
public void Dir (string initDir, object jsCallback)
{
IWin32Window owner = GetActiveWindowOwner ();
Thread t = new Thread (() =>
{
string result = string.Empty;
try
{
using (FolderBrowserDialog dlg = new FolderBrowserDialog ())
{
dlg.SelectedPath = string.IsNullOrEmpty (initDir)
? Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments)
: initDir;
if (dlg.ShowDialog (owner) == DialogResult.OK)
result = dlg.SelectedPath;
}
}
catch { }
CallJS (jsCallback, result);
});
t.IsBackground = true;
t.SetApartmentState (ApartmentState.STA);
t.Start ();
}
public void Dirs (string initDir, object jsCallback)
{
IWin32Window owner = GetActiveWindowOwner ();
Thread t = new Thread (() =>
{
string result = "[]";
try
{
using (var dlg = new OpenFileDialog ())
{
// trick: 多选文件夹
dlg.ValidateNames = false;
dlg.CheckFileExists = false;
dlg.CheckPathExists = true;
dlg.FileName = "SelectFolder";
dlg.InitialDirectory = string.IsNullOrEmpty (initDir)
? Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments)
: initDir;
dlg.Multiselect = true;
if (dlg.ShowDialog (owner) == DialogResult.OK)
{
var dirs = dlg.FileNames.Select (f => Path.GetDirectoryName (f)).Distinct ().ToArray ();
result = Newtonsoft.Json.JsonConvert.SerializeObject (dirs);
}
}
}
catch { }
CallJS (jsCallback, result);
});
t.IsBackground = true;
t.SetApartmentState (ApartmentState.STA);
t.Start ();
}
}
[ComVisible (true)]
[ClassInterface (ClassInterfaceType.AutoDual)]
public class _I_Storage
{
private static void CallJS (object jsFunc, params object [] args)
{
if (jsFunc == null) return;
try
{
// 这里固定第一个参数为 thisArg比如 1
object [] realArgs = new object [args.Length + 1];
realArgs [0] = jsFunc; // thisArg
Array.Copy (args, 0, realArgs, 1, args.Length);
jsFunc.GetType ().InvokeMember (
"call",
BindingFlags.InvokeMethod,
null,
jsFunc,
realArgs
);
}
catch
{
// ignore errors in callback invocation
}
}
protected _I_Path path = new _I_Path ();
public _I_Path Path { get { return path; } }
public _I_File GetFile (string path) { return new _I_File (path); }
public _I_Directory GetDirectory (string path) { return new _I_Directory (path); }
public _I_Directory GetDir (string path) { return GetDirectory (path); }
public _I_Explorer Explorer => new _I_Explorer ();
}
// Small shell helpers that P/Invoke for folder retrieval using CSIDL or Known Folder GUIDs
internal static class ShellHelpers
{
[System.Runtime.InteropServices.DllImport ("shell32.dll")]
private static extern int SHGetFolderPathW (IntPtr hwndOwner, int nFolder, IntPtr hToken, uint dwFlags, [System.Runtime.InteropServices.MarshalAs (System.Runtime.InteropServices.UnmanagedType.LPWStr)] StringBuilder pszPath);
public static string GetFolderPath (int csidl)
{
StringBuilder sb = new StringBuilder (260);
int hr = SHGetFolderPathW (IntPtr.Zero, csidl, IntPtr.Zero, 0, sb);
if (hr == 0) return sb.ToString ();
return string.Empty;
}
[System.Runtime.InteropServices.DllImport ("shell32.dll")]
private static extern int SHGetKnownFolderPath ([System.Runtime.InteropServices.MarshalAs (System.Runtime.InteropServices.UnmanagedType.LPStruct)] Guid rfid, uint dwFlags, IntPtr hToken, out IntPtr ppszPath);
[System.Runtime.InteropServices.DllImport ("ole32.dll")]
private static extern void CoTaskMemFree (IntPtr pv);
public static string GetKnownFolderPath (Guid guid)
{
IntPtr pathPtr;
int hr = SHGetKnownFolderPath (guid, 0, IntPtr.Zero, out pathPtr);
if (hr != 0 || pathPtr == IntPtr.Zero) return string.Empty;
try
{
string path = Marshal.PtrToStringUni (pathPtr);
return path ?? string.Empty;
}
finally
{
if (pathPtr != IntPtr.Zero) CoTaskMemFree (pathPtr);
}
}
}
}