mirror of
https://github.com/modernw/App-Installer-For-Windows-8.x-Reset.git
synced 2026-06-19 05:40:12 +10:00
Update
This commit is contained in:
@@ -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>
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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 { }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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")]
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 */ }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 ();
|
||||
}
|
||||
}
|
||||
@@ -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 () ?? "";
|
||||
}
|
||||
}
|
||||
@@ -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 (); }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 ();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Newtonsoft.Json" version="13.0.4" targetFramework="net40" />
|
||||
</packages>
|
||||
Reference in New Issue
Block a user