Private/srgNative.ps1
|
<#
C#-Helfer, die in reinem PowerShell nicht sauber gehen: - FolderPicker: moderner Explorer-Ordnerdialog (IFileOpenDialog) mit Adressleiste, Schnellzugriff, Mehrfachauswahl und Dark Mode. Der WinForms-FolderBrowserDialog von .NET Framework (PS 5.1) ist nur der alte Baumdialog. - Runner: startet robocopy mit umgeleiteter Ausgabe auf Hintergrund-Threads. PowerShell-Scriptblocks duerfen dort nicht laufen, deshalb sammelt der Runner Zeilen in einer Liste, die ein WinForms-Timer auf dem UI-Thread abholt. robocopy ueberschreibt die Prozentanzeige mit CR - solche Segmente landen in Progress statt im Protokoll. - PathProbe: Verzeichnispruefung mit Zeitlimit (Test-Path auf eine tote Freigabe blockiert sonst die Oberflaeche bis zu 20 s). #> function Initialize-srgHelper { if ('Srg.Runner' -as [type]) { return } Add-Type -TypeDefinition @' using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; using System.Text; using System.Text.RegularExpressions; using System.Threading; namespace Srg { [ComImport, Guid("DC1C5A9C-E88A-4dde-A5A1-60F82A20AEF7")] internal class FileOpenDialogCom { } [ComImport, Guid("42f85136-db7e-439c-85f1-e4075d135fc8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal interface IFileOpenDialog { [PreserveSig] int Show(IntPtr parent); void SetFileTypes(uint cFileTypes, IntPtr rgFilterSpec); void SetFileTypeIndex(uint iFileType); void GetFileTypeIndex(out uint piFileType); void Advise(IntPtr pfde, out uint pdwCookie); void Unadvise(uint dwCookie); void SetOptions(uint fos); void GetOptions(out uint pfos); void SetDefaultFolder(IShellItem psi); void SetFolder(IShellItem psi); void GetFolder(out IShellItem ppsi); void GetCurrentSelection(out IShellItem ppsi); void SetFileName([MarshalAs(UnmanagedType.LPWStr)] string pszName); void GetFileName([MarshalAs(UnmanagedType.LPWStr)] out string pszName); void SetTitle([MarshalAs(UnmanagedType.LPWStr)] string pszTitle); void SetOkButtonLabel([MarshalAs(UnmanagedType.LPWStr)] string pszText); void SetFileNameLabel([MarshalAs(UnmanagedType.LPWStr)] string pszLabel); void GetResult(out IShellItem ppsi); void AddPlace(IShellItem psi, int fdap); void SetDefaultExtension([MarshalAs(UnmanagedType.LPWStr)] string pszDefaultExtension); void Close(int hr); void SetClientGuid(ref Guid guid); void ClearClientData(); void SetFilter(IntPtr pFilter); void GetResults(out IShellItemArray ppenum); void GetSelectedItems(out IShellItemArray ppsai); } [ComImport, Guid("43826D1E-E718-42EE-BC55-A1E261C37BFE"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal interface IShellItem { void BindToHandler(IntPtr pbc, ref Guid bhid, ref Guid riid, out IntPtr ppv); void GetParent(out IShellItem ppsi); void GetDisplayName(uint sigdnName, [MarshalAs(UnmanagedType.LPWStr)] out string ppszName); void GetAttributes(uint sfgaoMask, out uint psfgaoAttribs); void Compare(IShellItem psi, uint hint, out int piOrder); } [ComImport, Guid("b63ea76d-1f85-456f-a19c-48159efa858b"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal interface IShellItemArray { void BindToHandler(IntPtr pbc, ref Guid bhid, ref Guid riid, out IntPtr ppvOut); void GetPropertyStore(int flags, ref Guid riid, out IntPtr ppv); void GetPropertyDescriptionList(IntPtr keyType, ref Guid riid, out IntPtr ppv); void GetAttributes(int dwAttribFlags, uint sfgaoMask, out uint psfgaoAttribs); void GetCount(out uint pdwNumItems); void GetItemAt(uint dwIndex, out IShellItem ppsi); void EnumItems(out IntPtr ppenumShellItems); } public static class FolderPicker { [DllImport("shell32.dll", CharSet = CharSet.Unicode, PreserveSig = false)] private static extern void SHCreateItemFromParsingName(string pszPath, IntPtr pbc, ref Guid riid, out IShellItem ppv); private const uint FOS_PICKFOLDERS = 0x20, FOS_FORCEFILESYSTEM = 0x40, FOS_NOCHANGEDIR = 0x8, FOS_ALLOWMULTISELECT = 0x200, FOS_PATHMUSTEXIST = 0x800; private const uint SIGDN_FILESYSPATH = 0x80058000; // null = abgebrochen public static string[] Pick(IntPtr owner, string title, string initialPath, bool multi) { IFileOpenDialog dlg = (IFileOpenDialog)new FileOpenDialogCom(); try { uint opt; dlg.GetOptions(out opt); opt |= FOS_PICKFOLDERS | FOS_FORCEFILESYSTEM | FOS_NOCHANGEDIR | FOS_PATHMUSTEXIST; if (multi) opt |= FOS_ALLOWMULTISELECT; dlg.SetOptions(opt); if (!string.IsNullOrEmpty(title)) dlg.SetTitle(title); if (!string.IsNullOrEmpty(initialPath)) { try { Guid iid = typeof(IShellItem).GUID; IShellItem folder; SHCreateItemFromParsingName(initialPath, IntPtr.Zero, ref iid, out folder); dlg.SetFolder(folder); } catch { } } if (dlg.Show(owner) != 0) return null; // Nach Show() darf nichts mehr werfen: die Aufrufer fallen sonst auf den alten // Dialog zurueck und der Benutzer sieht die Ordnerauswahl ein zweites Mal. // GetResults() liefert je nach Windows-Build S_OK mit leerem Zeiger (auch bei // Mehrfachauswahl) - dann gilt GetResult(). List<string> paths = new List<string>(); if (multi) { try { IShellItemArray items; dlg.GetResults(out items); if (items != null) { uint count; items.GetCount(out count); for (uint i = 0; i < count; i++) { IShellItem it; items.GetItemAt(i, out it); AddPath(paths, it); } } } catch { } } if (paths.Count == 0) { try { IShellItem one; dlg.GetResult(out one); AddPath(paths, one); } catch { } } return paths.Count > 0 ? paths.ToArray() : null; } finally { Marshal.ReleaseComObject(dlg); } } private static void AddPath(List<string> paths, IShellItem item) { if (item == null) return; string p; item.GetDisplayName(SIGDN_FILESYSPATH, out p); if (!string.IsNullOrEmpty(p)) paths.Add(p); } } public static class PathProbe { // 1 = vorhanden, 0 = nicht vorhanden, -1 = keine Antwort innerhalb des Zeitlimits public static int DirectoryExists(string path, int timeoutMs) { bool result = false; Thread t = new Thread(() => { try { result = Directory.Exists(path); } catch { } }); t.IsBackground = true; t.Start(); if (!t.Join(timeoutMs)) return -1; return result ? 1 : 0; } } public class Runner { private Process _p; private readonly object _lock = new object(); private List<string> _lines = new List<string>(); private static readonly Regex Percent = new Regex(@"^\s*\d+(\.\d+)?%\s*$"); public volatile string Progress = ""; public volatile string Current = ""; public volatile bool Finished; public volatile bool Cancelled; public int ExitCode = -1; public DateTime Started; public DateTime Ended; public long LineCount; public void Start(string fileName, string arguments, int codePage) { ProcessStartInfo psi = new ProcessStartInfo(fileName, arguments); psi.UseShellExecute = false; psi.CreateNoWindow = true; psi.RedirectStandardOutput = true; psi.RedirectStandardError = true; Encoding enc; try { enc = Encoding.GetEncoding(codePage); } catch { enc = Encoding.Default; } psi.StandardOutputEncoding = enc; psi.StandardErrorEncoding = enc; _p = new Process(); _p.StartInfo = psi; _p.Start(); Started = DateTime.Now; Thread t1 = new Thread(() => Pump(_p.StandardOutput)); t1.IsBackground = true; t1.Start(); Thread t2 = new Thread(() => Pump(_p.StandardError)); t2.IsBackground = true; t2.Start(); Thread t3 = new Thread(() => { t1.Join(); t2.Join(); _p.WaitForExit(); try { ExitCode = _p.ExitCode; } catch { } Ended = DateTime.Now; Finished = true; }); t3.IsBackground = true; t3.Start(); } private void Pump(StreamReader reader) { StringBuilder sb = new StringBuilder(); bool lastCr = false; int c; while ((c = reader.Read()) != -1) { char ch = (char)c; if (ch == '\r') { Flush(sb, true); lastCr = true; continue; } if (ch == '\n') { if (!(lastCr && sb.Length == 0)) Flush(sb, false); lastCr = false; continue; } lastCr = false; sb.Append(ch); } if (sb.Length > 0) Flush(sb, false); } private void Flush(StringBuilder sb, bool cr) { string s = sb.ToString(); sb.Length = 0; if (cr && Percent.IsMatch(s)) { Progress = s.Trim(); return; } if (s.Trim().Length > 0 && s.IndexOf('\t') >= 0) { Current = s.Trim(); Progress = ""; } lock (_lock) { _lines.Add(s); LineCount++; } } public string[] Drain(int max) { lock (_lock) { if (_lines.Count == 0) return new string[0]; int n = Math.Min(max, _lines.Count); string[] r = _lines.GetRange(0, n).ToArray(); _lines.RemoveRange(0, n); return r; } } public int Pending { get { lock (_lock) { return _lines.Count; } } } public void Stop() { Cancelled = true; try { if (_p != null && !_p.HasExited) _p.Kill(); } catch { } } } } '@ } |