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 (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 (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); } } } }