diff --git a/Apewer.Windows/Internals/Interop/Kernel32.cs b/Apewer.Windows/Internals/Interop/Kernel32.cs
index 7cb39ed..3b2ad7b 100644
--- a/Apewer.Windows/Internals/Interop/Kernel32.cs
+++ b/Apewer.Windows/Internals/Interop/Kernel32.cs
@@ -43,6 +43,9 @@ namespace Apewer.Internals.Interop
[DllImport("kernel32.dll", ExactSpelling = true)]
public static extern IntPtr GetCurrentProcess();
+ [DllImport("kernel32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
+ public static extern int GetCurrentThreadId();
+
[DllImport("kernel32")]
public static extern int GetShortPathName(string lpszLongPath, string lpszShortPath, int cchBuffer);
diff --git a/Apewer.Windows/Internals/Interop/User32.cs b/Apewer.Windows/Internals/Interop/User32.cs
index 8a67a21..d217567 100644
--- a/Apewer.Windows/Internals/Interop/User32.cs
+++ b/Apewer.Windows/Internals/Interop/User32.cs
@@ -125,6 +125,9 @@ namespace Apewer.Internals.Interop
[DllImport("user32.dll")]
public static extern int GetWindowTextW(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr)] System.Text.StringBuilder lpString, int nMaxCount);
+ [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
+ public static extern int GetWindowThreadProcessId(HandleRef hWnd, out int lpdwProcessId);
+
///
///
///
diff --git a/Apewer.Windows/Surface/Extensions.cs b/Apewer.Windows/Surface/Extensions.cs
deleted file mode 100644
index 480930b..0000000
--- a/Apewer.Windows/Surface/Extensions.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-#if NET40 || NET461
-
-using System;
-using System.Collections.Generic;
-using System.Drawing;
-using System.Drawing.Imaging;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Windows.Forms;
-
-namespace Apewer.Surface
-{
-
- ///
- public static class Extensions
- {
-
- /// 保存为 PNG 文件。
- public static byte[] SaveAsPng(this Image @this, bool disposeImage = false)
- {
- return ImageUtility.SaveAsBytes(@this, ImageFormat.Png, disposeImage);
- }
-
- /// 保存为 JPEG 文件。
- public static byte[] SaveAsJpeg(this Image @this, bool disposeImage = false)
- {
- return ImageUtility.SaveAsBytes(@this, ImageFormat.Jpeg, disposeImage);
- }
-
- ///
- public static void BeginInvoke(this Control control, Action action)
- {
- if (action == null) return;
- control.BeginInvoke(action as Delegate);
- }
-
- ///
- public static void Invoke(this Control control, Action action)
- {
- if (action == null) return;
- control.Invoke(action as Delegate);
- }
-
- }
-
-}
-
-#endif
diff --git a/Apewer.Windows/Surface/FormsUtility.cs b/Apewer.Windows/Surface/FormsUtility.cs
index 69e78b6..4a12d8c 100644
--- a/Apewer.Windows/Surface/FormsUtility.cs
+++ b/Apewer.Windows/Surface/FormsUtility.cs
@@ -23,7 +23,7 @@ namespace Apewer.Surface
/// 窗体实用工具。
[SecuritySafeCritical]
- public class FormsUtility
+ public static class FormsUtility
{
/// 线程锁。
@@ -76,6 +76,50 @@ namespace Apewer.Surface
#endif
+ #region 线程
+
+ /// 在拥有此控件的基础窗口句柄的线程上执行指定的委托。
+ public static void Invoke(this Control control, Action action)
+ {
+ if (control == null) throw new ArgumentNullException(nameof(control));
+ if (action == null) throw new ArgumentNullException(nameof(action));
+
+ // control.Invoke(action as Delegate);
+
+ control.Invoke(new Action(delegate ()
+ {
+ action.Invoke();
+ }));
+ }
+
+ /// 在创建控件的基础句柄所在线程上异步执行指定委托。
+ ///
+ public static IAsyncResult BeginInvoke(this Control control, Action action)
+ {
+ if (control == null) throw new ArgumentNullException(nameof(control));
+ if (action == null) throw new ArgumentNullException(nameof(action));
+
+ // control.BeginInvoke(action as Delegate);
+
+ return control.BeginInvoke(new Action(delegate ()
+ {
+ action.Invoke();
+ }));
+ }
+
+ /// 控件属于当前线程。
+ ///
+ public static bool OnCurrentThread(Control control)
+ {
+ if (control == null) throw new ArgumentNullException(nameof(control));
+
+ var controlThreadId = User32.GetWindowThreadProcessId(new HandleRef(control, control.Handle), out int _);
+ var currentThreadId = Kernel32.GetCurrentThreadId();
+ return controlThreadId == currentThreadId;
+ }
+
+ #endregion
+
#region 颜色。
/// 获取所有可枚举的颜色。
@@ -603,38 +647,6 @@ namespace Apewer.Surface
current.Visible = false;
}
- ///
- public static void Invoke(Control control, Action action, bool async = false)
- {
- if (control == null || action == null) return;
- // control.Invoke(action);
- if (async)
- {
- control.BeginInvoke(new Action(delegate ()
- {
- action.Invoke();
- }));
- }
- else
- {
- control.Invoke(new Action(delegate ()
- {
- action.Invoke();
- }));
- }
- }
-
- ///
- public static void BeginInvoke(Control control, Action action)
- {
- if (control == null || action == null) return;
-
- control.BeginInvoke(new Action(delegate ()
- {
- action.Invoke();
- }));
- }
-
/// 设置窗体置顶。
public static void SetTopMost(IntPtr form, bool value = true)
{
@@ -1041,6 +1053,86 @@ namespace Apewer.Surface
#endregion
+ #region WndProc
+
+ /// 允许鼠标调整窗体大小。此方法对 FormBorderStyle 为 None 的窗体生效。
+ /// 已处理事件。
+ ///
+ ///
+ public static bool AllowResizeForm(this Form form, ref Message m, int padding = 4)
+ {
+ if (form == null) throw new ArgumentNullException(nameof(form));
+ if (form.FormBorderStyle != FormBorderStyle.None) return false;
+
+ if (padding < 0) throw new ArgumentOutOfRangeException(nameof(padding));
+
+ const int HT_LEFT = 10;
+ const int HT_RIGHT = 11;
+ const int HT_TOP = 12;
+ const int HT_TOP_LEFT = 13;
+ const int HT_TOP_RIGHT = 14;
+ const int HT_BOTTOM = 15;
+ const int HT_BOTTOM_LEFT = 16;
+ const int HT_BOTTOM_RIGHT = 17;
+
+ switch (m.Msg)
+ {
+ case 0x0084:
+ var clientSize = form.ClientSize;
+ var screenPoint = new System.Drawing.Point((int)m.LParam & 0xFFFF, (int)m.LParam >> 16 & 0xFFFF);
+ var point = form.PointToClient(screenPoint);
+ if (point.X <= padding)
+ {
+ if (point.Y <= padding) m.Result = (IntPtr)HT_TOP_LEFT;
+ else if (point.Y >= clientSize.Height - padding) m.Result = (IntPtr)HT_BOTTOM_LEFT;
+ else m.Result = (IntPtr)HT_LEFT;
+ return true;
+ }
+ else if (point.X >= clientSize.Width - padding)
+ {
+ if (point.Y <= padding) m.Result = (IntPtr)HT_TOP_RIGHT;
+ else if (point.Y >= clientSize.Height - padding) m.Result = (IntPtr)HT_BOTTOM_RIGHT;
+ else m.Result = (IntPtr)HT_RIGHT;
+ return true;
+ }
+ else if (point.Y <= padding)
+ {
+ m.Result = (IntPtr)HT_TOP;
+ return true;
+ }
+ else if (point.Y >= clientSize.Height - padding)
+ {
+ m.Result = (IntPtr)HT_BOTTOM;
+ return true;
+ }
+ break;
+ }
+
+ return false;
+ }
+
+ /// 允许移动窗体。左键点击时修改消息,认为鼠标点在非客户区(标题栏)。
+ /// 已处理事件。
+ ///
+ public static bool AllowMoveForm(this Form form, ref Message m)
+ {
+ if (form == null) throw new ArgumentNullException(nameof(form));
+
+ switch (m.Msg)
+ {
+ // 左键点击时修改消息,认为鼠标点在非客户区(标题栏)。
+ case 0x0201:
+ m.Msg = 0x00A1;
+ m.LParam = IntPtr.Zero;
+ m.WParam = new IntPtr(2);
+ return true;
+ }
+
+ return false;
+ }
+
+ #endregion
+
}
}
diff --git a/Apewer.Windows/Surface/ImageUtility.cs b/Apewer.Windows/Surface/ImageUtility.cs
index fa48253..25def4f 100644
--- a/Apewer.Windows/Surface/ImageUtility.cs
+++ b/Apewer.Windows/Surface/ImageUtility.cs
@@ -21,10 +21,12 @@ namespace Apewer.Surface
internal static byte[] EmptyBytes { get { return new byte[0]; } }
- /// 保存图像到文件,失败时返回 NULL 值。
- public static byte[] SaveAsBytes(Image image, ImageFormat format, bool dispose = false)
+ /// 保存图像到文件。
+ ///
+ public static byte[] SaveAs(this Image image, ImageFormat format)
{
- if (image == null) return EmptyBytes;
+ if (image == null) throw new ArgumentNullException(nameof(image));
+ if (format == null) throw new ArgumentNullException(nameof(format));
var memory = new MemoryStream();
var bytes = null as byte[];
@@ -35,20 +37,21 @@ namespace Apewer.Surface
}
catch { }
memory.Dispose();
- if (dispose) image.Dispose();
return bytes;
}
- /// 保存为 PNG 文件,失败时返回 NULL 值。
- public static byte[] SaveAsPng(Image image, bool dispose = false)
+ /// 保存为 PNG 文件。
+ ///
+ public static byte[] SaveAsPng(this Image image)
{
- return SaveAsBytes(image, ImageFormat.Png, dispose);
+ return SaveAs(image, ImageFormat.Png);
}
- /// 保存为 JPEG 文件,失败时返回 NULL 值。
- public static byte[] SaveAsJpeg(Image image, bool dispose = false)
+ /// 保存为 JPEG 文件。
+ ///
+ public static byte[] SaveAsJpeg(this Image image)
{
- return SaveAsBytes(image, ImageFormat.Jpeg, dispose);
+ return SaveAs(image, ImageFormat.Jpeg);
}
/// 调整图像尺寸,生成新图像。
diff --git a/Apewer.Windows/_Extensions.cs b/Apewer.Windows/_Extensions.cs
deleted file mode 100644
index 4da20c0..0000000
--- a/Apewer.Windows/_Extensions.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using Apewer;
-using Apewer.Surface;
-using System;
-using System.Windows.Forms;
-
-/// 扩展方法。
-public static class Extensions_Apewer_Windows
-{
-
- #region Surface
-
-#if NETFX || NETCORE
-
- ///
- public static void Invoke(this Control @this, Action action, bool async = false) => FormsUtility.Invoke(@this, action, async);
-
-#endif
-
- #endregion
-
-}
\ No newline at end of file
diff --git a/Apewer/Apewer.props b/Apewer/Apewer.props
index 9aad0c6..67efd36 100644
--- a/Apewer/Apewer.props
+++ b/Apewer/Apewer.props
@@ -9,7 +9,7 @@
Apewer
Apewer Libraries
- 6.7.5
+ 6.7.6
diff --git a/Apewer/BytesSet.cs b/Apewer/BytesSet.cs
deleted file mode 100644
index a59af39..0000000
--- a/Apewer/BytesSet.cs
+++ /dev/null
@@ -1,328 +0,0 @@
-using Apewer.Internals;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Text;
-
-namespace Apewer
-{
-
- ///
- public class BytesSet
- {
-
- private Dictionary _dict = new Dictionary();
-
- private volatile Func _inbound = null;
- private volatile Func _outbound = null;
-
- ///
- public byte[] this[string key]
- {
- get { return GetValue(key); }
- set { SetValue(key, value); }
- }
-
- /// 设置或获取值入站的函数,设置时字典必须为空。设置为 Null 将忽略入站函数。
- public Func Inbound
- {
- get { lock (_dict) return _inbound; }
- set { lock (_dict) _inbound = value; }
- }
-
- /// 设置或获取值出站的函数。设置为 Null 将忽略出站函数。
- public Func Outbound
- {
- get { lock (_dict) return _outbound; }
- set { lock (_dict) _outbound = value; }
- }
-
- ///
- public int Count
- {
- get
- {
- var count = 0;
- lock (_dict) { count = _dict.Count; }
- return count;
- }
- }
-
- ///
- public List Keys
- {
- get
- {
- var list = new List();
- lock (_dict)
- {
- list.AddRange(_dict.Keys);
- }
- return list;
- }
- }
-
- ///
- public BytesSet() { }
-
- ///
- public BytesSet(Func inbound, Func outbound)
- {
- _inbound = inbound;
- _outbound = outbound;
- }
-
- ///
- public void Clear()
- {
- lock (_dict)
- {
- foreach (var key in _dict.Keys)
- {
- _dict[key] = null;
- }
- _dict.Clear();
- }
- }
-
- ///
- public bool Import(byte[] data)
- {
- var memory = new MemoryStream();
- lock (data)
- {
- if (data != null) memory.Write(data, 0, data.Length);
- BytesUtility.ResetPosition(memory);
- }
- if (memory.Length < 4) return false;
- var count = 0;
- var first = true;
- while (true)
- {
- if (!CanRead(memory, 4)) break;
- if (first)
- {
- var buffer = new byte[4];
- memory.Read(buffer, 0, 4);
- count = GetInt32(buffer);
-
- first = false;
- }
- else
- {
-
- // Read Key Length
- var keylength = 0;
- {
- if (!CanRead(memory, 4)) break;
- var buffer = new byte[4];
- memory.Read(buffer, 0, 4);
- keylength = GetInt32(buffer);
- }
-
- // Read Key Data
- var key = Constant.EmptyString;
- if (keylength > 1)
- {
- if (!CanRead(memory, keylength)) break;
- var buffer = new byte[keylength];
- memory.Read(buffer, 0, keylength);
- key = BytesUtility.ToText(buffer);
- }
-
- // Read Value Length
- var valuelength = 0;
- {
- if (!CanRead(memory, 4)) break;
- var buffer = new byte[4];
- memory.Read(buffer, 0, 4);
- valuelength = GetInt32(buffer);
- }
-
- // Read Key Data
- var value = Constant.EmptyBytes;
- if (valuelength > 1)
- {
- if (!CanRead(memory, valuelength)) break;
- var buffer = new byte[valuelength];
- memory.Read(buffer, 0, valuelength);
- value = BytesUtility.Clone(buffer);
- }
-
- if (_dict.ContainsKey(key)) continue;
- _dict.Add(key, value);
- if (_dict.Count >= count) break;
- }
- }
- return count == _dict.Count;
- }
-
- ///
- public byte[] Export()
- {
- var memory = new MemoryStream();
- lock (_dict)
- {
- var count = _dict.Count;
- var countbytes = GetBytes(count);
- memory.Write(countbytes, 0, countbytes.Length);
- foreach (var pair in _dict)
- {
- var keydata = BytesUtility.FromText(pair.Key);
- var keycount = GetBytes(keydata.Length);
- memory.Write(keycount, 0, keycount.Length);
- if (keydata.Length > 0) memory.Write(keydata, 0, keydata.Length);
-
- var valuedata = pair.Value ?? Constant.EmptyBytes;
- var valuecount = GetBytes(valuedata.Length);
- memory.Write(valuecount, 0, valuecount.Length);
- if (valuedata.Length > 0) memory.Write(valuedata, 0, valuedata.Length);
- }
- }
- var data = memory.ToArray();
- memory.Dispose();
- return data;
- }
-
- ///
- public bool Contains(string key)
- {
- if (key == null) return false;
- var contains = false;
- lock (_dict)
- {
- contains = _dict.ContainsKey(key);
- }
- return contains;
- }
-
- ///
- public byte[] GetValue(string key)
- {
- var k = key;
- var v = Constant.EmptyBytes;
- if (k == null) return v;
- lock (_dict)
- {
- if (_dict.ContainsKey(k)) v = _dict[k];
- }
- if (_outbound != null)
- {
- v = _outbound(v);
- if (v == null) v = Constant.EmptyBytes;
- }
- return v;
- }
-
- ///
- public bool SetValue(string key, byte[] value)
- {
- var k = key;
- var v = Constant.EmptyBytes;
- if (k == null) return false;
- lock (value)
- {
- if (value != null) v = BytesUtility.Clone(value);
- }
- if (_inbound != null)
- {
- v = _inbound(v);
- if (v == null) v = Constant.EmptyBytes;
- }
- lock (_dict)
- {
- if (_dict.ContainsKey(k)) _dict.Remove(k);
- _dict.Add(k, v);
- }
- return true;
- }
-
- private static bool CanRead(Stream stream, int length)
- {
- if (length < 0) return false;
- if (stream == null) return false;
- if (stream.CanRead == false) return false;
- if (stream.Position + length > stream.Length) return false;
- return true;
- }
-
- /// Int32 -> Byte[]
- private static byte[] GetBytes(int value)
- {
- const int t3 = 256 * 256 * 256;
- const int t2 = 256 * 256;
- const int t1 = 256;
-
- byte[] bs = { 0, 0, 0, 0 };
-
- if (value >= 0)
- {
- int vint = value;
-
- bs[0] = (byte)(vint / t3);
- vint = vint % t3;
-
- bs[1] = (byte)(vint / t2);
- vint = vint % t2;
-
- bs[2] = (byte)(vint / t1);
- vint = vint % t1;
-
- bs[3] = (byte)(vint);
- }
- else
- {
- int minusInt = Math.Abs(value + 1);
- var minusBytes = GetBytes(minusInt);
- bs[0] = (byte)(255 - minusBytes[0]);
- bs[1] = (byte)(255 - minusBytes[1]);
- bs[2] = (byte)(255 - minusBytes[2]);
- bs[3] = (byte)(255 - minusBytes[3]);
- }
-
- return bs;
- }
-
- /// Byte[] -> Int32
- private static Int32 GetInt32(byte[] value)
- {
- if (value.Length == 4)
- {
- const int t3 = 256 * 256 * 256;
- const int t2 = 256 * 256;
- const int t1 = 256;
-
- if (value[0] <= 127)
- {
- int[] vis = { 0, 0, 0, 0 };
- vis[0] = value[0] * t3;
- vis[1] = value[1] * t2;
- vis[2] = value[2] * t1;
- vis[3] = value[3];
- int vr = vis[0] + vis[1] + vis[2] + vis[3];
- return vr;
- }
- else
- {
- if ((value[0] == 128) && (value[1] == 0) && (value[2] == 0) && (value[3] == 0))
- {
- return int.MinValue;
- }
- else
- {
- var bytes = new byte[4];
- bytes[0] = (byte)(255 - value[0]);
- bytes[1] = (byte)(255 - value[1]);
- bytes[2] = (byte)(255 - value[2]);
- bytes[3] = (byte)(255 - value[3]);
- int vminusint = 0 - 1 - GetInt32(bytes);
- return vminusint;
- }
- }
- }
- return 0;
- }
-
- }
-
-}
diff --git a/Apewer/BytesUtility.cs b/Apewer/BytesUtility.cs
index cbb86ee..4b76127 100644
--- a/Apewer/BytesUtility.cs
+++ b/Apewer/BytesUtility.cs
@@ -13,7 +13,7 @@ namespace Apewer
{
/// 二进制。
- public class BytesUtility
+ public static class BytesUtility
{
/// 空字节数组,每次获取都将创建新的引用。
@@ -42,25 +42,19 @@ namespace Apewer
#region Bytes Array
/// 克隆字节数组。当源为 NULL 时获取零元素字节数组。
- public static byte[] Clone(byte[] bytes)
+ public static byte[] Clone(this byte[] bytes)
{
- if (bytes == null || bytes.LongLength < 0L) return Empty;
- var result = new byte[bytes.LongLength];
- bytes.CopyTo(result, 0L);
+ if (bytes == null) return Empty;
+ var length = bytes.Length;
+ if (length < 1) return Empty;
+ var result = new byte[length];
+ Buffer.BlockCopy(bytes, 0, result, 0, length);
return result;
}
- /// 创建数组,元素值为零。
- public static byte[] ZeroArray(int length = 0)
- {
- if (length < 1) return new byte[0];
- var array = new byte[length];
- for (int i = 0; i < length; i++) array[i] = 0;
- return array;
- }
-
- /// 所有字节取反。
- public static byte[] Adverse(byte[] bytes)
+ /// 每个字节取反。
+ /// value = 255 - value
+ public static byte[] Adverse(this byte[] bytes)
{
if (bytes == null || bytes.LongLength < 1L) return Empty;
var adverse = new byte[bytes.LongLength];
@@ -69,41 +63,42 @@ namespace Apewer
}
/// 确定此字节数组实例的开头是否与指定的字节数组匹配。
- public static bool StartsWith(byte[] bytes, params byte[] head)
+ public static bool StartsWith(this byte[] bytes, params byte[] head)
{
- var data = bytes;
+ // 头部为空,视为匹配。
+ if (head == null) return true;
+ var length = head.Length;
+ if (length < 1) return true;
- if (data == null) return false;
- if (head == null) return false;
-
- var datalength = data.LongLength;
- var headlength = head.LongLength;
- if (datalength < headlength) return false;
- if (headlength < 1L) return true;
+ // 样本为空,视为不匹配。
+ if (bytes == null) return false;
+ if (bytes.Length < length) return false;
- for (long i = 0; i < head.LongLength; i++)
+ // 对比头部字节。
+ for (var i = 0; i < length; i++)
{
- if (data[i] != head[i]) return false;
+ if (bytes[i] != head[i]) return false;
}
return true;
}
/// 确定此字节数组实例的结尾是否与指定的字节数组匹配。
- public static bool EndsWith(byte[] bytes, params byte[] end)
+ public static bool EndsWith(this byte[] bytes, params byte[] foot)
{
- if (bytes == null) return false;
- if (end == null) return false;
+ // 尾部为空,视为匹配。
+ if (foot == null) return true;
+ var length = foot.Length;
+ if (length < 1) return true;
- var dataLength = bytes.LongLength;
- var endLength = end.LongLength;
- if (dataLength < endLength) return false;
- if (endLength < 1L) return true;
+ // 样本为空,视为不匹配。
+ if (bytes == null) return false;
+ if (bytes.Length < length) return false;
- for (long i = 0; i < endLength; i++)
+ // 对比尾部字节。
+ var offset = bytes.Length - length;
+ for (var i = 0; i < length; i++)
{
- var dataindex = dataLength - i - 1;
- var headindex = endLength - i - 1;
- if (bytes[dataindex] != end[headindex]) return false;
+ if (bytes[offset + i] != foot[i]) return false;
}
return true;
}
@@ -142,7 +137,7 @@ namespace Apewer
public static byte[] Append(byte[] head, params byte[] bytes) => Merge(head, bytes);
/// 为文本数据添加 BOM 字节,若已存在则忽略。
- public static byte[] AddTextBom(params byte[] bytes)
+ public static byte[] AddTextBom(this byte[] bytes)
{
var bom = new byte[] { 0xEF, 0xBB, 0xBF };
if (bytes == null || bytes.LongLength < 1L) return bom;
@@ -152,7 +147,7 @@ namespace Apewer
}
/// 去除文本数据的 BOM 字节,若不存在则忽略。
- public static byte[] WipeTextBom(byte[] bytes)
+ public static byte[] WipeTextBom(this byte[] bytes)
{
if (bytes == null) return Empty;
var hasBom = (bytes.Length >= 3) && (bytes[0] == 0xEF) && (bytes[1] == 0xBB) && (bytes[2] == 0xBF);
@@ -171,7 +166,7 @@ namespace Apewer
#region Text
/// 将字节数组转换为十六进制文本。
- public static string ToHex(params byte[] bytes)
+ public static string ToHex(this byte[] bytes)
{
int length = bytes.Length;
if (length > 0)
@@ -188,7 +183,7 @@ namespace Apewer
}
/// 将十六进制文本转换为字节数组。
- public static byte[] FromHex(string hex)
+ public static byte[] FromHex(this string hex)
{
if (string.IsNullOrEmpty(hex) || hex.Length < 2) return Empty;
if (hex.Length % 2 != 0) return Empty;
@@ -208,11 +203,9 @@ namespace Apewer
return bytes;
}
- /// 将字节数组格式化为大写十六进制字符串。
- public static string ToX2(params byte[] bytes) => ToX2(true, bytes);
-
/// 将字节数组格式化为十六进制字符串,可指定大小写。
- public static string ToX2(bool upper, params byte[] bytes)
+ /// 例:D41D8CD98F00B204E9800998ECF8427E
+ public static string ToX2(this byte[] bytes, bool upper = true)
{
if (bytes == null) return "";
var length = bytes.Length;
@@ -233,7 +226,7 @@ namespace Apewer
/// Byte[] -> Base64
public static string ToBase64(params byte[] bytes)
{
- if (bytes.Length < 1) return Constant.EmptyString;
+ if (bytes == null || bytes.Length < 1) return Constant.EmptyString;
try { return Convert.ToBase64String(bytes); }
catch { return Constant.EmptyString; }
}
@@ -859,123 +852,97 @@ namespace Apewer
{
try
{
- var algorithm = new T();
- var result = algorithm.ComputeHash(bytes);
- algorithm.Clear();
-#if !NET20
- algorithm.Dispose();
-#endif
- return result;
+ using (var algorithm = new T())
+ {
+ var result = algorithm.ComputeHash(bytes);
+ algorithm.Clear();
+ return result;
+ }
}
catch { }
}
return Empty;
}
- private static byte[] ComputeHash(Stream stream, bool dispose, Action progress) where T : HashAlgorithm, new()
+ private static byte[] ComputeHash(Stream stream, Action progress) where T : HashAlgorithm, new()
{
if (progress == null)
{
- if (stream != null)
+
+ using (var algorithm = new T())
{
- var result = Empty;
- try
+ if (stream == null)
{
- var algorithm = new T();
- result = algorithm.ComputeHash(stream);
+ var result = algorithm.ComputeHash(Empty);
algorithm.Clear();
-#if !NET20
- algorithm.Dispose();
-#endif
+ return result;
+ }
+ else
+ {
+ var result = algorithm.ComputeHash(stream);
+ algorithm.Clear();
+ return result;
}
- catch { }
- if (dispose) stream.Dispose();
- return result;
}
- return Empty;
}
else
{
if (stream == null) return Empty;
// 初始化。
- var validcallback = progress != null;
- var capacity = DefaultBuffer;
- var buffer = new byte[capacity];
- var algorithm = new T();
- algorithm.Initialize();
-
- // 读取。
- var failed = false;
- while (true)
+ using (var algorithm = new T())
{
- var read = 0;
- try { read = stream.Read(buffer, 0, capacity); }
- catch { failed = true; }
+ algorithm.Initialize();
- if (read < capacity)
+ // 读取。
+ var count = 0;
+ var input = new byte[DefaultBuffer];
+ var output = new byte[DefaultBuffer];
+ while (true)
{
- if (read < 1)
+ count = stream.Read(input, 0, DefaultBuffer);
+
+ if (count < DefaultBuffer)
{
- algorithm.TransformFinalBlock(new byte[0], 0, 0);
+ algorithm.TransformFinalBlock(input, 0, count);
+ break;
}
else
{
- algorithm.TransformFinalBlock(buffer, 0, Convert.ToInt32(read));
+ algorithm.TransformBlock(input, 0, count, output, 0);
}
- break;
}
- else
- {
- algorithm.TransformBlock(buffer, 0, Convert.ToInt32(read), buffer, 0);
- }
- }
- if (failed)
- {
- algorithm.Clear();
-#if !NET20
- algorithm.Dispose();
-#endif
- if (dispose) stream.Dispose();
- return Empty;
- }
- else
- {
var result = algorithm.Hash;
algorithm.Clear();
-#if !NET20
- algorithm.Dispose();
-#endif
- if (dispose) stream.Dispose();
return result;
}
}
}
/// 获取 MD5 值。
- public static byte[] MD5(params byte[] bytes) => ComputeHash(bytes);
+ public static byte[] MD5(this byte[] bytes) => ComputeHash(bytes);
/// 获取 MD5 值。
- public static byte[] MD5(Stream stream, Action progress = null) => ComputeHash(stream, false, progress);
+ public static byte[] MD5(this Stream stream, Action progress = null) => ComputeHash(stream, progress);
/// 获取 SHA1 值。
- public static byte[] SHA1(params byte[] bytes) => ComputeHash(bytes);
+ public static byte[] SHA1(this byte[] bytes) => ComputeHash(bytes);
/// 获取 SHA1 值。
- public static byte[] SHA1(Stream stream, Action progress = null) => ComputeHash(stream, false, progress);
+ public static byte[] SHA1(this Stream stream, Action progress = null) => ComputeHash(stream, progress);
/// 获取 SHA256 值。
- public static byte[] SHA256(params byte[] bytes) => ComputeHash(bytes);
+ public static byte[] SHA256(this byte[] bytes) => ComputeHash(bytes);
/// 获取 SHA256 值。
- public static byte[] SHA256(Stream stream, Action progress = null) => ComputeHash(stream, false, progress);
+ public static byte[] SHA256(this Stream stream, Action progress = null) => ComputeHash(stream, progress);
/// 获取 SHA512 值。
- public static byte[] SHA512(params byte[] bytes) => ComputeHash(bytes);
+ public static byte[] SHA512(this byte[] bytes) => ComputeHash(bytes);
/// 获取 SHA512 值。
- public static byte[] SHA512(Stream stream, Action progress = null) => ComputeHash(stream, false, progress);
+ public static byte[] SHA512(this Stream stream, Action progress = null) => ComputeHash(stream, progress);
#endregion
diff --git a/Apewer/CipherSet.cs b/Apewer/CipherSet.cs
deleted file mode 100644
index 2934d6d..0000000
--- a/Apewer/CipherSet.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace Apewer
-{
-
- internal class CipherSet
- {
- }
-
-}
diff --git a/Apewer/CollectionUtility.cs b/Apewer/CollectionUtility.cs
index 29d530e..5d5ec57 100644
--- a/Apewer/CollectionUtility.cs
+++ b/Apewer/CollectionUtility.cs
@@ -396,6 +396,76 @@ namespace Apewer
return ab.Export();
}
+ /// 在数组尾部增加元素,生成新数组,不修改原数组。
+ /// 增加元素后的新数组。
+ public static T[] Push(this T[] array, T item)
+ {
+ if (array == null) return new T[] { item };
+
+ var length = array.Length;
+ var newArray = new T[length + 1];
+ if (length > 0) System.Array.Copy(array, 0, newArray, 0, length);
+ newArray[length] = item;
+ return newArray;
+ }
+
+ /// 在数组尾部增加元素,生成新数组,不修改原数组。
+ /// 增加元素后的新数组。
+ public static T[] Push(this T[] array, T[] items)
+ {
+ if (array == null) return items ?? new T[0];
+ if (items == null)
+ {
+ var result = new T[array.Length];
+ array.CopyTo(result, 0);
+ return result;
+ }
+ else
+ {
+ var length1 = array.Length;
+ var length2 = items.Length;
+ var result = new T[length1 + length2];
+ if (length1 > 0) System.Array.Copy(array, 0, result, 0, length1);
+ if (length2 > 0) System.Array.Copy(items, 0, result, length1, length2);
+ return result;
+ }
+ }
+
+ /// 在数组头部增加元素,生成新数组,不修改原数组。
+ /// 增加元素后的新数组。
+ public static T[] Unshift(this T[] array, T item)
+ {
+ if (array == null) return new T[] { item };
+
+ var length = array.Length;
+ var newArray = new T[length + 1];
+ newArray[0] = item;
+ if (length > 0) System.Array.Copy(array, 0, newArray, 1, length);
+ return newArray;
+ }
+
+ /// 在数组头部增加元素,生成新数组,不修改原数组。
+ /// 增加元素后的新数组。
+ public static T[] Unshift(this T[] array, T[] items)
+ {
+ if (array == null) return items ?? new T[0];
+ if (items == null)
+ {
+ var result = new T[array.Length];
+ array.CopyTo(result, 0);
+ return result;
+ }
+ else
+ {
+ var length1 = array.Length;
+ var length2 = items.Length;
+ var result = new T[length1 + length2];
+ if (length2 > 0) System.Array.Copy(items, 0, result, 0, length2);
+ if (length1 > 0) System.Array.Copy(array, 0, result, length2, length1);
+ return result;
+ }
+ }
+
#endregion
#region 排序
diff --git a/Apewer/Json.cs b/Apewer/Json.cs
index 605e934..ab66893 100644
--- a/Apewer/Json.cs
+++ b/Apewer/Json.cs
@@ -50,9 +50,19 @@ namespace Apewer
#endregion
- #region 构造。
+ #region 消息
+
+ private const string ValueIsNotSupported = "指定的值类型不受支持";
+ private const string IsNotJsonObject = "当前实例不是 Json 对象。";
+ private const string IsNotJsonArray = "当前实例不是 Json 数组。";
+ private const string InvalidIndex = "未指定有效的索引。";
+ private const string IndexLessZero = "指定的索引小于 0,无效。";
+ private const string IndexGraterMax = "指定的索引超出了最大值,无效。";
+ private const string IndexGraterCount = "指定的索引超出了数量,无效。";
- #region 基础。
+ #endregion
+
+ #region JToken
[NonSerialized]
private JToken _jtoken = null;
@@ -69,8 +79,57 @@ namespace Apewer
[NonSerialized]
private JValue _jvalue = null;
+ static JToken ToJToken(object value)
+ {
+ if (value == null) return new JValue(null, JTokenType.Null);
+
+ var type = value.GetType();
+ if (RuntimeUtility.IsNullableType(type)) value = RuntimeUtility.GetNullableValue(value);
+
+ if (value == null) return new JValue(null, JTokenType.Null);
+ if (value is Json json) return json?._jtoken ?? new JValue(null, JTokenType.Null);
+ if (value is DateTime dt) return new JValue(SerializeDateTime(dt));
+
+ if (value is string) return new JValue(value);
+ if (value is bool) return new JValue(value);
+
+ if (value is byte) return new JValue(value);
+ if (value is sbyte) return new JValue(value);
+ if (value is short) return new JValue(value);
+ if (value is ushort) return new JValue(value);
+ if (value is int) return new JValue(value);
+ if (value is uint) return new JValue(value);
+ if (value is long) return new JValue(value);
+ if (value is ulong uint64) return (uint64 > int.MaxValue) ? new JValue(uint64.ToString()) : new JValue(Convert.ToInt64(uint64));
+
+ if (value is float) return new JValue(value);
+ if (value is double) return new JValue(value);
+ if (value is decimal) return new JValue(value);
+
+ var from = From(value);
+ if (from != null) return from._jtoken;
+ throw new ArgumentException(ValueIsNotSupported);
+ }
+
+ static object ParseJToken(JToken jtoken)
+ {
+ if (jtoken == null) return null;
+ if (jtoken is JValue jvalue) return jvalue.Value;
+ if (jtoken is JObject) return new Json(jtoken);
+ if (jtoken is JArray) return new Json(jtoken);
+ if (jtoken is JProperty jproperty)
+ {
+ var value = jproperty.Value;
+ if (value == null) return null;
+ return ParseJToken(value);
+ }
+ throw new InvalidOperationException($"Json 类型 {jtoken.Type} 不支持解析。");
+ }
+
#endregion
+ #region 构造。
+
#region Reset
/// 重置当前对象为空。
@@ -190,19 +249,10 @@ namespace Apewer
#region 属性。
/// 用于兼容 SimpleJson 的操作。
- public string this[string name]
- {
- get
- {
- var property = GetProperty(name);
- if (property == null) return "";
- return property.ToString();
- }
- set
- {
- SetProperty(name, value ?? "");
- }
- }
+ public string this[string name] { get => GetProperty(name)?.ToString() ?? ""; set => SetProperty(name, value ?? ""); }
+
+ /// 获取或设置数组的元素。
+ public object this[int index] { get => GetItem(index); set => SetItem(index, value); }
private JTokenType TokenType
{
@@ -244,9 +294,7 @@ namespace Apewer
public string Type { get { return TokenType.ToString(); } }
/// 实例有效。
- public
- bool Available
- { get { return _jtoken != null && TokenType != JTokenType.None; } }
+ public bool Available { get { return _jtoken != null && TokenType != JTokenType.None; } }
/// 获取当前实例的值,当为 Json 格式时缩进。
public string Lucid { get { return ToString(true); } }
@@ -921,6 +969,37 @@ namespace Apewer
}
}
+ /// 获取数组的元素。
+ ///
+ ///
+ public object GetItem(int index)
+ {
+ if (!IsArray) throw new InvalidOperationException(IsNotJsonArray);
+
+ if (index < 0) throw new ArgumentOutOfRangeException(nameof(index), IndexLessZero);
+ var jarray = _jtoken as JArray;
+ if (index >= jarray.Count) throw new ArgumentOutOfRangeException(nameof(index), IndexGraterCount);
+
+ var item = jarray[index];
+ return ParseJToken(item);
+ }
+
+ /// 设置数组的元素。
+ ///
+ ///
+ ///
+ public void SetItem(int index, object value)
+ {
+ if (!IsArray) throw new InvalidOperationException(IsNotJsonArray);
+
+ if (index < 0) throw new ArgumentOutOfRangeException(nameof(index), IndexLessZero);
+ var jarray = _jtoken as JArray;
+ while (jarray.Count < index + 1) jarray.Add(null);
+
+ var jtoken = ToJToken(value);
+ jarray.SetItem(index, jtoken);
+ }
+
#endregion
#region Property
@@ -1817,13 +1896,17 @@ namespace Apewer
{
if (pt.Equals(typeof(DateTime)))
{
- try
+ if (AllowException)
{
- setter.Invoke(entity, new object[] { DeserializeDateTime(value as string) });
+ setter.Invoke(entity, new object[] { DeserializeDateTime(value) });
}
- catch (Exception exception)
+ else
{
- if (AllowException) throw exception;
+ try
+ {
+ setter.Invoke(entity, new object[] { DeserializeDateTime(value) });
+ }
+ catch { }
}
}
else if (pt.Equals(typeof(string)))
@@ -1888,39 +1971,202 @@ namespace Apewer
///
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
- var contains = false;
- if (IsObject)
+ if (!IsObject) throw new InvalidOperationException(IsNotJsonObject);
+
+ var contains = _jobject.ContainsKey(binder.Name);
+ if (!contains)
{
- var property = GetProperty(binder.Name);
- contains = property != null;
- result = contains ? property.Value : null;
- return contains;
+ result = null;
+ return true;
}
- result = null;
- return contains;
+ var jtoken = _jobject.GetValue(binder.Name);
+ result = ParseJToken(jtoken);
+ return true;
}
///
public override bool TrySetMember(SetMemberBinder binder, object value)
{
- var name = binder.Name;
-
- if (value == null) return SetProperty(name);
- if (value is Json) return SetProperty(name, (Json)value);
- if (value is string) return SetProperty(name, (string)value);
- if (value is bool) return SetProperty(name, (bool)value);
- if (value is byte) return SetProperty(name, (byte)value);
- if (value is sbyte) return SetProperty(name, (sbyte)value);
- if (value is short) return SetProperty(name, (short)value);
- if (value is ushort) return SetProperty(name, (ushort)value);
- if (value is int) return SetProperty(name, (int)value);
- if (value is uint) return SetProperty(name, (uint)value);
- if (value is long) return SetProperty(name, (long)value);
- if (value is float) return SetProperty(name, (float)value);
- if (value is double) return SetProperty(name, (double)value);
- if (value is decimal) return SetProperty(name, (decimal)value);
+ if (!IsObject) throw new InvalidOperationException(IsNotJsonObject);
+ var contains = _jobject.ContainsKey(binder.Name);
+ if (contains)
+ {
+ _jobject[binder.Name] = ToJToken(value);
+ return true;
+ }
+ else
+ {
+ _jobject.Add(binder.Name, ToJToken(value));
+ return true;
+ }
+ }
+
+ private Class DynamicIndex(object[] indexes, Func stringCallback, Func intCallback)
+ {
+ var index = indexes[0];
+ if (index != null)
+ {
+ if (IsObject)
+ {
+ if (index is string name) return new Class(stringCallback.Invoke(name));
+ }
+ if (IsArray)
+ {
+ if (index is int || index is short || index is byte || index is ushort || index is sbyte)
+ {
+ var int32 = int.Parse(index.ToString());
+ if (int32 < 0) throw new ArgumentOutOfRangeException("index", IndexLessZero);
+ return new Class(intCallback(int32));
+ }
+ else if (index is long int64)
+ {
+ if (int64 < 0) throw new ArgumentOutOfRangeException("index", IndexLessZero);
+ if (int64 > int.MaxValue) throw new ArgumentOutOfRangeException("index", IndexGraterMax);
+ var int32 = Convert.ToInt32(int64);
+ return new Class(intCallback(int32));
+ }
+ else if (index is uint uint32)
+ {
+ if (uint32 > int.MaxValue) throw new ArgumentOutOfRangeException("index", IndexGraterMax);
+ var int32 = Convert.ToInt32(uint32);
+ return new Class(intCallback(int32));
+ }
+ else if (index is ulong uint64)
+ {
+ if (uint64 > int.MaxValue) throw new ArgumentOutOfRangeException("index", IndexGraterMax);
+ var int32 = Convert.ToInt32(uint64);
+ return new Class(intCallback(int32));
+ }
+ }
+ }
+
+ throw new InvalidOperationException(InvalidIndex);
+ }
+
+ ///
+ public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
+ {
+ var box = DynamicIndex(indexes, name =>
+ {
+ var property = GetProperty(name);
+ return ParseJToken(property?._jtoken);
+ }, index =>
+ {
+ return GetItem(index);
+ });
+ if (box == null)
+ {
+ result = null;
+ return false;
+ }
+ else
+ {
+ result = box.Value;
+ return true;
+ }
+ }
+
+ ///
+ public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value)
+ {
+ var box = DynamicIndex(indexes, name =>
+ {
+ _jobject[name] = ToJToken(value);
+ return true;
+ }, index =>
+ {
+ SetItem(index, value);
+ return true;
+ });
+
+ return box != null;
+ }
+
+ ///
+ public override bool TryInvoke(InvokeBinder binder, object[] args, out object result)
+ {
+ switch (binder.CallInfo.ArgumentNames.First().Lower())
+ {
+ case "tostring":
+ if (args == null)
+ {
+ result = ToString();
+ return true;
+ }
+ else
+ {
+ switch (args.Length)
+ {
+ case 0:
+ result = ToString();
+ return true;
+ case 1:
+ if (args[0] == null)
+ {
+ result = null;
+ return false;
+ }
+ else
+ {
+ if (args[0] is bool indented)
+ {
+ result = ToString(indented);
+ return true;
+ }
+ else if (args[0] is byte int8)
+ {
+ result = ToString(int8 == 1);
+ return true;
+ }
+ else if (args[0] is short int16)
+ {
+ result = ToString(int16 == 1);
+ return true;
+ }
+ else if (args[0] is int int32)
+ {
+ result = ToString(int32 == 1);
+ return true;
+ }
+ else if (args[0] is long int64)
+ {
+ result = ToString(int64 == 1);
+ return true;
+ }
+ else
+ {
+ result = null;
+ return false;
+ }
+ }
+ default:
+ result = null;
+ return false;
+ }
+ }
+ default:
+ result = null;
+ return false;
+ }
+ }
+
+ ///
+ public override bool TryConvert(ConvertBinder binder, out object result)
+ {
+ if (binder.Type.Equals(typeof(Json)))
+ {
+ result = this;
+ return true;
+ }
+ if (binder.Type.Equals(typeof(string)))
+ {
+ result = ToString();
+ return true;
+ }
+
+ result = null;
return false;
}
@@ -2247,29 +2493,40 @@ namespace Apewer
#region DateTime
+ static Func _datetime_serializer = null;
+ static Func