This commit is contained in:
Bruce
2026-01-11 11:40:21 +08:00
parent 3ab9761705
commit 2e6214a35a
69 changed files with 6519 additions and 13232 deletions
+78
View File
@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{FFD3FD52-37A8-4F43-883C-DE8D996CB0E0}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>DataUtils</RootNamespace>
<AssemblyName>DataUtils</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>..\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.13.0.4\lib\net40\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Download.cs" />
<Compile Include="HResult.cs" />
<Compile Include="IE.cs" />
<Compile Include="Locale.cs" />
<Compile Include="Process.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Resources.cs" />
<Compile Include="ResourceXml.cs" />
<Compile Include="Storage.cs" />
<Compile Include="String.cs" />
<Compile Include="SysInit.cs" />
<Compile Include="Taskbar.cs" />
<Compile Include="Theme.cs" />
<Compile Include="Utils.cs" />
<Compile Include="Version.cs" />
<Compile Include="VisualElements.cs" />
<Compile Include="WebBrowser.cs" />
<Compile Include="Window.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
+270
View File
@@ -0,0 +1,270 @@
using System;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;
using System.Reflection;
using System.Globalization;
using System.Runtime.InteropServices;
using Newtonsoft.Json;
namespace DataUtils
{
[ComVisible (false)]
public sealed class DownloadHelper
{
private static readonly object s_downloadLock = new object (); // mimic g_download_cs
// Public static entry (starts background thread)
public static void DownloadFile (string httpUrl, string savePath, object onProgress, object onComplete, object onError)
{
if (string.IsNullOrEmpty (httpUrl)) throw new ArgumentNullException ("httpUrl");
if (string.IsNullOrEmpty (savePath)) throw new ArgumentNullException ("savePath");
var helper = new DownloadHelper (httpUrl, savePath, onProgress, onComplete, onError);
Thread th = new Thread (helper.Worker);
th.IsBackground = true;
th.Start ();
}
// Instance members
private readonly string _url;
private readonly string _savePath;
private readonly object _cbProgress;
private readonly object _cbComplete;
private readonly object _cbError;
private DownloadHelper (string url, string savePath, object cbProgress, object cbComplete, object cbError)
{
_url = url;
_savePath = savePath;
_cbProgress = cbProgress;
_cbComplete = cbComplete;
_cbError = cbError;
}
private void Worker ()
{
// Single download at a time (mimic CreateScopedLock)
lock (s_downloadLock)
{
HttpWebRequest request = null;
HttpWebResponse response = null;
Stream responseStream = null;
FileStream fileStream = null;
try
{
request = (HttpWebRequest)WebRequest.Create (_url);
request.Method = "GET";
request.AllowAutoRedirect = true;
request.UserAgent = "Mozilla/5.0 (Windows NT 6.2; Win32; x86) AppInstallerUpdater/1.0";
request.Timeout = 60000; // 60s connect timeout
request.ReadWriteTimeout = 60000;
response = (HttpWebResponse)request.GetResponse ();
long contentLength = -1;
try { contentLength = response.ContentLength; } catch { contentLength = -1; }
responseStream = response.GetResponseStream ();
// create directory if needed
string dir = Path.GetDirectoryName (_savePath);
if (!string.IsNullOrEmpty (dir) && !Directory.Exists (dir))
Directory.CreateDirectory (dir);
fileStream = new FileStream (_savePath, FileMode.Create, FileAccess.Write, FileShare.Read);
byte [] buffer = new byte [8192];
int bytesRead;
long received = 0;
var lastCheck = DateTime.UtcNow;
long lastBytes = 0;
const double reportIntervalSeconds = 0.5;
while ((bytesRead = SafeRead (responseStream, buffer, 0, buffer.Length)) > 0)
{
fileStream.Write (buffer, 0, bytesRead);
received += bytesRead;
var now = DateTime.UtcNow;
double interval = (now - lastCheck).TotalSeconds;
long speed = -1;
if (interval >= reportIntervalSeconds)
{
long bytesInInterval = received - lastBytes;
if (interval > 0)
speed = (long)(bytesInInterval / interval); // B/s
lastCheck = now;
lastBytes = received;
}
ReportProgress (received, contentLength >= 0 ? contentLength : 0, speed);
}
// flush and close file
fileStream.Flush ();
ReportComplete (_savePath, received);
}
catch (WebException wex)
{
string reason = BuildWebExceptionReason (wex);
ReportError (_savePath, reason);
}
catch (Exception ex)
{
ReportError (_savePath, ex.Message ?? ex.ToString ());
}
finally
{
try { if (responseStream != null) responseStream.Close (); } catch { }
try { if (response != null) response.Close (); } catch { }
try { if (fileStream != null) fileStream.Close (); } catch { }
}
}
}
// Safe read wrapper to handle potential stream interruptions
private static int SafeRead (Stream s, byte [] buffer, int offset, int count)
{
try
{
return s.Read (buffer, offset, count);
}
catch
{
return 0;
}
}
// Build a user-friendly reason text from WebException (includes status / inner messages)
private static string BuildWebExceptionReason (WebException wex)
{
try
{
StringBuilder sb = new StringBuilder ();
sb.Append ("WebException: ");
sb.Append (wex.Status.ToString ());
if (wex.Response != null)
{
try
{
var resp = (HttpWebResponse)wex.Response;
sb.AppendFormat (CultureInfo.InvariantCulture, " (HTTP {0})", (int)resp.StatusCode);
}
catch { }
}
if (!string.IsNullOrEmpty (wex.Message))
{
sb.Append (" - ");
sb.Append (wex.Message);
}
return sb.ToString ();
}
catch
{
return wex.Message ?? "Unknown WebException";
}
}
// ---------- Reporting helpers (use Newtonsoft.Json) ----------
private void ReportProgress (long received, long total, long speed)
{
if (_cbProgress == null) return;
double progress = 0.0;
if (total > 0) progress = received / (double)total * 100.0;
var payload = new
{
received = received,
total = total,
speed = FormatSpeed (speed),
progress = progress
};
string json = JsonConvert.SerializeObject (payload);
CallJS (_cbProgress, json);
}
private void ReportComplete (string file, long size)
{
if (_cbComplete == null) return;
var payload = new
{
file = file ?? string.Empty,
status = "ok",
size = size
};
string json = JsonConvert.SerializeObject (payload);
CallJS (_cbComplete, json);
}
private void ReportError (string file, string reason)
{
if (_cbError == null) return;
var payload = new
{
file = file ?? string.Empty,
status = "failed",
reason = reason ?? string.Empty
};
string json = JsonConvert.SerializeObject (payload);
CallJS (_cbError, json);
}
// Call JS callback object: invoke its "call" method like original code: jsFunc.call(1, arg)
private void CallJS (object jsFunc, string arg)
{
if (jsFunc == null) return;
try
{
// Use reflection to invoke `call` method with (thisArg, arg)
jsFunc.GetType ().InvokeMember (
"call",
BindingFlags.InvokeMethod,
null,
jsFunc,
new object [] { 1, arg });
}
catch
{
// ignore errors in callback invocation
}
}
// Format speed like original: B/s, KB/s, MB/s, …
private string FormatSpeed (long speed)
{
if (speed < 0) return "--/s";
string [] units = new string [] { "B/s", "KB/s", "MB/s", "GB/s", "TB/s" };
double s = (double)speed;
int idx = 0;
while (s >= 1024.0 && idx < units.Length - 1)
{
s /= 1024.0;
idx++;
}
return string.Format (CultureInfo.InvariantCulture, "{0:0.##} {1}", s, units [idx]);
}
}
// Simple COM-visible wrapper class for JS/COM consumers
[ComVisible (true)]
[ClassInterface (ClassInterfaceType.AutoDual)]
public class _I_Download
{
// Keep method order and names similar to your C++/CLI _I_Download.WorkAsync
public void WorkAsync (string httpurl, string saveFilePath, object onComplete, object onError, object onProgress)
{
DownloadHelper.DownloadFile (httpurl, saveFilePath, onProgress, onComplete, onError);
}
}
}
+101
View File
@@ -0,0 +1,101 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace DataUtils
{
[ComVisible (true)]
[ClassInterface (ClassInterfaceType.AutoDual)]
public class _I_HResult
{
private int hr;
private string errorcode;
private string detailmsg;
public _I_HResult (int hres)
{
hr = hres;
errorcode = null;
detailmsg = HResultToMessage (hr) ?? string.Empty;
}
public _I_HResult (int hres, string error, string message)
{
hr = hres;
errorcode = error ?? string.Empty;
detailmsg = message ?? string.Empty;
}
// Properties (read-only as in your C++/CLI)
public int HResult
{
get { return hr; }
}
// If user provided an explicit error code string use it; otherwise return the hex form "0xXXXXXXXX"
public string ErrorCode
{
get
{
if (!string.IsNullOrEmpty (errorcode)) return errorcode;
return "0x" + hr.ToString ("X8", System.Globalization.CultureInfo.InvariantCulture);
}
}
public string Message
{
get { return detailmsg; }
}
public bool Succeeded
{
get { return hr >= 0; } // SUCCEEDED macro: hr >= 0
}
public bool Failed
{
get { return hr < 0; } // FAILED macro: hr < 0
}
public override string ToString ()
{
return string.Format (System.Globalization.CultureInfo.InvariantCulture,
"HResult={0}, ErrorCode={1}, Message={2}", hr, ErrorCode, Message);
}
// Try to obtain a user-friendly message for the HRESULT.
// First try Marshal.GetExceptionForHR, then fallback to FormatMessage.
private static string HResultToMessage (int hresult)
{
try
{
Exception ex = Marshal.GetExceptionForHR (hresult);
if (ex != null)
{
string msg = ex.Message;
if (!string.IsNullOrEmpty (msg)) return msg;
}
}
catch
{
}
string fmt = FormatMessageFromSystem (hresult);
if (!string.IsNullOrEmpty (fmt)) return fmt;
int win32Code = hresult & 0xFFFF;
fmt = FormatMessageFromSystem (win32Code);
if (!string.IsNullOrEmpty (fmt)) return fmt;
return string.Empty;
}
private static string FormatMessageFromSystem (int messageId)
{
const int FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100;
const int FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200;
const int FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;
int flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
StringBuilder sb = new StringBuilder (512);
int res = FormatMessage (flags, IntPtr.Zero, messageId, 0, sb, sb.Capacity, IntPtr.Zero);
if (res != 0)
{
// Trim trailing newlines that FormatMessage often appends
return sb.ToString ().TrimEnd ('\r', '\n', ' ');
}
return null;
}
[DllImport ("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern int FormatMessage (int dwFlags, IntPtr lpSource, int dwMessageId, int dwLanguageId, [Out] StringBuilder lpBuffer, int nSize, IntPtr Arguments);
}
}
+198
View File
@@ -0,0 +1,198 @@
using System;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32;
using Newtonsoft.Json;
namespace DataUtils
{
public static class BrowserEmulation
{
// The emulation value used in original C++ (11001).
// 11001 typically corresponds to IE11 in edge mode for most hosts.
private const int EmulationValue = 11001;
/// <summary>
/// Set the browser emulation value for the current user and current executable.
/// Writes to HKCU\Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION.
/// Creates the key if it does not exist.
/// </summary>
public static void SetWebBrowserEmulation ()
{
try
{
string exeName = GetCurrentProcessFileName ();
if (string.IsNullOrEmpty (exeName)) return;
// Registry path under HKCU (per-user setting)
const string subKey = @"Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION";
using (RegistryKey key = Registry.CurrentUser.CreateSubKey (subKey, RegistryKeyPermissionCheck.ReadWriteSubTree))
{
if (key == null) return;
// set DWORD value
key.SetValue (exeName, EmulationValue, RegistryValueKind.DWord);
}
}
catch
{
// ignore exceptions to match original "best-effort" behavior
}
}
/// <summary>
/// 获取系统安装的 Internet Explorer 主版本号(如 8、9、10、11)。
/// 会尝试从 64-bit registry view 和 32-bit registry view 读取(HKLM\SOFTWARE\Microsoft\Internet Explorer)。
/// 返回 0 表示未能读取到版本信息。
/// </summary>
public static int GetInternetExplorerVersionMajor ()
{
const string ieKey = @"SOFTWARE\Microsoft\Internet Explorer";
string versionStr = null;
// Try RegistryView.Registry64 then Registry32 for robustness on WOW64 systems
foreach (RegistryView view in new [] { RegistryView.Registry64, RegistryView.Registry32 })
{
try
{
using (RegistryKey baseKey = RegistryKey.OpenBaseKey (RegistryHive.LocalMachine, view))
using (RegistryKey key = baseKey.OpenSubKey (ieKey))
{
if (key == null) continue;
object svcVersion = key.GetValue ("svcVersion");
if (svcVersion is string)
{
versionStr = (string)svcVersion;
}
else
{
object ver = key.GetValue ("Version");
if (ver is string) versionStr = (string)ver;
}
}
}
catch
{
// ignore and continue to next view
}
if (!string.IsNullOrEmpty (versionStr)) break;
}
if (string.IsNullOrEmpty (versionStr)) return 0;
// parse major number before first non-digit/dot
int major = 0;
try
{
// common version formats: "11.0.9600.16428" etc.
string [] parts = versionStr.Split (new char [] { '.' }, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length > 0 && int.TryParse (parts [0], out major))
{
return major;
}
// fallback: try to read first integer found
string num = "";
foreach (char c in versionStr)
{
if (char.IsDigit (c)) num += c;
else break;
}
if (!string.IsNullOrEmpty (num) && int.TryParse (num, out major)) return major;
}
catch
{
// ignore parsing errors
}
return 0;
}
public static int IEVersionMajor => GetInternetExplorerVersionMajor ();
public static bool IsInternetExplorer10 ()
{
return GetInternetExplorerVersionMajor () == 10;
}
public static bool IE10 => IsInternetExplorer10 ();
public static bool IsInternetExplorer11AndLater ()
{
return GetInternetExplorerVersionMajor () >= 11;
}
public static bool IE11 => IsInternetExplorer11AndLater ();
// Helper: get file name of current process executable, e.g. "myapp.exe"
private static string GetCurrentProcessFileName ()
{
try
{
string path = null;
try
{
// prefer process main module (may throw in some restricted environments)
path = Process.GetCurrentProcess ().MainModule.FileName;
}
catch
{
try
{
var asm = System.Reflection.Assembly.GetEntryAssembly ();
if (asm != null) path = asm.Location;
}
catch { }
}
if (string.IsNullOrEmpty (path))
{
path = AppDomain.CurrentDomain.FriendlyName;
}
if (string.IsNullOrEmpty (path)) return string.Empty;
return Path.GetFileName (path);
}
catch
{
return string.Empty;
}
}
}
[ComVisible (true)]
public interface IScriptBridge
{
/// <summary>
/// 调用程序窗口实例中的事件
/// </summary>
/// <param name="funcName">函数名</param>
/// <param name="e">事件参数</param>
/// <returns>调用结果</returns>
object CallEvent (string funcName, object e);
}
[ComVisible (true)]
[ClassInterface (ClassInterfaceType.AutoDual)]
public class _I_IEFrame
{
public int Version => BrowserEmulation.IEVersionMajor;
public string ParseHtmlColor (string color)
{
try
{
Color dcolor = ColorTranslator.FromHtml (color);
var obj = new
{
r = (ushort)dcolor.R,
g = (ushort)dcolor.G,
b = (ushort)dcolor.B,
a = (ushort)dcolor.A
};
return JsonConvert.SerializeObject (obj);
}
catch
{
return "{}";
}
}
private IWebBrowserPageScale iwbps;
public _I_IEFrame (IWebBrowserPageScale _iwbps)
{
iwbps = _iwbps;
}
public int Scale
{
get { return iwbps.PageScale; }
set { iwbps.PageScale = value; }
}
}
}
+373
View File
@@ -0,0 +1,373 @@
using System;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Text;
namespace DataUtils
{
internal static class NativeMethods
{
// GetLocaleInfoW for LCID-based queries
[DllImport ("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern int GetLocaleInfoW (int Locale, int LCType, [Out] StringBuilder lpLCData, int cchData);
// GetLocaleInfoEx for locale name based queries
[DllImport ("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern int GetLocaleInfoEx (string lpLocaleName, int LCType, [Out] StringBuilder lpLCData, int cchData);
// LocaleNameToLCID - available on Vista+; fallback is to use CultureInfo
[DllImport ("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern int LocaleNameToLCID (string lpName, uint dwFlags);
// LCIDToLocaleName (Vista+)
[DllImport ("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern int LCIDToLocaleName (int Locale, [Out] StringBuilder lpName, int cchName, uint dwFlags);
}
[ComVisible (true)]
[ClassInterface (ClassInterfaceType.AutoDual)]
public class _I_Locale
{
// Current locale name like "en-US"
public string CurrentLocale
{
get
{
try
{
// prefer thread culture name which reflects user culture
string name = CultureInfo.CurrentCulture.Name;
if (string.IsNullOrEmpty (name))
name = CultureInfo.InstalledUICulture.Name;
return name ?? string.Empty;
}
catch
{
return string.Empty;
}
}
}
// Current LCID (int)
public int CurrentLCID
{
get
{
try
{
return CultureInfo.CurrentCulture.LCID;
}
catch
{
return CultureInfo.InvariantCulture.LCID;
}
}
}
// Convert LCID -> locale name (e.g. 1033 -> "en-US")
public string ToLocaleName (int lcid)
{
try
{
// Try managed first
var ci = new CultureInfo (lcid);
if (!string.IsNullOrEmpty (ci.Name))
return ci.Name;
}
catch
{
// try Win32 LCIDToLocaleName (Vista+)
try
{
StringBuilder sb = new StringBuilder (LOCALE_NAME_MAX_LENGTH);
int res = NativeMethods.LCIDToLocaleName (lcid, sb, sb.Capacity, 0);
if (res > 0) return sb.ToString ();
}
catch { }
}
return string.Empty;
}
// Convert locale name -> LCID
public int ToLCID (string localeName)
{
if (string.IsNullOrEmpty (localeName)) return CultureInfo.InvariantCulture.LCID;
try
{
// prefer managed creation
var ci = new CultureInfo (localeName);
return ci.LCID;
}
catch
{
// try Win32 LocaleNameToLCID (Vista+)
try
{
int lcid = NativeMethods.LocaleNameToLCID (localeName, 0);
if (lcid != 0) return lcid;
}
catch { }
}
// fallback: invariant culture
return CultureInfo.InvariantCulture.LCID;
}
// Return a locale info string for given LCID and LCTYPE. LCTYPE is the Win32 LOCALE_* constant.
// Returns a string (or empty string on failure).
public object LocaleInfo (int lcid, int lctype)
{
try
{
// First try mapping common LCTYPE values to managed properties for better correctness
// Some common LCTYPE values:
// LOCALE_SISO639LANGNAME = 0x59 (89) -> Two-letter ISO language name
// LOCALE_SISO3166CTRYNAME = 0x5A (90) -> Two-letter country/region name
// LOCALE_SNAME = 0x5c (92) -> locale name like "en-US" (Vista+)
// But we cannot rely on all values, so we fallback to native GetLocaleInfoW.
if (lctype == 0x59) // LOCALE_SISO639LANGNAME
{
try
{
var ci = new CultureInfo (lcid);
return ci.TwoLetterISOLanguageName ?? string.Empty;
}
catch { }
}
else if (lctype == 0x5A) // LOCALE_SISO3166CTRYNAME
{
try
{
var ci = new CultureInfo (lcid);
try
{
var ri = new RegionInfo (ci.Name);
return ri.TwoLetterISORegionName ?? string.Empty;
}
catch
{
// some cultures have no region; fallback to parsing name
var name = ci.Name;
if (!string.IsNullOrEmpty (name) && name.IndexOf ('-') >= 0)
{
return name.Split ('-') [1];
}
}
}
catch { }
}
else if (lctype == 0x5c) // LOCALE_SNAME
{
try
{
var ci = new CultureInfo (lcid);
return ci.Name ?? string.Empty;
}
catch { }
}
// Fallback to native GetLocaleInfoW
StringBuilder sb = new StringBuilder (256);
int ret = NativeMethods.GetLocaleInfoW (lcid, lctype, sb, sb.Capacity);
if (ret > 0)
return sb.ToString ();
return string.Empty;
}
catch
{
return string.Empty;
}
}
// LocaleInfoEx: query by locale name string and LCTYPE
// Returns string if available; otherwise returns the integer result code (as int) if string empty (mimic C++ behavior).
public object LocaleInfoEx (string localeName, int lctype)
{
if (string.IsNullOrEmpty (localeName))
{
// fall back to current culture name
localeName = CurrentLocale;
if (string.IsNullOrEmpty (localeName)) return 0;
}
try
{
// Try managed shortcuts for common types
if (lctype == 0x59) // LOCALE_SISO639LANGNAME
{
try
{
var ci = new CultureInfo (localeName);
return ci.TwoLetterISOLanguageName ?? string.Empty;
}
catch { }
}
else if (lctype == 0x5A) // LOCALE_SISO3166CTRYNAME
{
try
{
var ci = new CultureInfo (localeName);
var ri = new RegionInfo (ci.Name);
return ri.TwoLetterISORegionName ?? string.Empty;
}
catch
{
// try to split
var parts = localeName.Split (new char [] { '-', '_' }, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length >= 2) return parts [1];
}
}
else if (lctype == 0x5c) // LOCALE_SNAME
{
// localeName is probably already the name
return localeName;
}
// Fallback to GetLocaleInfoEx
StringBuilder sb = new StringBuilder (LOCALE_NAME_MAX_LENGTH);
int res = NativeMethods.GetLocaleInfoEx (localeName, lctype, sb, sb.Capacity);
if (res > 0)
{
string outStr = sb.ToString ();
if (!string.IsNullOrEmpty (outStr))
return outStr;
}
// if nothing returned, return the result code
return res;
}
catch
{
return 0;
}
}
// Helpers similar to the C++: restricted (language) and elaborated (region) codes
public string GetLocaleRestrictedCode (string localeName)
{
if (string.IsNullOrEmpty (localeName)) localeName = CurrentLocale;
try
{
var ci = new CultureInfo (localeName);
return ci.TwoLetterISOLanguageName ?? string.Empty;
}
catch
{
// fallback: parse name
var parts = localeName.Split (new char [] { '-', '_' }, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length >= 1) return parts [0];
return string.Empty;
}
}
public string GetLocaleElaboratedCode (string localeName)
{
if (string.IsNullOrEmpty (localeName)) localeName = CurrentLocale;
try
{
var ci = new CultureInfo (localeName);
// Region part from RegionInfo
try
{
var ri = new RegionInfo (ci.Name);
return ri.TwoLetterISORegionName ?? string.Empty;
}
catch
{
// fallback: parse
var parts = localeName.Split (new char [] { '-', '_' }, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length >= 2) return parts [1];
}
}
catch
{
var parts = localeName.Split (new char [] { '-', '_' }, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length >= 2) return parts [1];
}
return string.Empty;
}
// LCID -> combined code like "en-US" (with configurable separator)
public string LcidToLocaleCode (int lcid)
{
try
{
var ci = new CultureInfo (lcid);
if (!string.IsNullOrEmpty (ci.Name)) return ci.Name;
}
catch
{
try
{
var name = ToLocaleName (lcid);
if (!string.IsNullOrEmpty (name)) return name;
}
catch { }
}
return string.Empty;
}
// Get the user default locale name
public string GetUserDefaultLocaleName ()
{
try
{
// In .NET, CurrentCulture corresponds to user default
string name = CultureInfo.CurrentCulture.Name;
if (!string.IsNullOrEmpty (name)) return name;
}
catch { }
return LcidToLocaleCode (CultureInfo.CurrentCulture.LCID);
}
// Get system default locale name (machine)
public string GetSystemDefaultLocaleName ()
{
try
{
// InstalledUICulture / Invariant fallback
string name = CultureInfo.InstalledUICulture.Name;
if (!string.IsNullOrEmpty (name)) return name;
}
catch { }
return LcidToLocaleCode (CultureInfo.InstalledUICulture.LCID);
}
// Get computer locale code similar to C++ approach
public string GetComputerLocaleCode ()
{
try
{
// Thread culture -> user -> system
string threadName = System.Threading.Thread.CurrentThread.CurrentCulture.Name;
if (!string.IsNullOrEmpty (threadName)) return threadName;
string user = GetUserDefaultLocaleName ();
if (!string.IsNullOrEmpty (user)) return user;
string system = GetSystemDefaultLocaleName ();
if (!string.IsNullOrEmpty (system)) return system;
}
catch { }
// fallback to invariant
return CultureInfo.InvariantCulture.Name ?? string.Empty;
}
// Compare two locale names; returns true if equal by name or LCID
public bool LocaleNameCompare (string left, string right)
{
if (string.Equals (left, right, StringComparison.OrdinalIgnoreCase)) return true;
try
{
int l = ToLCID (left);
int r = ToLCID (right);
return l == r && l != CultureInfo.InvariantCulture.LCID;
}
catch
{
return false;
}
}
// Constants
private const int LOCALE_NAME_MAX_LENGTH = 85; // defined by Windows
}
}
+201
View File
@@ -0,0 +1,201 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.IO;
using Newtonsoft.Json;
using System.Windows.Forms;
using System.Threading;
namespace DataUtils
{
public static class ProcessHelper
{
public static int ExecuteProgram (
string cmdline,
string file,
int wndShowMode,
bool wait,
string execDir = "")
{
try
{
var psi = new ProcessStartInfo
{
FileName = string.IsNullOrEmpty (file) ? cmdline : file,
Arguments = string.IsNullOrEmpty (file) ? "" : cmdline,
WorkingDirectory = string.IsNullOrEmpty (execDir) ? null : execDir,
UseShellExecute = false,
CreateNoWindow = false,
WindowStyle = (ProcessWindowStyle)wndShowMode
};
using (var p = Process.Start (psi))
{
if (wait && p != null)
p.WaitForExit ();
}
return 0;
}
catch (Exception ex)
{
return Marshal.GetHRForException (ex);
}
}
public static bool KillProcessByFilePath (
string filepath,
bool multiple = false,
bool isOnlyName = false)
{
if (string.IsNullOrEmpty (filepath)) return false;
bool killed = false;
string targetName = isOnlyName ? Path.GetFileName (filepath) : null;
foreach (var p in Process.GetProcesses ())
{
try
{
bool match = false;
if (isOnlyName)
{
match = string.Equals (p.ProcessName + ".exe", targetName,
StringComparison.OrdinalIgnoreCase);
}
else
{
string fullPath = p.MainModule.FileName;
match = string.Equals (fullPath, filepath,
StringComparison.OrdinalIgnoreCase);
}
if (match)
{
p.Kill ();
killed = true;
if (!multiple) break;
}
}
catch { }
}
return killed;
}
public static string GetFileVersionAsJson (string filePath)
{
try
{
var info = FileVersionInfo.GetVersionInfo (filePath);
var obj = new
{
info.CompanyName,
info.FileDescription,
info.FileVersion,
info.InternalName,
info.OriginalFilename,
info.ProductName,
info.ProductVersion,
info.LegalCopyright,
FileVersionRaw = info.FileMajorPart + "." +
info.FileMinorPart + "." +
info.FileBuildPart + "." +
info.FilePrivatePart
};
return JsonConvert.SerializeObject (obj);
}
catch (Exception ex)
{
return JsonConvert.SerializeObject (new { error = ex.Message });
}
}
public static int ExploreSaveFile (
IWin32Window owner,
List<string> results,
string filter = "Windows Store App Package (*.appx; *.appxbundle)|*.appx;*.appxbundle",
string defExt = "appx",
string title = "Please select the file to save:",
string initDir = null)
{
results.Clear ();
using (var dlg = new SaveFileDialog ())
{
dlg.Filter = filter;
dlg.DefaultExt = defExt;
dlg.Title = title;
dlg.InitialDirectory = initDir;
if (dlg.ShowDialog (owner) == DialogResult.OK)
{
results.Add (dlg.FileName);
}
}
return results.Count;
}
}
[ComVisible (true)]
[ClassInterface (ClassInterfaceType.AutoDual)]
public class _I_Process
{
public int Run (
string cmdline,
string filepath,
int wndtype,
bool wait,
string runpath)
{
return ProcessHelper.ExecuteProgram (cmdline, filepath, wndtype, wait, runpath);
}
public void RunAsync (
string cmdline,
string filepath,
int wndtype,
string runpath,
object callback)
{
var worker = new ProcessWorker (this)
{
CmdLine = cmdline,
FilePath = filepath,
WndType = wndtype,
RunPath = runpath,
Callback = callback
};
var th = new Thread (worker.Work)
{
IsBackground = true
};
th.Start ();
}
public bool Kill (string filename, bool allproc, bool onlyname)
{
return ProcessHelper.KillProcessByFilePath (filename, allproc, onlyname);
}
private class ProcessWorker
{
private readonly _I_Process parent;
public string CmdLine;
public string FilePath;
public int WndType;
public string RunPath;
public object Callback;
public ProcessWorker (_I_Process parent)
{
this.parent = parent;
}
public void Work ()
{
int ret = parent.Run (CmdLine, FilePath, WndType, true, RunPath);
if (Callback != null)
{
try
{
Callback.GetType ().InvokeMember (
"call",
System.Reflection.BindingFlags.InvokeMethod,
null,
Callback,
new object [] { 1, ret }
);
}
catch { }
}
}
}
}
}
+36
View File
@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("DataUtils")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("DataUtils")]
[assembly: AssemblyCopyright("Copyright © 2026")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
//将 ComVisible 设置为 false 将使此程序集中的类型
//对 COM 组件不可见。 如果需要从 COM 访问此程序集中的类型,
//请将此类型的 ComVisible 特性设置为 true。
[assembly: ComVisible (true)]
// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
[assembly: Guid("ffd3fd52-37a8-4f43-883c-de8d996cb0e0")]
// 程序集的版本信息由下列四个值组成:
//
// 主版本
// 次版本
// 生成号
// 修订号
//
//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值,
// 方法是按如下所示使用“*”: :
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
+272
View File
@@ -0,0 +1,272 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
namespace DataUtils
{
public class FileResXmlDoc: IDisposable
{
private string _filepath;
private XmlDocument _doc;
private bool _available;
public FileResXmlDoc (string xmlpath)
{
Create (xmlpath);
}
public bool Create (string xmlpath)
{
Destroy ();
if (string.IsNullOrEmpty (xmlpath) || !File.Exists (xmlpath))
{
_available = false;
return false;
}
try
{
_doc = new XmlDocument ();
_doc.Load (xmlpath);
_filepath = xmlpath;
_available = true;
}
catch
{
_available = false;
}
return _available;
}
public void Destroy ()
{
if (!_available) return;
_doc = null;
_available = false;
_filepath = null;
}
public void Dispose ()
{
Destroy ();
}
// Returns the chosen absolute (or resolved) path string or empty if none found
public string Get (string id)
{
if (!_available || _doc == null) return string.Empty;
if (string.IsNullOrEmpty (id)) return string.Empty;
XmlNode root = _doc.DocumentElement;
if (root == null) return string.Empty;
foreach (XmlNode node in root.ChildNodes)
{
var attr = node.Attributes? ["id"];
if (attr == null) continue;
if (!string.Equals (attr.Value, id, StringComparison.OrdinalIgnoreCase)) continue;
// Build dpi -> path map
var map = new Dictionary<int, string> ();
foreach (XmlNode child in node.ChildNodes)
{
var dpiAttr = child.Attributes? ["dpi"];
string dpiStr = dpiAttr != null ? dpiAttr.Value : "default";
string text = child.InnerText ?? string.Empty;
int dpiKey = 0;
if (string.Equals (dpiStr, "default", StringComparison.OrdinalIgnoreCase))
{
dpiKey = 0;
}
else
{
int parsed;
if (int.TryParse (dpiStr, out parsed)) dpiKey = parsed;
else dpiKey = 0;
}
// Resolve relative path relative to xml file directory
string candidate = text;
if (!Path.IsPathRooted (candidate) && !string.IsNullOrEmpty (_filepath))
{
string dir = Path.GetDirectoryName (_filepath);
if (!string.IsNullOrEmpty (dir))
candidate = Path.Combine (dir, candidate);
}
// Normalize
try { candidate = Path.GetFullPath (candidate); } catch { /* ignore */ }
// Insert/overwrite by key (keep last if duplicate)
map [dpiKey] = candidate;
}
// Convert map to list and sort by string value (to mimic original)
var list = map.Select (kv => new KeyValuePair<int, string> (kv.Key, kv.Value)).ToList ();
list.Sort ((a, b) => string.CompareOrdinal (a.Value, b.Value));
// Keep only those whose file exists
var existList = new List<KeyValuePair<int, string>> ();
foreach (var kv in list)
{
if (!string.IsNullOrEmpty (kv.Value) && File.Exists (kv.Value))
existList.Add (kv);
}
int dpiPercent = UITheme.GetDPI (); // uses earlier helper
// Find first with dpi >= dpiPercent
foreach (var kv in existList)
{
if (kv.Key >= dpiPercent) return kv.Value;
}
// otherwise return first existing candidate
if (existList.Count > 0) return existList [0].Value;
return string.Empty;
}
return string.Empty;
}
// Overloads for convenience
//public string Get (System.String idAsString) { return Get (idAsString); }
}
public class StringResXmlDoc: IDisposable
{
private XmlDocument doc;
private bool isValid;
public bool IsValid
{
get { return isValid; }
}
public StringResXmlDoc () { }
public StringResXmlDoc (string filePath)
{
Create (filePath);
}
public bool Create (string filePath)
{
Destroy ();
if (string.IsNullOrEmpty (filePath) || !File.Exists (filePath))
return false;
try
{
doc = new XmlDocument ();
doc.Load (filePath);
isValid = true;
return true;
}
catch
{
Destroy ();
return false;
}
}
public void Destroy ()
{
doc = null;
isValid = false;
}
public void Dispose ()
{
Destroy ();
GC.SuppressFinalize (this);
}
public string Get (string id)
{
if (!isValid || doc == null || string.IsNullOrEmpty (id))
return string.Empty;
XmlElement root = doc.DocumentElement;
if (root == null)
return string.Empty;
foreach (XmlNode node in root.ChildNodes)
{
if (node.Attributes == null)
continue;
XmlAttribute idAttr = node.Attributes ["id"];
if (idAttr == null)
continue;
if (!StringEqualsNormalized (idAttr.Value, id))
continue;
Dictionary<string, string> langValues =
new Dictionary<string, string> (StringComparer.OrdinalIgnoreCase);
foreach (XmlNode sub in node.ChildNodes)
{
if (sub.Attributes == null)
continue;
XmlAttribute langAttr = sub.Attributes ["name"];
if (langAttr == null)
continue;
string lang = langAttr.Value;
if (!string.IsNullOrEmpty (lang))
{
langValues [lang] = sub.InnerText;
}
}
return GetSuitableLanguageValue (langValues);
}
return string.Empty;
}
public string this [string id]
{
get { return Get (id); }
}
private static bool StringEqualsNormalized (string a, string b)
{
return string.Equals (
a != null ? a.Trim () : null,
b != null ? b.Trim () : null,
StringComparison.OrdinalIgnoreCase
);
}
private static string GetSuitableLanguageValue (
Dictionary<string, string> values)
{
if (values == null || values.Count == 0)
return string.Empty;
CultureInfo culture = CultureInfo.CurrentUICulture;
string val;
if (values.TryGetValue (culture.Name, out val))
return val;
if (values.TryGetValue (culture.TwoLetterISOLanguageName, out val))
return val;
if (values.TryGetValue ("en", out val))
return val;
foreach (string v in values.Values)
return v;
return string.Empty;
}
}
}
+84
View File
@@ -0,0 +1,84 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace DataUtils
{
[ComVisible (true)]
[ClassInterface (ClassInterfaceType.AutoDual)]
public class _I_Resources
{
// Flags for LoadLibraryEx
private const uint LOAD_LIBRARY_AS_DATAFILE = 0x00000002;
private const uint LOAD_LIBRARY_AS_IMAGE_RESOURCE = 0x00000020;
[DllImport ("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern IntPtr LoadLibraryEx (string lpFileName, IntPtr hFile, uint dwFlags);
[DllImport ("kernel32.dll", SetLastError = true)]
[return: MarshalAs (UnmanagedType.Bool)]
private static extern bool FreeLibrary (IntPtr hModule);
[DllImport ("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern IntPtr GetModuleHandle (string lpModuleName);
[DllImport ("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern int LoadString (IntPtr hInstance, uint uID, StringBuilder lpBuffer, int nBufferMax);
/// <summary>
/// Load a string resource from another module (DLL/EXE) identified by file path, or from the current module if filepath is null/empty.
/// </summary>
/// <param name="filepath">Path to the module file. If null/empty, use current process module.</param>
/// <param name="resid">Resource ID to load.</param>
/// <returns>The resource string, or empty string if not found or on error.</returns>
public string GetFromOthers (string filepath, uint resid)
{
IntPtr hModule = IntPtr.Zero;
bool needFree = false;
try
{
if (!string.IsNullOrWhiteSpace (filepath))
{
// Load as datafile + image resource so we can access resources without executing DllMain of the module.
hModule = LoadLibraryEx (filepath, IntPtr.Zero, LOAD_LIBRARY_AS_DATAFILE | LOAD_LIBRARY_AS_IMAGE_RESOURCE);
if (hModule == IntPtr.Zero)
{
// Failed to load; return empty string
return string.Empty;
}
needFree = true;
}
else
{
// Get handle of current process module (exe)
hModule = GetModuleHandle (null);
if (hModule == IntPtr.Zero)
return string.Empty;
}
// Prepare buffer. Typical string resources are not huge; 4096 should be enough.
const int BUFFER_SIZE = 4096;
StringBuilder sb = new StringBuilder (BUFFER_SIZE);
int copied = LoadString (hModule, resid, sb, sb.Capacity);
if (copied > 0)
{
// LoadString returns number of characters copied (excluding terminating null)
return sb.ToString (0, copied);
}
return string.Empty;
}
finally
{
if (needFree && hModule != IntPtr.Zero)
{
try { FreeLibrary (hModule); }
catch { /* ignore */ }
}
}
}
}
}
+519
View File
@@ -0,0 +1,519 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
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);
}
}
// 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_Storage
{
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); }
}
// 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);
}
}
}
}
+115
View File
@@ -0,0 +1,115 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace DataUtils
{
[ComVisible (true)]
[ClassInterface (ClassInterfaceType.AutoDual)]
public class _I_String
{
// Nested normalization helper
[ComVisible (true)]
public class _I_NString
{
// Normalize strings: Trim, apply Unicode normalization form KD, and to-lower invariant.
private static string Normalize (string s)
{
if (s == null) return string.Empty;
s = s.Trim ();
return s.ToLowerInvariant ();
}
// Compare normalized equality
public bool NEquals (string l, string r)
{
return string.Equals (Normalize (l), Normalize (r), StringComparison.Ordinal);
}
// Is normalized empty
public bool Empty (string l)
{
string n = Normalize (l);
return string.IsNullOrEmpty (n);
}
// Compare normalized strings with ordinal comparison (returns -1/0/1)
public int Compare (string l, string r)
{
string nl = Normalize (l);
string nr = Normalize (r);
return string.CompareOrdinal (nl, nr);
}
// Get length of normalized string (in characters)
public int Length (string l)
{
string nl = Normalize (l);
return nl.Length;
}
}
private _I_NString nstr = new _I_NString ();
public _I_NString NString { get { return nstr; } }
public string Trim (string src)
{
if (src == null) return string.Empty;
return src.Trim ();
}
public string ToLower (string src)
{
if (src == null) return null;
return src.ToLower (CultureInfo.InvariantCulture);
}
public string ToUpper (string src)
{
if (src == null) return null;
return src.ToUpper (CultureInfo.InvariantCulture);
}
public string Format (string fmt, params object [] args)
{
return Utilities.FormatString (fmt, args);
}
// FormatInnerHTML: escape format string to inner xml, and escape each argument to inner xml wrapped in <span>...</span>
public string FormatInnerHTML (string fmt, params object [] args)
{
if (fmt == null) fmt = string.Empty;
string escapedFormat = Utilities.EscapeToInnerXml (fmt);
if (args == null || args.Length == 0) return escapedFormat;
object [] newArgs = new object [args.Length];
for (int i = 0; i < args.Length; i++)
{
string argText = args [i] != null ? args [i].ToString () : string.Empty;
string esc = Utilities.EscapeToInnerXml (argText);
newArgs [i] = string.Format ("<span>{0}</span>", esc);
}
try
{
// We used an escaped format string, but its indices are the same.
return string.Format (CultureInfo.InvariantCulture, escapedFormat, newArgs);
}
catch
{
// fallback: simple concatenation
StringBuilder sb = new StringBuilder ();
sb.Append (escapedFormat);
sb.Append (" ");
for (int i = 0; i < newArgs.Length; i++)
{
if (i > 0) sb.Append (", ");
sb.Append (newArgs [i]);
}
return sb.ToString ();
}
}
public string StringArrayToJson (string [] strs)
{
return Utilities.StringArrayToJson (strs);
}
public static string FormatDateTime (string fmt, string jsDate)
{
DateTime dt = Convert.ToDateTime (jsDate);
return string.Format (fmt, dt);
}
}
}
+274
View File
@@ -0,0 +1,274 @@
using System;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
namespace DataUtils
{
internal static class VisualElementsStore
{
// Publicly accessible instances for internal use
public static readonly VisualElementManifest Vemanifest;
public static readonly FileResXmlDoc ScaleResources;
static VisualElementsStore ()
{
try
{
string programRoot = GetProgramRootDirectory ();
// manifest path: VisualElementsManifest.xml in program root
string manifestPath = Path.Combine (programRoot, "VisualElementsManifest.xml");
// scale xml candidate: VisualElements\scale.xml
string scaleCandidate = Path.Combine (programRoot, "VisualElements", "scale.xml");
// If scale.xml exists use it, otherwise fall back to VisualElementsManifest.xml
string scalePath = File.Exists (scaleCandidate) ? scaleCandidate : manifestPath;
// Initialize (constructors will attempt to load)
Vemanifest = new VisualElementManifest ();
if (File.Exists (manifestPath))
{
// Use Create to ensure we reflect true load status
Vemanifest.Create (manifestPath);
}
FileResXmlDoc tmp = null;
if (File.Exists (scalePath))
{
tmp = new FileResXmlDoc (scalePath);
}
else
{
// if both missing, try manifest as last resort (ResXmlDoc handles manifest-style layout)
if (File.Exists (manifestPath))
tmp = new FileResXmlDoc (manifestPath);
}
ScaleResources = tmp;
}
catch
{
// swallow exceptions; leave fields null if initialization fails
Vemanifest = new VisualElementManifest ();
ScaleResources = null;
}
}
private static string GetProgramRootDirectory ()
{
try
{
// Prefer the directory of the executing assembly
string codeBase = Assembly.GetExecutingAssembly ().Location;
if (!string.IsNullOrEmpty (codeBase))
{
string dir = Path.GetDirectoryName (codeBase);
if (!string.IsNullOrEmpty (dir)) return dir;
}
}
catch { }
try
{
return AppDomain.CurrentDomain.BaseDirectory ?? Environment.CurrentDirectory;
}
catch { }
return Environment.CurrentDirectory;
}
}
[ComVisible (true)]
[ClassInterface (ClassInterfaceType.AutoDual)]
public class _I_VisualElement
{
private string _appid;
public _I_VisualElement ()
{
_appid = "App";
}
public _I_VisualElement (string appid)
{
_appid = string.IsNullOrEmpty (appid) ? "App" : appid;
}
public string Id
{
get { return _appid; }
set { _appid = string.IsNullOrEmpty (value) ? "App" : value; }
}
public string DisplayName
{
get { return VisualElementsStore.Vemanifest != null ? VisualElementsStore.Vemanifest.DisplayName (_appid) : string.Empty; }
}
public string Logo
{
get { return VisualElementsStore.Vemanifest != null ? VisualElementsStore.Vemanifest.Logo (_appid) : string.Empty; }
}
public string SmallLogo
{
get { return VisualElementsStore.Vemanifest != null ? VisualElementsStore.Vemanifest.SmallLogo (_appid) : string.Empty; }
}
public string ForegroundText
{
get
{
if (VisualElementsStore.Vemanifest == null) return "dark";
var t = VisualElementsStore.Vemanifest.ForegroundText (_appid);
return t == ManifestTextColor.Light ? "light" : "dark";
}
}
public string Lnk32x32Logo
{
get { return VisualElementsStore.Vemanifest != null ? VisualElementsStore.Vemanifest.Lnk32x32Logo (_appid) : string.Empty; }
}
public string ItemDisplayLogo
{
get { return VisualElementsStore.Vemanifest != null ? VisualElementsStore.Vemanifest.ItemDisplayLogo (_appid) : string.Empty; }
}
public bool ShowNameOnTile
{
get { return VisualElementsStore.Vemanifest != null ? VisualElementsStore.Vemanifest.ShowNameOnTile (_appid) : false; }
}
public string BackgroundColor
{
get { return VisualElementsStore.Vemanifest != null ? VisualElementsStore.Vemanifest.BackgroundColor (_appid) : string.Empty; }
}
public string SplashScreenImage
{
get { return VisualElementsStore.Vemanifest != null ? VisualElementsStore.Vemanifest.SplashScreenImage (_appid) : string.Empty; }
}
public string SplashScreenBackgroundColor
{
get { return VisualElementsStore.Vemanifest != null ? VisualElementsStore.Vemanifest.SplashScreenBackgroundColor (_appid) : string.Empty; }
}
public string SplashScreenBackgroundColorDarkMode
{
get { return VisualElementsStore.Vemanifest != null ? VisualElementsStore.Vemanifest.SplashScreenBackgroundColorDarkMode (_appid) : string.Empty; }
}
// Indexer for script-friendly access: v["displayname"]
public object this [string propertyName]
{
get { return Get (propertyName); }
}
// Generic getter by property name (case-insensitive)
public object Get (string propertyName)
{
if (string.IsNullOrEmpty (propertyName)) return string.Empty;
string key = propertyName.Trim ().ToLowerInvariant ();
switch (key)
{
case "displayname": return DisplayName;
case "logo": return Logo;
case "smalllogo": return SmallLogo;
case "foregroundtext": return ForegroundText;
case "lnk32x32logo": return Lnk32x32Logo;
case "itemdisplaylogo": return ItemDisplayLogo;
case "shownameontile": return ShowNameOnTile;
case "backgroundcolor": return BackgroundColor;
case "splashscreenimage": return SplashScreenImage;
case "splashscreenbackgroundcolor": return SplashScreenBackgroundColor;
case "splashscreenbackgroundcolordarkmode": return SplashScreenBackgroundColorDarkMode;
default: return string.Empty;
}
}
}
[ComVisible (true)]
[ClassInterface (ClassInterfaceType.AutoDual)]
public class _I_VisualElements
{
public string [] GetIds ()
{
try
{
var list = VisualElementsStore.Vemanifest != null ? VisualElementsStore.Vemanifest.AppIds () : new System.Collections.Generic.List<string> () { "App" };
return list.ToArray ();
}
catch
{
return new string [] { "App" };
}
}
public string GetIdsToJson ()
{
var ids = GetIds ();
// Use Utilities.StringArrayToJson (which uses Newtonsoft.Json if available)
try
{
return Utilities.StringArrayToJson (ids);
}
catch
{
// fallback
return Newtonsoft.Json.JsonConvert.SerializeObject (ids);
}
}
public _I_VisualElement Get (string id)
{
return new _I_VisualElement (id);
}
public _I_VisualElement this [string id]
{
get { return Get (id); }
}
// Attribute-style methods
public string DisplayName (string appid) { return VisualElementsStore.Vemanifest != null ? VisualElementsStore.Vemanifest.DisplayName (string.IsNullOrEmpty (appid) ? "App" : appid) : string.Empty; }
public string Logo (string appid) { return VisualElementsStore.Vemanifest != null ? VisualElementsStore.Vemanifest.Logo (string.IsNullOrEmpty (appid) ? "App" : appid) : string.Empty; }
public string SmallLogo (string appid) { return VisualElementsStore.Vemanifest != null ? VisualElementsStore.Vemanifest.SmallLogo (string.IsNullOrEmpty (appid) ? "App" : appid) : string.Empty; }
public string ForegroundText (string appid)
{
if (VisualElementsStore.Vemanifest == null) return "dark";
var t = VisualElementsStore.Vemanifest.ForegroundText (string.IsNullOrEmpty (appid) ? "App" : appid);
return t == ManifestTextColor.Light ? "light" : "dark";
}
public string Lnk32x32Logo (string appid) { return VisualElementsStore.Vemanifest != null ? VisualElementsStore.Vemanifest.Lnk32x32Logo (string.IsNullOrEmpty (appid) ? "App" : appid) : string.Empty; }
public string ItemDisplayLogo (string appid) { return VisualElementsStore.Vemanifest != null ? VisualElementsStore.Vemanifest.ItemDisplayLogo (string.IsNullOrEmpty (appid) ? "App" : appid) : string.Empty; }
public bool ShowNameOnTile (string appid) { return VisualElementsStore.Vemanifest != null ? VisualElementsStore.Vemanifest.ShowNameOnTile (string.IsNullOrEmpty (appid) ? "App" : appid) : false; }
public string BackgroundColor (string appid) { return VisualElementsStore.Vemanifest != null ? VisualElementsStore.Vemanifest.BackgroundColor (string.IsNullOrEmpty (appid) ? "App" : appid) : string.Empty; }
public string SplashScreenImage (string appid) { return VisualElementsStore.Vemanifest != null ? VisualElementsStore.Vemanifest.SplashScreenImage (string.IsNullOrEmpty (appid) ? "App" : appid) : string.Empty; }
public string SplashScreenBackgroundColor (string appid) { return VisualElementsStore.Vemanifest != null ? VisualElementsStore.Vemanifest.SplashScreenBackgroundColor (string.IsNullOrEmpty (appid) ? "App" : appid) : string.Empty; }
public string SplashScreenBackgroundColorDarkMode (string appid) { return VisualElementsStore.Vemanifest != null ? VisualElementsStore.Vemanifest.SplashScreenBackgroundColorDarkMode (string.IsNullOrEmpty (appid) ? "App" : appid) : string.Empty; }
// Generic getter by attribute name
public object GetValue (string appid, string attributeName)
{
if (string.IsNullOrEmpty (attributeName)) return string.Empty;
string key = attributeName.Trim ().ToLowerInvariant ();
switch (key)
{
case "displayname": return DisplayName (appid);
case "logo": return Logo (appid);
case "smalllogo": return SmallLogo (appid);
case "foregroundtext": return ForegroundText (appid);
case "lnk32x32logo": return Lnk32x32Logo (appid);
case "itemdisplaylogo": return ItemDisplayLogo (appid);
case "shownameontile": return ShowNameOnTile (appid);
case "backgroundcolor": return BackgroundColor (appid);
case "splashscreenimage": return SplashScreenImage (appid);
case "splashscreenbackgroundcolor": return SplashScreenBackgroundColor (appid);
case "splashscreenbackgroundcolordarkmode": return SplashScreenBackgroundColorDarkMode (appid);
default: return string.Empty;
}
}
}
}
+80
View File
@@ -0,0 +1,80 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace DataUtils
{
public enum TBPFLAG
{
TBPF_NOPROGRESS = 0x0,
TBPF_INDETERMINATE = 0x1,
TBPF_NORMAL = 0x2,
TBPF_ERROR = 0x4,
TBPF_PAUSED = 0x8
}
[ComImport]
[Guid ("EA1AFB91-9E28-4B86-90E9-9E9F8A5EEA84")]
[InterfaceType (ComInterfaceType.InterfaceIsIUnknown)]
public interface ITaskbarList3
{
void HrInit ();
void AddTab (IntPtr hwnd);
void DeleteTab (IntPtr hwnd);
void ActivateTab (IntPtr hwnd);
void SetActiveAlt (IntPtr hwnd);
void MarkFullscreenWindow (IntPtr hwnd, [MarshalAs (UnmanagedType.Bool)] bool fFullscreen);
void SetProgressValue (IntPtr hwnd, ulong ullCompleted, ulong ullTotal);
void SetProgressState (IntPtr hwnd, TBPFLAG tbpFlags);
void RegisterTab (IntPtr hwndTab, IntPtr hwndMDI);
void UnregisterTab (IntPtr hwndTab);
void SetTabOrder (IntPtr hwndTab, IntPtr hwndInsertBefore);
void SetTabActive (IntPtr hwndTab, IntPtr hwndMDI, uint dwReserved);
void ThumbBarAddButtons (IntPtr hwnd, uint cButtons, IntPtr pButtons);
void ThumbBarUpdateButtons (IntPtr hwnd, uint cButtons, IntPtr pButtons);
void ThumbBarSetImageList (IntPtr hwnd, IntPtr himl);
void SetOverlayIcon (IntPtr hwnd, IntPtr hIcon, string pszDescription);
void SetThumbnailTooltip (IntPtr hwnd, string pszTip);
void SetThumbnailClip (IntPtr hwnd, ref RECT prcClip);
}
[ComImport]
[Guid ("56FDF344-FD6D-11d0-958A-006097C9A090")]
public class TaskbarList { }
[StructLayout (LayoutKind.Sequential)]
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
public sealed class TaskbarProgress: IDisposable
{
private readonly ITaskbarList3 _taskbar;
private readonly IntPtr _hwnd;
public TaskbarProgress (IntPtr hwnd)
{
_hwnd = hwnd;
_taskbar = (ITaskbarList3)new TaskbarList ();
_taskbar.HrInit ();
}
public void SetState (TBPFLAG state)
{
_taskbar.SetProgressState (_hwnd, state);
}
public void SetValue (ulong completed, ulong total)
{
_taskbar.SetProgressValue (_hwnd, completed, total);
}
public void Clear ()
{
_taskbar.SetProgressState (_hwnd, TBPFLAG.TBPF_NOPROGRESS);
}
public void Dispose ()
{
Clear ();
Marshal.ReleaseComObject (_taskbar);
}
}
}
+362
View File
@@ -0,0 +1,362 @@
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using Microsoft.Win32;
namespace DataUtils
{
public enum HighContrastTheme
{
None,
Black,
White,
Other
}
public static class UITheme
{
// --- P/Invoke & constants ---
private const int SPI_GETHIGHCONTRAST = 0x0042;
private const uint HCF_HIGHCONTRASTON = 0x00000001;
private const int COLOR_WINDOW = 5;
private const int COLOR_WINDOWTEXT = 8;
private const int LOGPIXELSX = 88; // GetDeviceCaps index for DPI X
// private const int HORZRES = 8; // not used now
private const int SM_CXSCREEN = 0;
private const int SM_CYSCREEN = 1;
[StructLayout (LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct HIGHCONTRAST
{
public int cbSize;
public uint dwFlags;
public IntPtr lpszDefaultScheme;
}
[DllImport ("user32.dll", SetLastError = true)]
private static extern bool SystemParametersInfo (int uiAction, int uiParam, ref HIGHCONTRAST pvParam, int fWinIni);
[DllImport ("user32.dll")]
private static extern int GetSysColor (int nIndex);
[DllImport ("user32.dll")]
private static extern int GetSystemMetrics (int nIndex);
[DllImport ("user32.dll")]
private static extern IntPtr GetDC (IntPtr hWnd);
[DllImport ("user32.dll")]
private static extern int ReleaseDC (IntPtr hWnd, IntPtr hDC);
[DllImport ("gdi32.dll")]
private static extern int GetDeviceCaps (IntPtr hdc, int nIndex);
// DwmGetColorizationColor
[DllImport ("dwmapi.dll", EntryPoint = "DwmGetColorizationColor", SetLastError = false)]
private static extern int DwmGetColorizationColor (out uint pcrColorization, out bool pfOpaqueBlend);
// --- Methods ---
public static bool IsHighContrastEnabled ()
{
try
{
HIGHCONTRAST hc = new HIGHCONTRAST ();
hc.cbSize = Marshal.SizeOf (typeof (HIGHCONTRAST));
if (SystemParametersInfo (SPI_GETHIGHCONTRAST, hc.cbSize, ref hc, 0))
{
return (hc.dwFlags & HCF_HIGHCONTRASTON) != 0;
}
}
catch
{
// ignore errors
}
return false;
}
public static bool IsHighContrast => IsHighContrastEnabled ();
public static HighContrastTheme GetHighContrastTheme ()
{
try
{
HIGHCONTRAST hc = new HIGHCONTRAST ();
hc.cbSize = Marshal.SizeOf (typeof (HIGHCONTRAST));
if (!SystemParametersInfo (SPI_GETHIGHCONTRAST, hc.cbSize, ref hc, 0))
return HighContrastTheme.None;
if ((hc.dwFlags & HCF_HIGHCONTRASTON) == 0)
return HighContrastTheme.None;
int bgColorRef = GetSysColor (COLOR_WINDOW);
int textColorRef = GetSysColor (COLOR_WINDOWTEXT);
int bgR = (bgColorRef & 0x0000FF);
int bgG = (bgColorRef & 0x00FF00) >> 8;
int bgB = (bgColorRef & 0xFF0000) >> 16;
int txtR = (textColorRef & 0x0000FF);
int txtG = (textColorRef & 0x00FF00) >> 8;
int txtB = (textColorRef & 0xFF0000) >> 16;
int brightnessBg = (bgR + bgG + bgB) / 3;
int brightnessText = (txtR + txtG + txtB) / 3;
if (brightnessBg < brightnessText) return HighContrastTheme.Black;
else if (brightnessBg > brightnessText) return HighContrastTheme.White;
else return HighContrastTheme.Other;
}
catch
{
return HighContrastTheme.None;
}
}
public static HighContrastTheme HighContrast => GetHighContrastTheme ();
// Returns DPI as percent (100 = normal 96 DPI)
public static int GetDPI ()
{
IntPtr hdc = IntPtr.Zero;
try
{
hdc = GetDC (IntPtr.Zero);
if (hdc == IntPtr.Zero) return 0;
int dpiX = GetDeviceCaps (hdc, LOGPIXELSX);
if (dpiX <= 0) return 0;
// convert to percentage of 96 DPI baseline
int percent = (int)Math.Round ((dpiX / 96.0) * 100.0);
return percent;
}
catch
{
return 0;
}
finally
{
if (hdc != IntPtr.Zero) ReleaseDC (IntPtr.Zero, hdc);
}
}
public static int DPI => GetDPI ();
public static double DPIDouble => GetDPI () * 0.01;
public static int GetScreenWidth ()
{
try { return GetSystemMetrics (SM_CXSCREEN); }
catch { return 0; }
}
public static int ScreenWidth => GetScreenWidth ();
public static int GetScreenHeight ()
{
try { return GetSystemMetrics (SM_CYSCREEN); }
catch { return 0; }
}
public static int ScreenHeight => GetScreenHeight ();
public static Color GetDwmThemeColor ()
{
try
{
uint color;
bool opaque;
int hr = DwmGetColorizationColor (out color, out opaque);
if (hr == 0) // S_OK
{
byte r = (byte)((color & 0x00FF0000) >> 16);
byte g = (byte)((color & 0x0000FF00) >> 8);
byte b = (byte)(color & 0x000000FF);
return Color.FromArgb (r, g, b);
}
}
catch
{
// ignored
}
// fallback default (matches original C++ fallback)
return Color.FromArgb (0, 120, 215);
}
public static Color ThemeColor => GetDwmThemeColor ();
public static string ColorToHtml (Color color)
{
// Return #RRGGBB
return string.Format ("#{0:X2}{1:X2}{2:X2}", color.R, color.G, color.B);
}
public static Color StringToColor (string colorStr)
{
if (string.IsNullOrWhiteSpace (colorStr)) return Color.Transparent;
string s = colorStr.Trim ();
// Named color
try
{
Color byName = Color.FromName (s);
if (byName.IsKnownColor || byName.IsNamedColor)
{
return byName;
}
}
catch { /* ignore */ }
// Hex: #RGB, #RRGGBB, #AARRGGBB
if (s.StartsWith ("#"))
{
string hex = s.Substring (1);
// Expand short forms (#RGB or #RGBA)
if (hex.Length == 3 || hex.Length == 4)
{
string expanded = "";
for (int i = 0; i < hex.Length; i++)
{
expanded += hex [i].ToString () + hex [i].ToString ();
}
hex = expanded;
}
uint argb;
try
{
argb = Convert.ToUInt32 (hex, 16);
}
catch
{
return Color.Transparent;
}
if (hex.Length == 6)
{
int r = (int)((argb >> 16) & 0xFF);
int g = (int)((argb >> 8) & 0xFF);
int b = (int)(argb & 0xFF);
return Color.FromArgb (r, g, b);
}
else if (hex.Length == 8)
{
int a = (int)((argb >> 24) & 0xFF);
int r = (int)((argb >> 16) & 0xFF);
int g = (int)((argb >> 8) & 0xFF);
int b = (int)(argb & 0xFF);
return Color.FromArgb (a, r, g, b);
}
else
{
return Color.Transparent;
}
}
// rgb()/rgba() functional notation
// Accept forms like: rgb(255,0,0) or rgba(255,0,0,0.5) or rgb(100%,0%,0%)
var m = Regex.Match (s, @"^(rgba?)\s*\(\s*([^\)]+)\s*\)$", RegexOptions.IgnoreCase);
if (m.Success)
{
string func = m.Groups [1].Value.ToLowerInvariant ();
string inside = m.Groups [2].Value;
string [] parts = inside.Split (new char [] { ',' }, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length >= 3)
{
try
{
Func<string, int> comp = (string v) => {
v = v.Trim ();
if (v.EndsWith ("%"))
{
// percentage
string num = v.TrimEnd ('%');
float p;
if (float.TryParse (num, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out p))
{
return (int)Math.Round (Math.Max (0, Math.Min (100, p)) * 255.0 / 100.0);
}
return 0;
}
else
{
int iv;
if (int.TryParse (v, out iv))
{
return Math.Max (0, Math.Min (255, iv));
}
// fallback parse float
float fv;
if (float.TryParse (v, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out fv))
{
return Math.Max (0, Math.Min (255, (int)Math.Round (fv)));
}
return 0;
}
};
int r = comp (parts [0]);
int g = comp (parts [1]);
int b = comp (parts [2]);
int a = 255;
if (func == "rgba" && parts.Length >= 4)
{
string av = parts [3].Trim ();
if (av.EndsWith ("%"))
{
string num = av.TrimEnd ('%');
float p;
if (float.TryParse (num, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out p))
{
a = (int)Math.Round (Math.Max (0, Math.Min (100, p)) * 255.0 / 100.0);
}
}
else
{
// alpha may be 0..1 or 0..255
float af;
if (float.TryParse (av, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out af))
{
if (af <= 1.0f) a = (int)Math.Round (Math.Max (0.0f, Math.Min (1.0f, af)) * 255.0f);
else a = (int)Math.Round (Math.Max (0.0f, Math.Min (255.0f, af)));
}
}
}
return Color.FromArgb (a, r, g, b);
}
catch
{
return Color.Transparent;
}
}
}
// fallback: try parse as known color again (case-insensitive)
try
{
Color named = Color.FromName (s);
if (named.IsKnownColor || named.IsNamedColor) return named;
}
catch { }
return Color.Transparent;
}
public static bool IsAppInDarkMode ()
{
try
{
// HKCU\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize\AppsUseLightTheme
object val = Registry.GetValue (
@"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize",
"AppsUseLightTheme",
null);
if (val == null) return false; // default to light? original returned false
int intVal;
if (val is int) intVal = (int)val;
else
{
if (!int.TryParse (val.ToString (), out intVal)) return false;
}
// 0 => dark, 1 => light
return intVal == 0;
}
catch
{
return false;
}
}
public static bool AppDarkMode => IsAppInDarkMode ();
}
}
+185
View File
@@ -0,0 +1,185 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using Newtonsoft.Json;
namespace DataUtils
{
public static class Utilities
{
// Format string with args (compatible with your C++ FormatString wrapper)
public static string FormatString (string fmt, params object [] args)
{
if (fmt == null) return string.Empty;
if (args == null || args.Length == 0) return fmt;
try
{
return string.Format (System.Globalization.CultureInfo.InvariantCulture, fmt, args);
}
catch
{
// On format error, fallback to simple concat
try
{
StringBuilder sb = new StringBuilder ();
sb.Append (fmt);
sb.Append (" : ");
for (int i = 0; i < args.Length; i++)
{
if (i > 0) sb.Append (", ");
sb.Append (args [i] != null ? args [i].ToString () : "null");
}
return sb.ToString ();
}
catch
{
return fmt;
}
}
}
// Escape string to be used as InnerXml content (returns XML-escaped content)
public static string EscapeToInnerXml (string str)
{
if (str == null) return string.Empty;
var doc = new XmlDocument ();
// create a root element and use InnerText to perform escaping
var root = doc.CreateElement ("body");
root.InnerText = str;
return root.InnerXml;
}
// Returns the current process full path (exe)
public static string GetCurrentProgramPath ()
{
try
{
return System.Diagnostics.Process.GetCurrentProcess ().MainModule.FileName;
}
catch
{
try
{
return System.Reflection.Assembly.GetEntryAssembly ().Location;
}
catch
{
return AppDomain.CurrentDomain.BaseDirectory;
}
}
}
// JSON array builder using Newtonsoft.Json
public static string StringArrayToJson (string [] values)
{
if (values == null) return "[]";
try
{
return JsonConvert.SerializeObject (values);
}
catch
{
// Fallback to manual builder
StringBuilder sb = new StringBuilder ();
sb.Append ('[');
for (int i = 0; i < values.Length; i++)
{
if (i > 0) sb.Append (',');
sb.Append ('"');
sb.Append (JsonEscape (values [i] ?? string.Empty));
sb.Append ('"');
}
sb.Append (']');
return sb.ToString ();
}
}
public static string StringListToJson (System.Collections.Generic.List<string> list)
{
if (list == null) return "[]";
return StringArrayToJson (list.ToArray ());
}
// Minimal JSON string escaper (fallback)
private static string JsonEscape (string s)
{
if (string.IsNullOrEmpty (s)) return s ?? string.Empty;
StringBuilder sb = new StringBuilder (s.Length + 8);
foreach (char c in s)
{
switch (c)
{
case '"': sb.Append ("\\\""); break;
case '\\': sb.Append ("\\\\"); break;
case '\b': sb.Append ("\\b"); break;
case '\f': sb.Append ("\\f"); break;
case '\n': sb.Append ("\\n"); break;
case '\r': sb.Append ("\\r"); break;
case '\t': sb.Append ("\\t"); break;
default:
if (c < 32 || c == '\u2028' || c == '\u2029')
{
sb.AppendFormat ("\\u{0:X4}", (int)c);
}
else
{
sb.Append (c);
}
break;
}
}
return sb.ToString ();
}
// Helper: combine multiple filters split by ';' or '\' (legacy)
public static string [] SplitFilters (string filter)
{
if (string.IsNullOrEmpty (filter)) return new string [] { "*" };
// Accept ';' or '\' or '|' as separators (common)
string [] parts = filter.Split (new char [] { ';', '\\', '|' }, StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < parts.Length; i++)
{
parts [i] = parts [i].Trim ();
if (parts [i].Length == 0) parts [i] = "*";
}
if (parts.Length == 0) return new string [] { "*" };
return parts;
}
// Normalize full path for comparisons
public static string NormalizeFullPath (string path)
{
if (string.IsNullOrEmpty (path)) return string.Empty;
try
{
return Path.GetFullPath (path).TrimEnd (Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
}
catch
{
return path.Trim ();
}
}
// 忽略大小写和首尾空的比较
public static bool NEquals (this string left, string right)
{
return (left ?? "")?.Trim ()?.ToLower ()?.Equals ((right ?? "")?.Trim ()?.ToLower ()) ?? false;
}
public static int NCompareTo (this string l, string r)
{
return (l ?? "")?.Trim ()?.ToLower ().CompareTo ((r ?? "")?.Trim ()?.ToLower ()) ?? 0;
}
public static bool NEmpty (this string l)
{
return (l ?? "")?.NEquals ("") ?? true;
}
public static int NLength (this string l)
{
return (l ?? "")?.Length ?? 0;
}
public static bool NNoEmpty (this string l) => !((l ?? "")?.NEmpty () ?? true);
public static string NNormalize (this string l) => (l ?? "")?.Trim ()?.ToLower () ?? "";
}
}
+368
View File
@@ -0,0 +1,368 @@
using System;
using System.Globalization;
using System.Runtime.InteropServices;
namespace DataUtils
{
/// <summary>
/// Compact version type that encodes 4 x 16-bit parts into a 64-bit value:
/// bits 48..63 = major, 32..47 = minor, 16..31 = build, 0..15 = revision.
/// </summary>
[ComVisible (true)]
public class Version: IComparable<Version>, IEquatable<Version>
{
// Backing fields
private ushort major;
private ushort minor;
private ushort build;
private ushort revision;
public ushort Major
{
get { return major; }
set { major = value; }
}
public ushort Minor
{
get { return minor; }
set { minor = value; }
}
public ushort Build
{
get { return build; }
set { build = value; }
}
public ushort Revision
{
get { return revision; }
set { revision = value; }
}
public Version ()
{
major = minor = build = revision = 0;
}
public Version (ushort major, ushort minor, ushort build, ushort revision)
{
this.major = major;
this.minor = minor;
this.build = build;
this.revision = revision;
}
public Version (ushort major, ushort minor, ushort build) : this (major, minor, build, 0) { }
public Version (ushort major, ushort minor) : this (major, minor, 0, 0) { }
public Version (ushort major) : this (major, 0, 0, 0) { }
public Version (ulong packed)
{
FromUInt64 (packed);
}
public Version (string versionString)
{
ParseInto (versionString);
}
public Version (Version other)
{
if (other == null) throw new ArgumentNullException ("other");
major = other.major;
minor = other.minor;
build = other.build;
revision = other.revision;
}
public ulong ToUInt64 ()
{
// cast to ulong before shifting
return (((ulong)major) << 48) | (((ulong)minor) << 32) | (((ulong)build) << 16) | ((ulong)revision);
}
public void FromUInt64 (ulong value)
{
major = (ushort)((value >> 48) & 0xFFFFUL);
minor = (ushort)((value >> 32) & 0xFFFFUL);
build = (ushort)((value >> 16) & 0xFFFFUL);
revision = (ushort)(value & 0xFFFFUL);
}
public ulong Data { get { return ToUInt64 (); } set { FromUInt64 (value); } }
public override string ToString ()
{
// use string.Format to be compatible with older compilers
return string.Format (CultureInfo.InvariantCulture, "{0}.{1}.{2}.{3}", major, minor, build, revision);
}
public string ToShortString ()
{
// omit trailing zeros if desired: e.g. "1.2" or "1.2.3"
if (revision != 0)
return ToString ();
if (build != 0)
return string.Format (CultureInfo.InvariantCulture, "{0}.{1}.{2}", major, minor, build);
if (minor != 0)
return string.Format (CultureInfo.InvariantCulture, "{0}.{1}", major, minor);
return string.Format (CultureInfo.InvariantCulture, "{0}", major);
}
private void ParseInto (string s)
{
if (string.IsNullOrEmpty (s))
{
major = minor = build = revision = 0;
return;
}
char [] separators = new char [] { '.', ',' };
string [] parts = s.Split (separators, StringSplitOptions.RemoveEmptyEntries);
ushort [] values = new ushort [4];
for (int i = 0; i < values.Length && i < parts.Length; i++)
{
ushort v = 0;
try
{
int parsed = int.Parse (parts [i].Trim (), NumberStyles.Integer, CultureInfo.InvariantCulture);
if (parsed < 0) parsed = 0;
if (parsed > 0xFFFF) parsed = 0xFFFF;
v = (ushort)parsed;
}
catch
{
v = 0;
}
values [i] = v;
}
major = values [0];
minor = values [1];
build = values [2];
revision = values [3];
}
public string Expression { get { return this.ToString (); } set { this.ParseInto (value); } }
public static Version Parse (string s)
{
if (s == null) throw new ArgumentNullException ("s");
return new Version (s);
}
public static bool TryParse (string s, out Version result)
{
result = null;
if (s == null) return false;
try
{
result = new Version (s);
return true;
}
catch
{
result = null;
return false;
}
}
public bool IsEmpty
{
get { return (major == 0 && minor == 0 && build == 0 && revision == 0); }
}
public bool Equals (Version other)
{
if (object.ReferenceEquals (other, null)) return false;
return (this.major == other.major
&& this.minor == other.minor
&& this.build == other.build
&& this.revision == other.revision);
}
public bool Equals (ulong ver) { return this == new Version (ver); }
public override bool Equals (object obj)
{
Version v = obj as Version;
return Equals (v);
}
public override int GetHashCode ()
{
// derive from packed ulong but return int
ulong packed = ToUInt64 ();
// combine high and low 32 bits for a reasonable hash
return ((int)(packed & 0xFFFFFFFF)) ^ ((int)((packed >> 32) & 0xFFFFFFFF));
}
public int CompareTo (Version other)
{
if (object.ReferenceEquals (other, null)) return 1;
// Compare by packed value (same semantics as C++ compare())
ulong a = this.ToUInt64 ();
ulong b = other.ToUInt64 ();
if (a < b) return -1;
if (a > b) return 1;
return 0;
}
public int CompareTo (ulong another) { return this.CompareTo (new Version (another)); }
public long Compare (Version other)
{
if (other == null) throw new ArgumentNullException ("other");
// return signed difference of packed values using long
long diff = (long)this.ToUInt64 () - (long)other.ToUInt64 ();
return diff;
}
public long Compare (ulong another) { return this.Compare (new Version (another)); }
public static bool operator == (Version a, Version b)
{
if (object.ReferenceEquals (a, b)) return true;
if (object.ReferenceEquals (a, null) || object.ReferenceEquals (b, null)) return false;
return a.Equals (b);
}
public static bool operator != (Version a, Version b)
{
return !(a == b);
}
public static bool operator < (Version a, Version b)
{
if (object.ReferenceEquals (a, null))
return !object.ReferenceEquals (b, null); // null < non-null
return a.CompareTo (b) < 0;
}
public static bool operator > (Version a, Version b)
{
if (object.ReferenceEquals (a, null))
return false;
return a.CompareTo (b) > 0;
}
public static bool operator <= (Version a, Version b)
{
if (object.ReferenceEquals (a, b)) return true;
if (object.ReferenceEquals (a, null)) return true; // null <= anything
return a.CompareTo (b) <= 0;
}
public static bool operator >= (Version a, Version b)
{
if (object.ReferenceEquals (a, b)) return true;
if (object.ReferenceEquals (a, null)) return false;
return a.CompareTo (b) >= 0;
}
public static explicit operator ulong (Version v)
{
if (v == null) return 0UL;
return v.ToUInt64 ();
}
public static explicit operator Version (ulong value)
{
return new Version (value);
}
public static Version Decode (ulong packed)
{
return new Version (packed);
}
public static ulong Encode (Version v)
{
if (v == null) return 0UL;
return v.ToUInt64 ();
}
}
[ComVisible (true)]
[ClassInterface (ClassInterfaceType.AutoDual)]
public class _I_Version
{
private Version inner;
public _I_Version ()
{
inner = new Version ();
}
public _I_Version (ushort p_ma, ushort p_mi, ushort p_b, ushort p_r)
{
inner = new Version (p_ma, p_mi, p_b, p_r);
}
public _I_Version (ushort p_ma, ushort p_mi, ushort p_b) : this (p_ma, p_mi, p_b, 0) { }
public _I_Version (ushort p_ma, ushort p_mi) : this (p_ma, p_mi, 0, 0) { }
public _I_Version (ushort p_ma) : this (p_ma, 0, 0, 0) { }
public ushort Major
{
get { return inner.Major; }
set { inner.Major = value; }
}
public ushort Minor
{
get { return inner.Minor; }
set { inner.Minor = value; }
}
public ushort Build
{
get { return inner.Build; }
set { inner.Build = value; }
}
public ushort Revision
{
get { return inner.Revision; }
set { inner.Revision = value; }
}
public ushort [] Data
{
get
{
return new ushort [] { inner.Major, inner.Minor, inner.Build, inner.Revision };
}
set
{
if (value == null)
{
inner.Major = inner.Minor = inner.Build = inner.Revision = 0;
return;
}
if (value.Length > 0) inner.Major = value [0];
else inner.Major = 0;
if (value.Length > 1) inner.Minor = value [1];
else inner.Minor = 0;
if (value.Length > 2) inner.Build = value [2];
else inner.Build = 0;
if (value.Length > 3) inner.Revision = value [3];
else inner.Revision = 0;
}
}
public string DataStr
{
get { return Stringify (); }
set { Parse (value); }
}
public _I_Version Parse (string ver)
{
if (string.IsNullOrEmpty (ver))
{
inner = new Version ();
return this;
}
char [] separators = new char [] { '.', ',' };
string [] parts = ver.Split (separators, StringSplitOptions.RemoveEmptyEntries);
ushort [] vals = new ushort [4];
for (int i = 0; i < vals.Length && i < parts.Length; i++)
{
ushort v = 0;
try
{
int parsed = int.Parse (parts [i].Trim (), NumberStyles.Integer, CultureInfo.InvariantCulture);
if (parsed < 0) parsed = 0;
if (parsed > 0xFFFF) parsed = 0xFFFF;
v = (ushort)parsed;
}
catch
{
v = 0;
}
vals [i] = v;
}
inner.Major = vals [0];
inner.Minor = vals [1];
inner.Build = vals [2];
inner.Revision = vals [3];
return this;
}
public string Stringify ()
{
return inner.ToString ();
}
public override string ToString ()
{
return Stringify ();
}
public bool Valid ()
{
return inner.Major != 0 && inner.Minor != 0 && inner.Build != 0 && inner.Revision != 0;
}
public ulong ToPackedUInt64 ()
{
return inner.ToUInt64 ();
}
public void FromPackedUInt64 (ulong packed)
{
inner.FromUInt64 (packed);
}
public Version InnerVersion
{
get { return inner; }
set { inner = (value != null) ? new Version (value) : new Version (); }
}
}
}
+280
View File
@@ -0,0 +1,280 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Xml;
namespace DataUtils
{
public enum ManifestTextColor
{
Dark = 0x000000,
Light = 0xFFFFFF
}
[ComVisible (true)]
public class VisualElementManifest: IDisposable
{
private XmlDocument _doc;
private bool _available;
public VisualElementManifest ()
{
_available = false;
}
public VisualElementManifest (string filename)
{
Create (filename);
}
public VisualElementManifest (Stream stream)
{
Create (stream);
}
public bool Create (string filename)
{
Destroy ();
if (string.IsNullOrEmpty (filename) || !File.Exists (filename))
{
_available = false;
return false;
}
try
{
_doc = new XmlDocument ();
// Load using UTF-8/UTF-16 auto-detection
_doc.Load (filename);
_available = true;
}
catch
{
_available = false;
}
return _available;
}
public bool Create (Stream stream)
{
Destroy ();
if (stream == null) { _available = false; return false; }
try
{
_doc = new XmlDocument ();
_doc.Load (stream);
_available = true;
}
catch
{
_available = false;
}
return _available;
}
public void Destroy ()
{
if (!_available) return;
_doc = null;
_available = false;
}
public bool Valid { get { return _available; } }
// Helper: find VisualElements node for an app id (if root is <Applications> iterate <Application> children)
private XmlNode VisualElementNode (string id)
{
if (!_available || _doc == null) return null;
XmlElement root = _doc.DocumentElement;
if (root == null) return null;
string rootName = root.Name;
if (string.Equals (rootName, "Applications", StringComparison.OrdinalIgnoreCase))
{
foreach (XmlNode app in root.SelectNodes ("Application"))
{
var attr = app.Attributes? ["Id"];
if (attr != null && string.Equals (attr.Value, id, StringComparison.OrdinalIgnoreCase))
{
return app.SelectSingleNode ("VisualElements");
}
}
return null;
}
else if (string.Equals (rootName, "Application", StringComparison.OrdinalIgnoreCase))
{
return root.SelectSingleNode ("VisualElements");
}
return null;
}
// Utility to get attribute string safely
private static string Attr (XmlNode node, string attrName)
{
if (node == null || node.Attributes == null) return string.Empty;
var a = node.Attributes [attrName];
return a != null ? a.Value : string.Empty;
}
public string DisplayName (string id = "App")
{
var visual = VisualElementNode (id);
return visual != null ? Attr (visual, "DisplayName") : string.Empty;
}
public string Logo (string id = "App")
{
var visual = VisualElementNode (id);
if (visual == null) return string.Empty;
string logo = Attr (visual, "Logo");
if (!string.IsNullOrEmpty (logo)) return logo;
return Attr (visual, "Square150x150Logo") ?? string.Empty;
}
public string SmallLogo (string id = "App")
{
var visual = VisualElementNode (id);
if (visual == null) return string.Empty;
string small = Attr (visual, "SmallLogo");
if (!string.IsNullOrEmpty (small)) return small;
return Attr (visual, "Square70x70Logo") ?? string.Empty;
}
public ManifestTextColor ForegroundText (string id = "App")
{
var visual = VisualElementNode (id);
if (visual == null) return ManifestTextColor.Dark;
string fg = Attr (visual, "ForegroundText");
return string.Equals (fg, "light", StringComparison.OrdinalIgnoreCase) ? ManifestTextColor.Light : ManifestTextColor.Dark;
}
public string Lnk32x32Logo (string id = "App")
{
var visual = VisualElementNode (id);
return visual != null ? Attr (visual, "Lnk32x32Logo") : string.Empty;
}
public string ItemDisplayLogo (string id = "App")
{
var visual = VisualElementNode (id);
if (visual == null) return string.Empty;
string item = Attr (visual, "ItemDisplayLogo");
if (!string.IsNullOrEmpty (item)) return item;
item = Attr (visual, "Lnk32x32Logo");
if (!string.IsNullOrEmpty (item)) return item;
return Attr (visual, "Square44x44Logo") ?? string.Empty;
}
public bool ShowNameOnTile (string id = "App")
{
var visual = VisualElementNode (id);
if (visual == null) return false;
string val = Attr (visual, "ShowNameOnSquare150x150Logo");
return string.Equals (val, "on", StringComparison.OrdinalIgnoreCase);
}
public string BackgroundColor (string id = "App")
{
var visual = VisualElementNode (id);
return visual != null ? Attr (visual, "BackgroundColor") : string.Empty;
}
public string SplashScreenImage (string id = "App")
{
var visual = VisualElementNode (id);
if (visual == null) return string.Empty;
var splash = visual.SelectSingleNode ("SplashScreen");
return splash != null ? Attr (splash, "Image") : string.Empty;
}
public string SplashScreenBackgroundColor (string id = "App")
{
var visual = VisualElementNode (id);
if (visual == null) return string.Empty;
var splash = visual.SelectSingleNode ("SplashScreen");
string bg = splash != null ? Attr (splash, "BackgroundColor") : string.Empty;
if (!string.IsNullOrEmpty (bg)) return bg;
return Attr (visual, "BackgroundColor") ?? string.Empty;
}
public string SplashScreenBackgroundColorDarkMode (string id = "App")
{
var visual = VisualElementNode (id);
if (visual == null) return string.Empty;
var splash = visual.SelectSingleNode ("SplashScreen");
string bg = splash != null ? Attr (splash, "DarkModeBackgroundColor") : string.Empty;
if (!string.IsNullOrEmpty (bg)) return bg;
return Attr (visual, "DarkModeBackgroundColor") ?? string.Empty;
}
// Check if an app id exists in document
public bool IsAppIdExists (string id)
{
if (!_available || _doc == null) return false;
XmlElement root = _doc.DocumentElement;
if (root == null) return false;
if (string.Equals (root.Name, "Applications", StringComparison.OrdinalIgnoreCase))
{
foreach (XmlNode app in root.SelectNodes ("Application"))
{
var attr = app.Attributes? ["Id"];
if (attr != null && string.Equals (attr.Value, id, StringComparison.OrdinalIgnoreCase))
return true;
}
return false;
}
else if (string.Equals (root.Name, "Application", StringComparison.OrdinalIgnoreCase))
{
var attr = root.Attributes? ["Id"];
if (attr != null && string.Equals (attr.Value, id, StringComparison.OrdinalIgnoreCase)) return true;
}
return false;
}
// Get all application ids as a list
public List<string> AppIds ()
{
var output = new List<string> ();
if (!_available || _doc == null)
{
output.Add ("App");
return output;
}
XmlElement root = _doc.DocumentElement;
if (root == null)
{
output.Add ("App");
return output;
}
if (string.Equals (root.Name, "Applications", StringComparison.OrdinalIgnoreCase))
{
foreach (XmlNode app in root.SelectNodes ("Application"))
{
var attr = app.Attributes? ["Id"];
if (attr != null && !string.IsNullOrEmpty (attr.Value))
{
if (!output.Contains (attr.Value)) output.Add (attr.Value);
}
}
}
else if (string.Equals (root.Name, "Application", StringComparison.OrdinalIgnoreCase))
{
var attr = root.Attributes? ["Id"];
if (attr != null && !string.IsNullOrEmpty (attr.Value))
{
if (!output.Contains (attr.Value)) output.Add (attr.Value);
}
}
if (output.Count == 0) output.Add ("App");
return output;
}
public void Dispose ()
{
Destroy ();
}
}
}
+40
View File
@@ -0,0 +1,40 @@
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace DataUtils
{
public enum OLECMDID
{
OLECMDID_OPTICAL_ZOOM = 63
}
public enum OLECMDEXECOPT
{
OLECMDEXECOPT_DODEFAULT = 0,
OLECMDEXECOPT_DONTPROMPTUSER = 2
}
[ComImport]
[Guid ("D30C1661-CDAF-11d0-8A3E-00C04FC9E26E")]
[InterfaceType (ComInterfaceType.InterfaceIsIDispatch)]
public interface IWebBrowser2
{
[DispId (0x000001F4)]
void ExecWB (
OLECMDID cmdID,
OLECMDEXECOPT cmdexecopt,
ref object pvaIn,
ref object pvaOut
);
}
public static class WebBrowserHelper
{
public static IWebBrowser2 GetWebBrowser2 (WebBrowser browser)
{
return browser.ActiveXInstance as IWebBrowser2;
}
}
public interface IWebBrowserPageScale
{
int PageScale { get; set; }
}
}
+73
View File
@@ -0,0 +1,73 @@
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace DataUtils
{
public class _I_UI_Size
{
private int m_width;
private int m_height;
public _I_UI_Size (int w, int h)
{
m_width = w;
m_height = h;
}
public int Width => m_width;
public int Height => m_height;
public int GetWidth () => m_width;
public int GetHeight () => m_height;
}
[ComVisible (true)]
[ClassInterface (ClassInterfaceType.AutoDual)]
public class _I_UI
{
private Form wndInst;
public _I_UI (Form wnd)
{
wndInst = wnd;
}
public int DPIPercent => UITheme.GetDPI ();
public double DPI => DPIPercent * 0.01;
public _I_UI_Size WndSize => new _I_UI_Size (wndInst.Width, wndInst.Height);
public _I_UI_Size ClientSize
{
get
{
var cs = wndInst.ClientSize;
return new _I_UI_Size (cs.Width, cs.Height);
}
}
public string ThemeColor => UITheme.ColorToHtml (UITheme.GetDwmThemeColor ());
public bool DarkMode => UITheme.IsAppInDarkMode ();
public string HighContrast
{
get
{
switch (UITheme.GetHighContrastTheme ())
{
case HighContrastTheme.None: return "none";
case HighContrastTheme.Black: return "black";
case HighContrastTheme.White: return "white";
case HighContrastTheme.Other: return "high";
default: return "none";
}
}
}
}
[ComVisible (true)]
[ClassInterface (ClassInterfaceType.AutoDual)]
public class _I_Window
{
private IScriptBridge iscrp;
public _I_Window (IScriptBridge _iscrp)
{
iscrp = _iscrp;
}
public object CallEvent (string name, params object [] args)
{
if (iscrp == null) return null;
object arg0 = (args != null && args.Length > 0) ? args [0] : null;
return iscrp.CallEvent (name, arg0);
}
}
}
+4
View File
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="13.0.4" targetFramework="net40" />
</packages>