diff --git a/AppxPackage/PackageReader.cs b/AppxPackage/PackageReader.cs index 6bf363e..fd41972 100644 --- a/AppxPackage/PackageReader.cs +++ b/AppxPackage/PackageReader.cs @@ -31,11 +31,25 @@ namespace AppxPackage "Square71x71Logo", "StartPage", "Tall150x310Logo", - "VisualGroup", "WideLogo", "Wide310x150Logo", "Executable" }; + public static readonly string [] ImageFilePathItems = new string [] + { + "LockScreenLogo", + "Logo", + "SmallLogo", + "Square150x150Logo", + "Square30x30Logo", + "Square310x310Logo", + "Square44x44Logo", + "Square70x70Logo", + "Square71x71Logo", + "Tall150x310Logo", + "WideLogo", + "Wide310x150Logo" + }; public static bool IsFilePathKey (string key) { foreach (var i in FilePathItems) @@ -293,7 +307,7 @@ namespace AppxPackage return obj.Trim ().ToLowerInvariant ().GetHashCode (); } } - private HashSet set = new HashSet (new TrimIgnoreCaseComparer ()); + private HashSet set = new HashSet (new TrimIgnoreCaseComparer ()); public Dictionary FileResourceAllValues (string resid, ZipOutputStream zos) { var comparer = new TrimIgnoreCaseComparer (); @@ -716,6 +730,13 @@ namespace AppxPackage return true; return false; } + protected bool IsImageFilePathKey (string key) + { + foreach (var i in ConstData.ImageFilePathItems) + if ((i?.Trim ()?.ToLower () ?? "") == (key?.Trim ()?.ToLower () ?? "")) + return true; + return false; + } public new string this [string key] { get @@ -767,7 +788,7 @@ namespace AppxPackage string pri = PriGetRes (value); return string.IsNullOrEmpty (pri) ? value : pri; } - if (IsFilePathKey (key) && !string.IsNullOrEmpty (value)) + if (IsImageFilePathKey (key) && !string.IsNullOrEmpty (value)) { string pri = PriGetRes (value); return string.IsNullOrEmpty (pri) ? value : pri; @@ -777,7 +798,7 @@ namespace AppxPackage public string NewAtBase64 (string key) { string value = NewAt (key, true); - if (!IsFilePathKey (key) || string.IsNullOrEmpty (value)) return ""; + if (!IsImageFilePathKey (key) || string.IsNullOrEmpty (value)) return ""; switch (PackageReadHelper.GetPackageType (m_hReader)) { case 1: // PKGTYPE_APPX @@ -2458,7 +2479,7 @@ namespace AppxPackage #endregion #region dependencies XmlElement nodeDependencies = xml.CreateElement ("Dependencies"); - var deps = Dependencies; + var deps = Dependencies; if (deps != null) { foreach (var dep in deps) diff --git a/Bridge/SysInit.cs b/Bridge/SysInit.cs index 1c91d0c..155b230 100644 --- a/Bridge/SysInit.cs +++ b/Bridge/SysInit.cs @@ -936,6 +936,7 @@ namespace Bridge public _I_Notice Notice => new _I_Notice (); public _I_Process Process => proc; public _I_Web Web => new _I_Web (); + public _I_Utilities Utilities => new _I_Utilities (); public string CmdArgs { get diff --git a/DataUtils/Calendar.cs b/DataUtils/Calendar.cs new file mode 100644 index 0000000..9b8177e --- /dev/null +++ b/DataUtils/Calendar.cs @@ -0,0 +1,816 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Linq; +using System.Reflection; + +namespace DataUtils +{ + [ComVisible (true)] + [ClassInterface (ClassInterfaceType.AutoDual)] + public class _I_Calendar + { + private DateTime _utcDateTime; // 存储 UTC 时间(所有算术的基础) + private Calendar _calendar; // 当前日历系统 + private TimeZoneInfo _timeZone; // 当前时区 + private bool _is24HourClock; // true = 24小时制,false = 12小时制 + private List _languages; // 语言优先级列表 + private string _numeralSystem = "Latn"; // 数字系统(简化实现,仅存储) + private const long TicksPerNanosecond = 100; // 1 tick = 100 ns + + // 缓存的本地时间(根据时区从 _utcDateTime 转换得到) + private DateTime _localDateTime; + + // 更新本地时间(当 UTC 时间或时区改变时调用) + private void UpdateLocalDateTime () + { + _localDateTime = TimeZoneInfo.ConvertTimeFromUtc (_utcDateTime, _timeZone); + } + + // 当本地时间被修改后,同步回 UTC 时间 + private void UpdateUtcDateTime () + { + _utcDateTime = TimeZoneInfo.ConvertTimeToUtc (_localDateTime, _timeZone); + } + + // 辅助:日历字段的读取 + private int GetCalendarField (Func fieldGetter) + { + return fieldGetter (_calendar, _localDateTime); + } + + // 辅助:日历字段的设置(返回新的本地时间) + private DateTime SetCalendarField (DateTime currentLocal, Func fieldSetter, int value) + { + return fieldSetter (_calendar, currentLocal, value); + } + + // 根据日历标识符创建日历实例 + private static Calendar CreateCalendar (string calendarId) + { + switch (calendarId) + { + case "GregorianCalendar": + return new GregorianCalendar (); + case "HebrewCalendar": + return new HebrewCalendar (); + case "HijriCalendar": + return new HijriCalendar (); + case "JapaneseCalendar": + return new JapaneseCalendar (); + case "KoreanCalendar": + return new KoreanCalendar (); + case "TaiwanCalendar": + return new TaiwanCalendar (); + case "ThaiBuddhistCalendar": + return new ThaiBuddhistCalendar (); + case "UmAlQuraCalendar": + return new UmAlQuraCalendar (); + // 可根据需要增加更多日历 + default: + return new GregorianCalendar (); + } + } + + private _I_Language GetFormatCulture () + { + if (_languages == null || _languages.Count == 0) + return new _I_Language (CultureInfo.CurrentCulture.Name); + try + { + return new _I_Language (_languages [0]); + } + catch + { + return new _I_Language (CultureInfo.CurrentCulture.Name); + } + } + + private List JsArrayToList (object jsArray) + { + var result = new List (); + + if (jsArray == null) + return result; + + // JS Array 有 length 属性和数字索引器 + var type = jsArray.GetType (); + + int length = (int)type.InvokeMember ( + "length", + BindingFlags.GetProperty, + null, + jsArray, + null); + + for (int i = 0; i < length; i++) + { + object value = type.InvokeMember ( + i.ToString (), + BindingFlags.GetProperty, + null, + jsArray, + null); + + result.Add (value); + } + + return result; + } + + public _I_Calendar () + : this (new List { CultureInfo.CurrentCulture.Name }, + "GregorianCalendar", "24HourClock", TimeZoneInfo.Local.Id) + { + } + + public _I_Calendar (object languages) + : this (languages, "GregorianCalendar", "24HourClock", TimeZoneInfo.Local.Id) + { + } + + public _I_Calendar (object languages, string calendar, string clock) + : this (languages, calendar, clock, TimeZoneInfo.Local.Id) + { + } + + public _I_Calendar (object languages, string calendar, string clock, string timeZoneId) + { + _languages = languages == null ? new List () : new List (JsArrayToList (languages).Select (e => e as string)); + if (_languages.Count == 0) + _languages.Add (CultureInfo.CurrentCulture.Name); + + _calendar = CreateCalendar (calendar); + _is24HourClock = (clock == "24HourClock"); + _timeZone = TimeZoneInfo.FindSystemTimeZoneById (timeZoneId); + _utcDateTime = DateTime.UtcNow; + UpdateLocalDateTime (); + } + + public int Day + { + get + { + return _calendar.GetDayOfMonth (_localDateTime); + } + set + { + int currentDay = _calendar.GetDayOfMonth (_localDateTime); + if (value == currentDay) return; + // 通过加减天数来设置日期 + DateTime newLocal = _calendar.AddDays (_localDateTime, value - currentDay); + _localDateTime = newLocal; + UpdateUtcDateTime (); + } + } + + public int DayOfWeek + { + get + { + // 返回 0-6,0 表示星期日 + return (int)_calendar.GetDayOfWeek (_localDateTime); + } + } + + public int Era + { + get + { + return _calendar.GetEra (_localDateTime); + } + set + { + // 更改纪元较复杂,简化:仅当值不同时不做任何操作(可根据需求实现) + // 这里忽略设置,或者可以抛出 NotSupportedException + if (value != Era) + throw new NotSupportedException ("Setting Era directly is not supported in this implementation."); + } + } + + public int FirstDayInThisMonth + { + get { return 1; } + } + + public int FirstEra + { + get { return _calendar.GetEra (_calendar.MinSupportedDateTime); } + } + + public int FirstHourInThisPeriod + { + get + { + if (_is24HourClock) + return 0; + else + return (Period == 0) ? 1 : 13; + } + } + + public int FirstMinuteInThisHour + { + get { return 0; } + } + + public int FirstMonthInThisYear + { + get { return 1; } + } + + public int FirstPeriodInThisDay + { + get { return 0; } // 0 = AM + } + + public int FirstSecondInThisMinute + { + get { return 0; } + } + + public int FirstYearInThisEra + { + get + { + DateTime eraStart = _calendar.MinSupportedDateTime; + int targetEra = Era; + while (_calendar.GetEra (eraStart) != targetEra && eraStart < DateTime.MaxValue) + { + eraStart = _calendar.AddYears (eraStart, 1); + } + return _calendar.GetYear (eraStart); + } + } + + public int Hour + { + get + { + int hour24 = _calendar.GetHour (_localDateTime); + if (_is24HourClock) + return hour24; + int hour12 = hour24 % 12; + return (hour12 == 0) ? 12 : hour12; + } + set + { + if (_is24HourClock) + { + if (value < 0 || value > 23) + throw new ArgumentOutOfRangeException (nameof (value), "Hour must be between 0 and 23 for 24-hour clock."); + DateTime newLocal = _localDateTime.Date.AddHours (value).AddMinutes (Minute).AddSeconds (Second); + _localDateTime = newLocal; + } + else + { + if (value < 1 || value > 12) + throw new ArgumentOutOfRangeException (nameof (value), "Hour must be between 1 and 12 for 12-hour clock."); + int hour24 = value % 12; + if (Period == 1) hour24 += 12; + DateTime newLocal = _localDateTime.Date.AddHours (hour24).AddMinutes (Minute).AddSeconds (Second); + _localDateTime = newLocal; + } + UpdateUtcDateTime (); + } + } + + public bool IsDaylightSavingTime + { + get { return _timeZone.IsDaylightSavingTime (_localDateTime); } + } + + public _I_List Languages + { + get { return new _I_List (_languages.AsReadOnly ().Select (e => (object)e), true) ; } + } + + public int LastDayInThisMonth + { + get { return _calendar.GetDaysInMonth (_calendar.GetYear (_localDateTime), _calendar.GetMonth (_localDateTime)); } + } + + public int LastEra + { + get { return _calendar.GetEra (_calendar.MaxSupportedDateTime); } + } + + public int LastHourInThisPeriod + { + get + { + if (_is24HourClock) + return 23; + else + return (Period == 0) ? 11 : 23; + } + } + + public int LastMinuteInThisHour + { + get { return 59; } + } + + public int LastMonthInThisYear + { + get { return _calendar.GetMonthsInYear (_calendar.GetYear (_localDateTime)); } + } + + public int LastPeriodInThisDay + { + get { return 1; } // 1 = PM + } + + public int LastSecondInThisMinute + { + get { return 59; } + } + + public int LastYearInThisEra + { + get + { + DateTime eraEnd = _calendar.MaxSupportedDateTime; + int targetEra = Era; + while (_calendar.GetEra (eraEnd) != targetEra && eraEnd > DateTime.MinValue) + { + eraEnd = _calendar.AddYears (eraEnd, -1); + } + return _calendar.GetYear (eraEnd); + } + } + + public int Minute + { + get { return _calendar.GetMinute (_localDateTime); } + set + { + if (value < 0 || value > 59) + throw new ArgumentOutOfRangeException (nameof (value), "Minute must be between 0 and 59."); + DateTime newLocal = _localDateTime.AddMinutes (value - _calendar.GetMinute (_localDateTime)); + _localDateTime = newLocal; + UpdateUtcDateTime (); + } + } + + public int Month + { + get { return _calendar.GetMonth (_localDateTime); } + set + { + if (value < 1 || value > _calendar.GetMonthsInYear (_calendar.GetYear (_localDateTime))) + throw new ArgumentOutOfRangeException (nameof (value), "Month is out of range for current year."); + DateTime newLocal = SetCalendarField (_localDateTime, (cal, dt, val) => cal.AddMonths (dt, val - cal.GetMonth (dt)), value); + _localDateTime = newLocal; + UpdateUtcDateTime (); + } + } + + public int Nanosecond + { + get + { + long ticksInSecond = _localDateTime.Ticks % TimeSpan.TicksPerSecond; + return (int)(ticksInSecond * 100); // 1 tick = 100 ns + } + set + { + if (value < 0 || value > 999999999) + throw new ArgumentOutOfRangeException (nameof (value), "Nanosecond must be between 0 and 999,999,999."); + long ticks = (_localDateTime.Ticks / TimeSpan.TicksPerSecond) * TimeSpan.TicksPerSecond + (value / 100); + _localDateTime = new DateTime (ticks, _localDateTime.Kind); + UpdateUtcDateTime (); + } + } + + public int NumberOfDaysInThisMonth + { + get { return LastDayInThisMonth; } + } + + public int NumberOfEras + { + get { return _calendar.Eras.Length; } + } + + public int NumberOfHoursInThisPeriod + { + get { return _is24HourClock ? 24 : 12; } + } + + public int NumberOfMinutesInThisHour + { + get { return 60; } + } + + public int NumberOfMonthsInThisYear + { + get { return _calendar.GetMonthsInYear (_calendar.GetYear (_localDateTime)); } + } + + public int NumberOfPeriodsInThisDay + { + get { return 2; } + } + + public int NumberOfSecondsInThisMinute + { + get { return 60; } + } + + public int NumberOfYearsInThisEra + { + get { return LastYearInThisEra - FirstYearInThisEra + 1; } + } + + public string NumeralSystem + { + get { return _numeralSystem; } + set { _numeralSystem = value; } // 简化:未实现实际数字转换 + } + + public int Period + { + get + { + int hour24 = _calendar.GetHour (_localDateTime); + return (hour24 >= 12) ? 1 : 0; + } + set + { + if (value != 0 && value != 1) + throw new ArgumentOutOfRangeException (nameof (value), "Period must be 0 (AM) or 1 (PM)."); + int currentPeriod = Period; + if (currentPeriod == value) return; + // 切换 AM/PM:加减 12 小时 + DateTime newLocal = _localDateTime.AddHours (value == 0 ? -12 : 12); + _localDateTime = newLocal; + UpdateUtcDateTime (); + } + } + + public string ResolvedLanguage + { + get + { + if (_languages != null && _languages.Count > 0) + return _languages [0]; + return CultureInfo.CurrentCulture.Name; + } + } + + public int Second + { + get { return _calendar.GetSecond (_localDateTime); } + set + { + if (value < 0 || value > 59) + throw new ArgumentOutOfRangeException (nameof (value), "Second must be between 0 and 59."); + DateTime newLocal = _localDateTime.AddSeconds (value - _calendar.GetSecond (_localDateTime)); + _localDateTime = newLocal; + UpdateUtcDateTime (); + } + } + + public int Year + { + get { return _calendar.GetYear (_localDateTime); } + set + { + if (value < _calendar.GetYear (_calendar.MinSupportedDateTime) || value > _calendar.GetYear (_calendar.MaxSupportedDateTime)) + throw new ArgumentOutOfRangeException (nameof (value), "Year is out of range for this calendar."); + DateTime newLocal = SetCalendarField (_localDateTime, (cal, dt, val) => cal.AddYears (dt, val - cal.GetYear (dt)), value); + _localDateTime = newLocal; + UpdateUtcDateTime (); + } + } + + public void AddDays (int days) + { + _localDateTime = _calendar.AddDays (_localDateTime, days); + UpdateUtcDateTime (); + } + + public void AddEras (int eras) + { + // 简化:每个纪元按 1000 年估算(实际应基于日历的纪元范围) + // 大多数情况下不应频繁使用此方法 + _localDateTime = _calendar.AddYears (_localDateTime, eras * 1000); + UpdateUtcDateTime (); + } + + public void AddHours (int hours) + { + _localDateTime = _localDateTime.AddHours (hours); + UpdateUtcDateTime (); + } + + public void AddMinutes (int minutes) + { + _localDateTime = _localDateTime.AddMinutes (minutes); + UpdateUtcDateTime (); + } + + public void AddMonths (int months) + { + _localDateTime = _calendar.AddMonths (_localDateTime, months); + UpdateUtcDateTime (); + } + + public void AddNanoseconds (int nanoseconds) + { + long ticksToAdd = nanoseconds / 100; + _localDateTime = _localDateTime.AddTicks (ticksToAdd); + UpdateUtcDateTime (); + } + + public void AddPeriods (int periods) + { + _localDateTime = _localDateTime.AddHours (periods * 12); + UpdateUtcDateTime (); + } + + public void AddSeconds (int seconds) + { + _localDateTime = _localDateTime.AddSeconds (seconds); + UpdateUtcDateTime (); + } + + public void AddWeeks (int weeks) + { + AddDays (weeks * 7); + } + + public void AddYears (int years) + { + _localDateTime = _calendar.AddYears (_localDateTime, years); + UpdateUtcDateTime (); + } + + public void ChangeCalendarSystem (string calendarId) + { + Calendar newCalendar = CreateCalendar (calendarId); + // 注意:日历改变不会改变绝对时间,但会影响组件(年、月、日等) + // 只需替换日历实例,_localDateTime 保持不变(但解释方式变了) + _calendar = newCalendar; + } + + public void ChangeClock (string clock) + { + _is24HourClock = (clock == "24HourClock"); + } + + public void ChangeTimeZone (string timeZoneId) + { + _timeZone = TimeZoneInfo.FindSystemTimeZoneById (timeZoneId); + UpdateLocalDateTime (); // 重新计算本地时间(UTC 不变) + } + + public _I_Calendar Clone () + { + var clone = new _I_Calendar (_languages, GetCalendarSystem (), GetClock (), GetTimeZone ()); + clone.SetDateTime (GetDateTime ()); + return clone; + } + + public int Compare (_I_Calendar other) + { + if (other == null) return 1; + return DateTime.Compare (this.GetDateTime (), other.GetDateTime ()); + } + + public static DateTime JsDateToDateTime (object jsDate) + { + if (jsDate == null) + throw new ArgumentNullException (nameof (jsDate)); + Type type = jsDate.GetType (); + double milliseconds = (double)type.InvokeMember ( + "getTime", + BindingFlags.InvokeMethod, + null, + jsDate, + null); + DateTimeOffset epoch = new DateTimeOffset (1970, 1, 1, 0, 0, 0, TimeSpan.Zero); + DateTimeOffset dateTimeOffset = epoch.AddMilliseconds (milliseconds); + return dateTimeOffset.UtcDateTime; + } + public int CompareDateTime (object other) + { + return DateTime.Compare (this.GetDateTime (), JsDateToDateTime (other)); + } + + public void CopyTo (_I_Calendar other) + { + if (other == null) throw new ArgumentNullException (nameof (other)); + other._utcDateTime = this._utcDateTime; + other._localDateTime = this._localDateTime; + other._calendar = this._calendar; + other._timeZone = this._timeZone; + other._is24HourClock = this._is24HourClock; + other._languages = new List (this._languages); + other._numeralSystem = this._numeralSystem; + } + + public string DayAsPaddedString (int minDigits) + { + return Day.ToString ().PadLeft (minDigits, '0'); + } + + public string DayAsString () + { + return Day.ToString (); + } + + public string DayOfWeekAsSoloString () + { + return DayOfWeekAsString (); + } + + public string DayOfWeekAsSoloString (int idealLength) + { + // 简化:忽略 idealLength + return DayOfWeekAsString (); + } + + public string DayOfWeekAsString () + { + return _localDateTime.ToString ("dddd", GetFormatCulture ()); + } + + public string DayOfWeekAsString (int idealLength) + { + return DayOfWeekAsString (); + } + + public string EraAsString () + { + // 简化:返回纪元索引的字符串 + return Era.ToString (); + } + + public string EraAsString (int idealLength) + { + return EraAsString (); + } + + public string GetCalendarSystem () + { + if (_calendar is GregorianCalendar) return "GregorianCalendar"; + if (_calendar is HebrewCalendar) return "HebrewCalendar"; + if (_calendar is HijriCalendar) return "HijriCalendar"; + if (_calendar is JapaneseCalendar) return "JapaneseCalendar"; + if (_calendar is KoreanCalendar) return "KoreanCalendar"; + if (_calendar is TaiwanCalendar) return "TaiwanCalendar"; + if (_calendar is ThaiBuddhistCalendar) return "ThaiBuddhistCalendar"; + if (_calendar is UmAlQuraCalendar) return "UmAlQuraCalendar"; + return "GregorianCalendar"; + } + + public string GetClock () + { + return _is24HourClock ? "24HourClock" : "12HourClock"; + } + + public DateTime GetDateTime () + { + // 返回 UTC 时间 + return _utcDateTime; + } + + public string GetTimeZone () + { + return _timeZone.Id; + } + + public string HourAsPaddedString (int minDigits) + { + return Hour.ToString ().PadLeft (minDigits, '0'); + } + + public string HourAsString () + { + return Hour.ToString (); + } + + public string MinuteAsPaddedString (int minDigits) + { + return Minute.ToString ().PadLeft (minDigits, '0'); + } + + public string MinuteAsString () + { + return Minute.ToString (); + } + + public string MonthAsNumericString () + { + return Month.ToString (); + } + + public string MonthAsPaddedNumericString (int minDigits) + { + return Month.ToString ().PadLeft (minDigits, '0'); + } + + public string MonthAsSoloString () + { + return _localDateTime.ToString ("MMMM", GetFormatCulture ()); + } + + public string MonthAsSoloString (int idealLength) + { + return MonthAsSoloString (); + } + + public string MonthAsString () + { + return _localDateTime.ToString ("MMM", GetFormatCulture ()); + } + + public string MonthAsString (int idealLength) + { + return MonthAsString (); + } + + public string NanosecondAsPaddedString (int minDigits) + { + return Nanosecond.ToString ().PadLeft (minDigits, '0'); + } + + public string NanosecondAsString () + { + return Nanosecond.ToString (); + } + + public string PeriodAsString () + { + return PeriodAsString (0); + } + + public string PeriodAsString (int idealLength) + { + return _localDateTime.ToString ("tt", GetFormatCulture ()); + } + + public string SecondAsPaddedString (int minDigits) + { + return Second.ToString ().PadLeft (minDigits, '0'); + } + + public string SecondAsString () + { + return Second.ToString (); + } + + public void SetDateTime (DateTime value) + { + _utcDateTime = value.ToUniversalTime (); + UpdateLocalDateTime (); + } + + public void SetToMax () + { + SetDateTime (DateTime.MaxValue); + } + + public void SetToMin () + { + SetDateTime (DateTime.MinValue); + } + + public void SetToNow () + { + SetDateTime (DateTime.UtcNow); + } + + public string TimeZoneAsString () + { + return TimeZoneAsString (0); + } + + public string TimeZoneAsString (int idealLength) + { + // 简化:返回标准显示名称 + return _timeZone.DisplayName; + } + + public string YearAsPaddedString (int minDigits) + { + return Year.ToString ().PadLeft (minDigits, '0'); + } + + public string YearAsString () + { + return Year.ToString (); + } + + public string YearAsTruncatedString (int remainingDigits) + { + string yearStr = Year.ToString (); + if (yearStr.Length <= remainingDigits) + return yearStr; + return yearStr.Substring (yearStr.Length - remainingDigits); + } + } +} \ No newline at end of file diff --git a/DataUtils/DataUtils.csproj b/DataUtils/DataUtils.csproj index 328e189..cdd9b1a 100644 --- a/DataUtils/DataUtils.csproj +++ b/DataUtils/DataUtils.csproj @@ -45,6 +45,8 @@ + + diff --git a/DataUtils/DateTimeFormat.cs b/DataUtils/DateTimeFormat.cs new file mode 100644 index 0000000..bc5f156 --- /dev/null +++ b/DataUtils/DateTimeFormat.cs @@ -0,0 +1,494 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; + +namespace DataUtils +{ + public enum YearFormat + { + None = 0, + Default = 1, + Abbreviated = 2, + Full = 3, + } + + public enum MonthFormat + { + None = 0, + Default = 1, + Abbreviated = 2, + Full = 3, + Numeric = 4, + } + + public enum DayFormat + { + None = 0, + Default = 1, + } + + public enum DayOfWeekFormat + { + None = 0, + Default = 1, + Abbreviated = 2, + Full = 3, + } + + public enum HourFormat + { + None = 0, + Default = 1, + } + + public enum MinuteFormat + { + None = 0, + Default = 1, + } + + public enum SecondFormat + { + None = 0, + Default = 1, + } + + [ComVisible (true)] + [ClassInterface (ClassInterfaceType.AutoDual)] + public class _I_DateTimeFormatter + { + // ---------- 私有字段 ---------- + private string _formatTemplate; // 原始模板字符串 + private string _formatPattern; // 实际用于 .NET 格式化的模式 + private List _languages; // 语言优先级列表 + private string _geographicRegion; // 地理区域 + private string _calendar = "GregorianCalendar"; + private string _clock = "24HourClock"; + private string _numeralSystem = "Latn"; + private CultureInfo _culture; // 解析后的 CultureInfo + private string _resolvedLanguage; // 实际使用的语言 + private string _resolvedGeographicRegion; // 实际使用的区域 + + // 存储构造时传入的枚举值(用于 IncludeXXX 属性) + private YearFormat _yearFormat = YearFormat.None; + private MonthFormat _monthFormat = MonthFormat.None; + private DayFormat _dayFormat = DayFormat.None; + private DayOfWeekFormat _dayOfWeekFormat = DayOfWeekFormat.None; + private HourFormat _hourFormat = HourFormat.None; + private MinuteFormat _minuteFormat = MinuteFormat.None; + private SecondFormat _secondFormat = SecondFormat.None; + + // ---------- 辅助方法 ---------- + private static string MapTemplateToPattern (string template, CultureInfo culture, string clock) + { + if (string.IsNullOrEmpty (template)) + return string.Empty; + + switch (template.ToLowerInvariant ()) + { + case "longdate": + return culture.DateTimeFormat.LongDatePattern; + case "shortdate": + return culture.DateTimeFormat.ShortDatePattern; + case "longtime": + return culture.DateTimeFormat.LongTimePattern; + case "shorttime": + return culture.DateTimeFormat.ShortTimePattern; + case "dayofweek": + return culture.DateTimeFormat.ShortestDayNames? [0] ?? "dddd"; // 近似 + case "dayofweek.full": + return "dddd"; + case "dayofweek.abbreviated": + return "ddd"; + case "day": + return "dd"; + case "month": + return "MMMM"; + case "month.full": + return "MMMM"; + case "month.abbreviated": + return "MMM"; + case "month.numeric": + return "MM"; + case "year": + return "yyyy"; + case "year.full": + return "yyyy"; + case "year.abbreviated": + return "yy"; + case "hour": + return clock == "24HourClock" ? "HH" : "hh"; + case "minute": + return "mm"; + case "second": + return "ss"; + case "timezone": + return "zzz"; + default: + // 如果不是预定义模板,则当作自定义模式直接返回 + return template; + } + } + + // 根据枚举组合构建格式模式 + private static string BuildPatternFromEnums (YearFormat year, MonthFormat month, DayFormat day, DayOfWeekFormat dayOfWeek, + HourFormat hour, MinuteFormat minute, SecondFormat second, + string clock, CultureInfo culture) + { + var parts = new List (); + // 日期部分 + if (dayOfWeek != DayOfWeekFormat.None) + { + if (dayOfWeek == DayOfWeekFormat.Abbreviated) + parts.Add ("ddd"); + else // Full or Default + parts.Add ("dddd"); + } + if (year != YearFormat.None) + { + if (year == YearFormat.Abbreviated) + parts.Add ("yy"); + else + parts.Add ("yyyy"); + } + if (month != MonthFormat.None) + { + switch (month) + { + case MonthFormat.Numeric: + parts.Add ("MM"); + break; + case MonthFormat.Abbreviated: + parts.Add ("MMM"); + break; + case MonthFormat.Full: + parts.Add ("MMMM"); + break; + default: + parts.Add ("MM"); + break; + } + } + if (day != DayFormat.None) + { + parts.Add ("dd"); + } + string datePart = string.Join (" ", parts); + // 时间部分 + var timeParts = new List (); + if (hour != HourFormat.None) + { + if (clock == "24HourClock") + timeParts.Add ("HH"); + else + timeParts.Add ("hh"); + } + if (minute != MinuteFormat.None) + { + timeParts.Add ("mm"); + } + if (second != SecondFormat.None) + { + timeParts.Add ("ss"); + } + string timePart = timeParts.Count > 0 ? string.Join (":", timeParts) : ""; + if (!string.IsNullOrEmpty (datePart) && !string.IsNullOrEmpty (timePart)) + return datePart + " " + timePart; + if (!string.IsNullOrEmpty (datePart)) + return datePart; + return timePart; + } + + // 将 JS Date 对象转换为 DateTime + private DateTime ConvertJsDateToDateTime (object jsDate) + { + if (jsDate == null) + throw new ArgumentNullException (nameof (jsDate)); + + Type type = jsDate.GetType (); + // 调用 getTime() 获取毫秒数 (double) + double milliseconds = (double)type.InvokeMember ( + "getTime", + BindingFlags.InvokeMethod, + null, + jsDate, + null); + + // 手动计算 Unix 纪元转换(兼容低版本 .NET) + DateTime epoch = new DateTime (1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + DateTime utcDateTime = epoch.AddMilliseconds (milliseconds); + // 返回本地时间(可根据需求调整) + return utcDateTime.ToLocalTime (); + } + + // 初始化文化信息 + private void InitializeCulture () + { + string lang = (_languages != null && _languages.Count > 0) ? _languages [0] : CultureInfo.CurrentCulture.Name; + try + { + _culture = new CultureInfo (lang); + _resolvedLanguage = _culture.Name; + } + catch + { + _culture = CultureInfo.CurrentCulture; + _resolvedLanguage = _culture.Name; + } + _resolvedGeographicRegion = _geographicRegion ?? _culture.Name; + // 根据区域和语言更新格式模式(如果使用模板) + if (!string.IsNullOrEmpty (_formatTemplate)) + { + _formatPattern = MapTemplateToPattern (_formatTemplate, _culture, _clock); + } + else if (_yearFormat != YearFormat.None || _monthFormat != MonthFormat.None || + _dayFormat != DayFormat.None || _dayOfWeekFormat != DayOfWeekFormat.None || + _hourFormat != HourFormat.None || _minuteFormat != MinuteFormat.None || + _secondFormat != SecondFormat.None) + { + _formatPattern = BuildPatternFromEnums (_yearFormat, _monthFormat, _dayFormat, _dayOfWeekFormat, + _hourFormat, _minuteFormat, _secondFormat, _clock, _culture); + } + } + + // ---------- 构造函数 ---------- + public _I_DateTimeFormatter (string formatTemplate) + : this (formatTemplate, null, null, null, null) + { + } + + public _I_DateTimeFormatter (string formatTemplate, IEnumerable languages) + : this (formatTemplate, languages, null, null, null) + { + } + + public _I_DateTimeFormatter (string formatTemplate, IEnumerable languages, string geographicRegion, string calendar, string clock) + { + _formatTemplate = formatTemplate; + _languages = languages == null ? new List () : new List (languages); + _geographicRegion = geographicRegion; + if (!string.IsNullOrEmpty (calendar)) + _calendar = calendar; + if (!string.IsNullOrEmpty (clock)) + _clock = clock; + InitializeCulture (); + } + + public _I_DateTimeFormatter (YearFormat year, MonthFormat month, DayFormat day, DayOfWeekFormat dayOfWeek) + : this (year, month, day, dayOfWeek, HourFormat.None, MinuteFormat.None, SecondFormat.None, null, null, null, null) + { + } + + public _I_DateTimeFormatter (HourFormat hour, MinuteFormat minute, SecondFormat second) + : this (YearFormat.None, MonthFormat.None, DayFormat.None, DayOfWeekFormat.None, hour, minute, second, null, null, null, null) + { + } + + public _I_DateTimeFormatter (YearFormat year, MonthFormat month, DayFormat day, DayOfWeekFormat dayOfWeek, + HourFormat hour, MinuteFormat minute, SecondFormat second, + IEnumerable languages) + : this (year, month, day, dayOfWeek, hour, minute, second, languages, null, null, null) + { + } + + public _I_DateTimeFormatter (YearFormat year, MonthFormat month, DayFormat day, DayOfWeekFormat dayOfWeek, + HourFormat hour, MinuteFormat minute, SecondFormat second, + IEnumerable languages, string geographicRegion, string calendar, string clock) + { + _yearFormat = year; + _monthFormat = month; + _dayFormat = day; + _dayOfWeekFormat = dayOfWeek; + _hourFormat = hour; + _minuteFormat = minute; + _secondFormat = second; + _languages = languages == null ? new List () : new List (languages); + _geographicRegion = geographicRegion; + if (!string.IsNullOrEmpty (calendar)) + _calendar = calendar; + if (!string.IsNullOrEmpty (clock)) + _clock = clock; + InitializeCulture (); + } + + // ---------- 属性 ---------- + public string Calendar + { + get { return _calendar; } + set + { + _calendar = value; + // 日历更改可能需要重新初始化模式,这里简化处理 + } + } + + public string Clock + { + get { return _clock; } + set + { + _clock = value; + InitializeCulture (); // 重新生成模式(因为小时格式可能变化) + } + } + + public string GeographicRegion + { + get { return _geographicRegion; } + set + { + _geographicRegion = value; + InitializeCulture (); + } + } + + public int IncludeDay + { + get { return (int)_dayFormat; } + } + + public int IncludeDayOfWeek + { + get { return (int)_dayOfWeekFormat; } + } + + public int IncludeHour + { + get { return (int)_hourFormat; } + } + + public int IncludeMinute + { + get { return (int)_minuteFormat; } + } + + public int IncludeMonth + { + get { return (int)_monthFormat; } + } + + public int IncludeSecond + { + get { return (int)_secondFormat; } + } + + public int IncludeYear + { + get { return (int)_yearFormat; } + } + + public _I_List Languages + { + get { return new _I_List (_languages.AsReadOnly ().Select (e => (object)e), true); } + } + + public static _I_DateTimeFormatter LongDate + { + get + { + return new _I_DateTimeFormatter ("longdate"); + } + } + + public static _I_DateTimeFormatter LongTime + { + get + { + return new _I_DateTimeFormatter ("longtime"); + } + } + + public string NumeralSystem + { + get { return _numeralSystem; } + set { _numeralSystem = value; } + } + + public _I_List Patterns + { + get + { + return new _I_List (new List { _formatPattern }.AsReadOnly ().Select (e => (object)e), true); + } + } + + public string ResolvedGeographicRegion + { + get { return _resolvedGeographicRegion; } + } + + public string ResolvedLanguage + { + get { return _resolvedLanguage; } + } + + public static _I_DateTimeFormatter ShortDate + { + get + { + return new _I_DateTimeFormatter ("shortdate"); + } + } + + public static _I_DateTimeFormatter ShortTime + { + get + { + return new _I_DateTimeFormatter ("shorttime"); + } + } + + public string Template + { + get + { + if (!string.IsNullOrEmpty (_formatTemplate)) + return _formatTemplate; + // 从枚举组合生成模板描述(简化) + return _formatPattern; + } + } + + // ---------- 方法 ---------- + public string FormatC (DateTime dateTime) + { + return dateTime.ToString (_formatPattern, _culture); + } + + public string FormatC (DateTime dateTime, string timeZoneId) + { + if (!string.IsNullOrEmpty (timeZoneId)) + { + try + { + TimeZoneInfo tzi = TimeZoneInfo.FindSystemTimeZoneById (timeZoneId); + DateTime targetTime = TimeZoneInfo.ConvertTime (dateTime, tzi); + return targetTime.ToString (_formatPattern, _culture); + } + catch + { + // 时区无效,回退到原始时间 + return dateTime.ToString (_formatPattern, _culture); + } + } + return dateTime.ToString (_formatPattern, _culture); + } + + // 为方便 JS 调用,提供接受 object 的重载(自动识别 JS Date) + public string Format (object jsDate) + { + DateTime dt = ConvertJsDateToDateTime (jsDate); + return FormatC (dt); + } + + public string FormatWithTimeZone (object jsDate, string timeZoneId) + { + DateTime dt = ConvertJsDateToDateTime (jsDate); + return FormatC (dt, timeZoneId); + } + } +} diff --git a/DataUtils/Enumerable.cs b/DataUtils/Enumerable.cs index a951823..844d40a 100644 --- a/DataUtils/Enumerable.cs +++ b/DataUtils/Enumerable.cs @@ -10,7 +10,7 @@ namespace DataUtils { [ComVisible (true)] [InterfaceType (ComInterfaceType.InterfaceIsDual)] - public interface _I_Enumerable + public interface _I_Enumerable: IDisposable { int Length { get; set; } object this [int index] { get; set; } @@ -33,6 +33,9 @@ namespace DataUtils int IndexOfKey (int key); // 按内部 key 查找 void Move (int index, int newIndex); // 移动元素 void PushAll (object [] items); // 一次性 push 多个 + object Get (int index); + object Set (int index, object value); + object At (int index); } public class _I_List: _I_Enumerable, IList { @@ -43,6 +46,9 @@ namespace DataUtils IsReadOnly = readOnly; IsSynchronized = sync; } + public _I_List (bool readOnly = false, bool fixedSize = false, bool sync = true) : + this (null, readOnly, fixedSize, sync) + { } protected List _list; protected object _lock = new object (); public object this [int index] { get { return _list [index]; } set { _list [index] = value; } } @@ -155,5 +161,13 @@ namespace DataUtils return first; } public void Unshift (object value) => _list.Insert (0, value); + public void Dispose () + { + _list?.Clear (); + _list = null; + } + public object Get (int index) => this [index]; + public object Set (int index, object value) => this [index] = value; + public object At (int index) => this [index]; } } diff --git a/DataUtils/Locale.cs b/DataUtils/Locale.cs index a5ce047..e641203 100644 --- a/DataUtils/Locale.cs +++ b/DataUtils/Locale.cs @@ -1,29 +1,116 @@ using System; +using System.Collections.Generic; using System.Globalization; +using System.Linq; using System.Runtime.InteropServices; using System.Text; +using System.Text.RegularExpressions; namespace DataUtils { internal static class NativeMethods { + public const int LOCALE_SSHORTESTSCRIPT = 0x0000004F; // 获取四字母脚本代码 + public const uint KLF_ACTIVATE = 0x00000001; // 激活键盘布局 // 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); + [DllImport ("user32.dll")] + public static extern IntPtr GetKeyboardLayout (uint dwLayout); + [DllImport ("user32.dll", CharSet = CharSet.Auto)] + public static extern IntPtr LoadKeyboardLayout (string pwszKLID, uint Flags); } + [ComVisible (true)] + [ClassInterface (ClassInterfaceType.AutoDual)] + public class _I_Language: System.Globalization.CultureInfo + { + public _I_Language (string localeName) : base (localeName) { } + public string AbbreviatedName => this.ThreeLetterISOLanguageName; + public string LanguageTag => this.IetfLanguageTag ?? this.Name; + public int LayoutDirection + { + get + { + if (base.TextInfo.IsRightToLeft) return 1; + string tag = this.LanguageTag; + bool isVerticalCandidate = false; + if (tag != null) + { + var scriptMatch = Regex.Match (tag, @"-([A-Za-z]{4})(?:-|$)"); + if (scriptMatch.Success) + { + string script = scriptMatch.Groups [1].Value; + if (script == "Hani" || script == "Hira" || script == "Kana" || script == "Jpan" || script == "Kore" || script == "Hans" || script == "Hant") + isVerticalCandidate = true; + } + if (!isVerticalCandidate) + { + var regionMatch = Regex.Match (tag, @"-([A-Za-z]{2})$"); + if (regionMatch.Success) + { + string region = regionMatch.Groups [1].Value.ToUpperInvariant (); + if (region == "JP" || region == "CN" || region == "TW" || region == "HK" || region == "MO" || region == "KR") + isVerticalCandidate = true; + } + } + } + if (isVerticalCandidate) + { + return 2; + } + return 0; + } + } + public string Script + { + get + { + StringBuilder sb = new StringBuilder (10); + if (NativeMethods.GetLocaleInfoEx (this.Name, NativeMethods.LOCALE_SSHORTESTSCRIPT, sb, sb.Capacity) > 0) + return sb.ToString (); + // 如果失败,尝试从语言标记中解析脚本子标记(如 "zh-Hans-CN" 中的 "Hans") + var match = Regex.Match (this.Name, @"-([A-Za-z]{4})(?:-|$)"); + if (match.Success) + return match.Groups [1].Value; + + return "Unknown"; + } + } + public _I_List GetExtensionSubtags (string singleton) + { + if (string.IsNullOrEmpty (singleton) || singleton.Length != 1) + throw new ArgumentException ("Singleton must be a single character", nameof (singleton)); + var subtags = new List (); + string tag = this.LanguageTag; + string pattern = $@"-{Regex.Escape (singleton)}-([a-zA-Z0-9](?:-[a-zA-Z0-9]+)*)"; + var match = Regex.Match (tag, pattern); + if (match.Success) + { + string extPart = match.Groups [1].Value; + subtags.AddRange (extPart.Split ('-')); + } + return new _I_List (subtags.Select (i => (object)i)); + } + public bool TrySetInputMethodLanguageTag (string languageTag) + { + int lcid = NativeMethods.LocaleNameToLCID (languageTag, 0); + if (lcid == 0) + return false; + string klid = $"{lcid:X8}"; + IntPtr hkl = NativeMethods.LoadKeyboardLayout (klid, NativeMethods.KLF_ACTIVATE); + return hkl != IntPtr.Zero; + } + } [ComVisible (true)] [ClassInterface (ClassInterfaceType.AutoDual)] public class _I_Locale @@ -47,7 +134,6 @@ namespace DataUtils } } } - // Current LCID (int) public int CurrentLCID { @@ -63,7 +149,6 @@ namespace DataUtils } } } - // Convert LCID -> locale name (e.g. 1033 -> "en-US") public string ToLocaleName (int lcid) { @@ -87,7 +172,6 @@ namespace DataUtils } return string.Empty; } - // Convert locale name -> LCID public int ToLCID (string localeName) { @@ -111,7 +195,6 @@ namespace DataUtils // 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) @@ -177,7 +260,6 @@ namespace DataUtils 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) @@ -239,7 +321,6 @@ namespace DataUtils return 0; } } - // Helpers similar to the C++: restricted (language) and elaborated (region) codes public string GetLocaleRestrictedCode (string localeName) { @@ -257,7 +338,6 @@ namespace DataUtils return string.Empty; } } - public string GetLocaleElaboratedCode (string localeName) { if (string.IsNullOrEmpty (localeName)) localeName = CurrentLocale; @@ -284,7 +364,6 @@ namespace DataUtils } return string.Empty; } - // LCID -> combined code like "en-US" (with configurable separator) public string LcidToLocaleCode (int lcid) { @@ -304,7 +383,6 @@ namespace DataUtils } return string.Empty; } - // Get the user default locale name public string GetUserDefaultLocaleName () { @@ -317,7 +395,6 @@ namespace DataUtils catch { } return LcidToLocaleCode (CultureInfo.CurrentCulture.LCID); } - // Get system default locale name (machine) public string GetSystemDefaultLocaleName () { @@ -330,7 +407,6 @@ namespace DataUtils catch { } return LcidToLocaleCode (CultureInfo.InstalledUICulture.LCID); } - // Get computer locale code similar to C++ approach public string GetComputerLocaleCode () { @@ -350,7 +426,38 @@ namespace DataUtils // fallback to invariant return CultureInfo.InvariantCulture.Name ?? string.Empty; } - + public _I_List RecommendLocaleNames + { + get + { + var arr = new string [] { + System.Threading.Thread.CurrentThread.CurrentCulture.Name, + GetUserDefaultLocaleName (), + GetSystemDefaultLocaleName (), + LcidToLocaleCode (CurrentLCID), + GetLocaleRestrictedCode (System.Threading.Thread.CurrentThread.CurrentCulture.Name), + GetLocaleRestrictedCode (GetUserDefaultLocaleName ()), + GetLocaleRestrictedCode (GetSystemDefaultLocaleName ()), + "en-US", + "en" + }; + var list = new _I_List (); + foreach (var loc in arr) + { + var lloc = loc.Trim ().ToLowerInvariant (); + var isfind = false; + foreach (var item in list) + { + var str = item as string; + if (string.IsNullOrWhiteSpace (str)) isfind = true; + isfind = str.Trim ().ToLowerInvariant () == lloc; + if (isfind) break; + } + if (!isfind) list.Add (loc); + } + return list; + } + } // Compare two locale names; returns true if equal by name or LCID public bool LocaleNameCompare (string left, string right) { @@ -366,8 +473,62 @@ namespace DataUtils return false; } } - // Constants private const int LOCALE_NAME_MAX_LENGTH = 85; // defined by Windows + public _I_Language CreateLanguage (string localeName) => new _I_Language (localeName); + public static string CurrentInputMethodLanguageTag + { + get + { + IntPtr hkl = NativeMethods.GetKeyboardLayout (0); + int lcid = hkl.ToInt32 () & 0xFFFF; + + StringBuilder sb = new StringBuilder (85); + int result = NativeMethods.LCIDToLocaleName (lcid, sb, sb.Capacity, 0); + if (result > 0) + return sb.ToString (); + + return null; + } + } + public static bool IsWellFormed (string languageTag) + { + if (string.IsNullOrEmpty (languageTag)) + return false; + + try + { + var _ = new CultureInfo (languageTag); + return true; + } + catch + { + return false; + } + } + public static _I_List GetMuiCompatibleLanguageListFromLanguageTags (IEnumerable languageTags) + { + var result = new List (); + foreach (string tag in languageTags) + { + if (string.IsNullOrEmpty (tag)) + continue; + result.Add (tag); + try + { + var ci = new CultureInfo (tag); + string parent = ci.Parent.Name; + if (!string.IsNullOrEmpty (parent) && parent != tag && !result.Contains (parent)) + result.Add (parent); + } + catch { } + string neutral = Regex.Replace (tag, @"-.*$", ""); + if (neutral != tag && !result.Contains (neutral)) + result.Add (neutral); + } + if (!result.Contains ("neutral")) + result.Add ("neutral"); + return new _I_List (result.Select (t => (object)t)); + } } } diff --git a/DataUtils/Utils.cs b/DataUtils/Utils.cs index bf01c33..03b9692 100644 --- a/DataUtils/Utils.cs +++ b/DataUtils/Utils.cs @@ -1,9 +1,11 @@ using System; +using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; +using System.Runtime.Serialization; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -382,5 +384,236 @@ namespace DataUtils } [ComVisible (true)] [ClassInterface (ClassInterfaceType.AutoDual)] - public class _I_Exception: IExcep + public class _I_Exception: Exception, IDisposable + { + private Exception bex = null; + public _I_Exception (Exception ex) { bex = ex; } + public _I_Exception (string message) { bex = new Exception (message); } + public _I_Exception (string msg, Exception innerEx) { bex = new Exception (msg, innerEx); } + public override IDictionary Data => bex.Data; + public override Exception GetBaseException () => bex.GetBaseException (); + public override void GetObjectData (SerializationInfo info, StreamingContext context) => bex.GetObjectData (info, context); + public override string HelpLink + { + get { return bex.HelpLink; } + set { bex.HelpLink = value; } + } + public override string Message => bex.Message; + public override string Source + { + get { return bex.Source; } + set { bex.Source = value; } + } + public override string StackTrace => bex.StackTrace; + public override string ToString () => bex.ToString (); + public override int GetHashCode () => bex.GetHashCode (); + public override bool Equals (object obj) => bex.Equals (obj); + public void Dispose () { bex = null; } + } + [ComVisible (true)] + [ClassInterface (ClassInterfaceType.AutoDual)] + public class _I_KeyValuePair: IDisposable + { + object key = null; + object value = null; + public _I_KeyValuePair (object k, object v) + { + key = k; + value = v; + } + public object Key { get { return key; } set { key = value; } } + public object Value { get { return value; } set { this.value = value; } } + public void Dispose () + { + key = null; + value = null; + } + ~_I_KeyValuePair () + { + key = null; + value = null; + } + } + [ComVisible (true)] + [ClassInterface (ClassInterfaceType.AutoDual)] + public class _I_WwwFormUrlDecoder + { + private readonly Dictionary _params = new Dictionary (); + public _I_WwwFormUrlDecoder (string query) + { + if (string.IsNullOrEmpty (query)) return; + if (query.StartsWith ("?")) query = query.Substring (1); + foreach (var pair in query.Split ('&')) + { + var kv = pair.Split ('='); + if (kv.Length == 2) + _params [Uri.UnescapeDataString (kv [0])] = Uri.UnescapeDataString (kv [1]); + } + } + public string GetFirstValueByName (string name) + { + string value = null; + return _params.TryGetValue (name, out value) ? value : null; + } + public int Size => _params.Count; + public _I_KeyValuePair GetAt (uint index) + { + var pair = _params.ElementAt ((int)index); + return new _I_KeyValuePair (pair.Key, pair.Value); + } + } + [ComVisible (true)] + [ClassInterface (ClassInterfaceType.AutoDual)] + public class _I_Uri: Uri + { + public _I_Uri (string uri): base (uri) { } + public _I_Uri (string baseUri, string relativeUri) : base (new Uri (baseUri), relativeUri) { } + public string AbsoluteCanonicalUri => this.GetLeftPart (UriPartial.Authority) + this.PathAndQuery + this.Fragment; + public string DisplayIri => Uri.UnescapeDataString (this.OriginalString); + public string DisplayUri => Uri.UnescapeDataString (this.OriginalString); + public string Domain => ExtractDomain (this.Host); + public string Extension => System.IO.Path.GetExtension (this.AbsolutePath)?.TrimStart ('.') ?? ""; + public string Password => ExtractPassword (this.UserInfo); + public string Path => this.AbsolutePath; + public object QueryParsed => new _I_WwwFormUrlDecoder (this.Query); + public string RawUri => this.OriginalString; + public string SchemeName => this.Scheme; + public bool Suspicious => !Uri.IsWellFormedUriString (this.OriginalString, UriKind.Absolute); + public string UserName => ExtractUserName (this.UserInfo); + public _I_Uri CombineUri (string relativeUri) + { + return new _I_Uri (this.AbsoluteUri, relativeUri); + } + public static string EscapeComponent (string component) + { + return Uri.EscapeDataString (component); + } + public static string UnescapeComponent (string component) + { + return Uri.UnescapeDataString (component); + } + private static string ExtractDomain (string host) + { + var parts = host.Split ('.'); + if (parts.Length >= 2) + return string.Join (".", parts.Skip (1)); + return host; + } + private static string ExtractUserName (string userInfo) + { + if (string.IsNullOrEmpty (userInfo)) return ""; + var parts = userInfo.Split (':'); + return parts [0]; + } + private static string ExtractPassword (string userInfo) + { + if (string.IsNullOrEmpty (userInfo)) return ""; + var parts = userInfo.Split (':'); + return parts.Length > 1 ? parts [1] : ""; + } + } + [ComVisible (true)] + [ClassInterface (ClassInterfaceType.AutoDual)] + public class _I_Utilities + { + public _I_Uri CreateUri (string uri) => new _I_Uri (uri); + public _I_Uri CreateUri2 (string baseUri, string relaUri) => new _I_Uri (baseUri, relaUri); + public _I_Exception CreateException (string message) => new _I_Exception (message); + public _I_Exception CreateException2 (string message, _I_Exception innerEx) => new _I_Exception (message, innerEx); + public _I_Calendar CreateCalendar () => new _I_Calendar (); + public _I_Calendar CreateCalendar2 (object list) => new _I_Calendar (list); + public _I_Calendar CreateCalendar3 (object list, string arg1, string arg2) => new _I_Calendar (list, arg1, arg2); + public _I_Calendar CreateCalendar4 (object list, string arg1, string arg2, string arg3) => new _I_Calendar (list, arg1, arg2, arg3); + public _I_DateTimeFormatter CreateDateTimeFormatterFromTemplate (string formatTemplate) + { + return new _I_DateTimeFormatter (formatTemplate); + } + public _I_DateTimeFormatter CreateDateTimeFormatterFromTemplateAndLanguages (string formatTemplate, object languagesArray) + { + List languages = JsArrayToStringList (languagesArray); + return new _I_DateTimeFormatter (formatTemplate, languages); + } + public _I_DateTimeFormatter CreateDateTimeFormatterFromTemplateFull (string formatTemplate, object languagesArray, + string geographicRegion, string calendar, string clock) + { + List languages = JsArrayToStringList (languagesArray); + return new _I_DateTimeFormatter (formatTemplate, languages, geographicRegion, calendar, clock); + } + public _I_DateTimeFormatter CreateDateTimeFormatterFromDateEnums (int year, int month, int day, int dayOfWeek) + { + return new _I_DateTimeFormatter ( + (YearFormat)year, + (MonthFormat)month, + (DayFormat)day, + (DayOfWeekFormat)dayOfWeek + ); + } + public _I_DateTimeFormatter CreateDateTimeFormatterFromTimeEnums (int hour, int minute, int second) + { + return new _I_DateTimeFormatter ( + (HourFormat)hour, + (MinuteFormat)minute, + (SecondFormat)second + ); + } + public _I_DateTimeFormatter CreateDateTimeFormatterFromDateTimeEnums (int year, int month, int day, int dayOfWeek, + int hour, int minute, int second, object languagesArray) + { + List languages = JsArrayToStringList (languagesArray); + return new _I_DateTimeFormatter ( + (YearFormat)year, + (MonthFormat)month, + (DayFormat)day, + (DayOfWeekFormat)dayOfWeek, + (HourFormat)hour, + (MinuteFormat)minute, + (SecondFormat)second, + languages + ); + } + public _I_DateTimeFormatter CreateDateTimeFormatterFromDateTimeEnumsFull (int year, int month, int day, int dayOfWeek, + int hour, int minute, int second, object languagesArray, + string geographicRegion, string calendar, string clock) + { + List languages = JsArrayToStringList (languagesArray); + return new _I_DateTimeFormatter ( + (YearFormat)year, + (MonthFormat)month, + (DayFormat)day, + (DayOfWeekFormat)dayOfWeek, + (HourFormat)hour, + (MinuteFormat)minute, + (SecondFormat)second, + languages, + geographicRegion, + calendar, + clock + ); + } + private List JsArrayToStringList (object jsArray) + { + var result = new List (); + if (jsArray == null) return result; + + Type type = jsArray.GetType (); + try + { + int length = (int)type.InvokeMember ("length", BindingFlags.GetProperty, null, jsArray, null); + for (int i = 0; i < length; i++) + { + object value = type.InvokeMember (i.ToString (), BindingFlags.GetProperty, null, jsArray, null); + if (value != null) + result.Add (value.ToString ()); + } + } + catch + { + // 如果无法获取 length,则假设是单个字符串 + string single = jsArray.ToString (); + if (!string.IsNullOrEmpty (single)) + result.Add (single); + } + return result; + } + } } diff --git a/DataUtils/Web.cs b/DataUtils/Web.cs index 9d2af40..e2fbd50 100644 --- a/DataUtils/Web.cs +++ b/DataUtils/Web.cs @@ -297,11 +297,18 @@ namespace DataUtils return new HttpResponse (res); } } - public void SendAsync (string sBody, string encoding, object pfResolve) + public void SendAsync (string sBody, string encoding, object pfResolve, object pfReject) { System.Threading.ThreadPool.QueueUserWorkItem (delegate { - JsUtils.Call (pfResolve, Send (sBody, encoding)); + try + { + JsUtils.Call (pfResolve, Send (sBody, encoding)); + } + catch (Exception ex) + { + JsUtils.Call (pfReject, new _I_Exception (ex)); + } }); } public void Dispose () { } diff --git a/LICENSE.SharpZipLib b/LICENSE.SharpZipLib new file mode 100644 index 0000000..f4597ac --- /dev/null +++ b/LICENSE.SharpZipLib @@ -0,0 +1,17 @@ +Copyright © 2000-2018 SharpZipLib Contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy of this +software and associated documentation files (the "Software"), to deal in the Software +without restriction, including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/Reader/Program.cs b/Reader/Program.cs index ce137b7..3530406 100644 --- a/Reader/Program.cs +++ b/Reader/Program.cs @@ -18,10 +18,6 @@ namespace Reader static void Main () { Directory.SetCurrentDirectory (AppDomain.CurrentDomain.BaseDirectory); - AppxPackage.PackageReader.AddApplicationItem ("SmallLogo"); - AppxPackage.PackageReader.AddApplicationItem ("Square30x30Logo"); - AppxPackage.PackageReader.AddApplicationItem ("Logo"); - AppxPackage.PackageReader.AddApplicationItem ("Square44x44Logo"); DataUtils.BrowserEmulation.SetWebBrowserEmulation (); Application.EnableVisualStyles (); Application.SetCompatibleTextRenderingDefault (false); diff --git a/Reader/Project2.ico b/Reader/Project2.ico new file mode 100644 index 0000000..6056912 Binary files /dev/null and b/Reader/Project2.ico differ diff --git a/Reader/Reader.csproj b/Reader/Reader.csproj index 0595c06..a0c5701 100644 --- a/Reader/Reader.csproj +++ b/Reader/Reader.csproj @@ -71,6 +71,9 @@ prompt MinimumRecommendedRules.ruleset + + Project2.ico + ..\packages\Newtonsoft.Json.13.0.3\lib\net40\Newtonsoft.Json.dll @@ -170,6 +173,9 @@ false + + +