diff --git a/Apewer.Run/Apewer.Run.csproj b/Apewer.Run/Apewer.Run.csproj
index bcd48ff..2ee913f 100644
--- a/Apewer.Run/Apewer.Run.csproj
+++ b/Apewer.Run/Apewer.Run.csproj
@@ -1,15 +1,38 @@
-
- Exe
-
-
-
- net461
-
-
-
-
-
+
+ Exe
+
+
+
+ net461
+
+
+
+
+ $(AssemblyName) - .NET Framework 4.6.1
+ NETFX;NET461;NET45;HAVE_ASYNC;HAVE_BIG_INTEGER;$(DefineConstants);$(AdditionalConstants)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Apewer/Apewer.csproj b/Apewer/Apewer.csproj
index ed44a85..dea55f6 100644
--- a/Apewer/Apewer.csproj
+++ b/Apewer/Apewer.csproj
@@ -9,7 +9,7 @@
latest
CS0108,CS0162,CS0414,CS0612,CS0618,CS0649,CS1589,CS1570,CS1572,CS1573,CS3019,CS3021
Library
- netcoreapp3.1;netstandard2.1;net461;net40;net20
+ netcoreapp3.1;netstandard2.1;netstandard2.0;net461;net40;net20
@@ -20,7 +20,7 @@
Apewer
Apewer Libraries
- 6.3.2
+ 6.3.3
@@ -43,6 +43,12 @@
NETSTD;HAVE_ASYNC;HAVE_BIG_INTEGER;$(DefineConstants);$(AdditionalConstants)
+
+
+ $(AssemblyName) - .NET Standard 2.0
+ NETSTD;HAVE_ASYNC;HAVE_BIG_INTEGER;$(DefineConstants);$(AdditionalConstants)
+
+
$(AssemblyName) - .NET Framework 2.0
diff --git a/Apewer/BinaryUtility.cs b/Apewer/BinaryUtility.cs
index eaf7342..be97f67 100644
--- a/Apewer/BinaryUtility.cs
+++ b/Apewer/BinaryUtility.cs
@@ -153,6 +153,9 @@ namespace Apewer
return wiped;
}
+ /// 生成新的 GUID 数据。
+ public static byte[] NewGuid() => Guid.NewGuid().ToByteArray();
+
#endregion
#region Text
diff --git a/Apewer/ChinaUtility.cs b/Apewer/ChinaUtility.cs
new file mode 100644
index 0000000..91e039a
--- /dev/null
+++ b/Apewer/ChinaUtility.cs
@@ -0,0 +1,81 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Apewer
+{
+
+ ///
+ public class ChinaUtility
+ {
+
+ private static bool CheckIdCode18(string idCode18)
+ {
+ // GB11643-1999
+ if (string.IsNullOrEmpty(idCode18) || idCode18.Length != 18) return false;
+ var code = idCode18;
+
+ // 数字。
+ var n = 0L;
+ if (long.TryParse(code.Remove(17), out n) == false) return false;
+ if (n < Math.Pow(10, 16)) return false;
+ if (long.TryParse(code.Replace('x', '0').Replace('X', '0'), out n) == false) return false;
+
+ // 省份。
+ var address = "11x22x35x44x53x12x23x36x45x54x13x31x37x46x61x14x32x41x50x62x15x33x42x51x63x21x34x43x52x64x65x71x81x82x91";
+ if (address.IndexOf(code.Remove(2)) < 0) return false;
+
+ // 生日。
+ var birth = code.Substring(6, 8).Insert(6, "-").Insert(4, "-");
+ var time = new DateTime();
+ if (DateTime.TryParse(birth, out time) == false) return false;
+
+ // 校验码。
+ var cc = "10x98765432".ToCharArray();
+ var wi = "7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2".Split(',');
+ var ai = code.Remove(17).ToCharArray();
+ var sum = 0;
+ for (int i = 0; i < 17; i++)
+ {
+ sum += int.Parse(wi[i]) * int.Parse(ai[i].ToString());
+ }
+ var y = sum % 11;
+ if (cc[y] != code[17]) return false;
+
+ return true;
+ }
+
+ private static bool CheckIdCode15(string idCode15)
+ {
+ if (string.IsNullOrEmpty(idCode15) || idCode15.Length != 15) return false;
+ var code = idCode15;
+
+ // 数字。
+ var n = 0L;
+ if (long.TryParse(code, out n) == false || n < Math.Pow(10, 14)) return false;
+
+ // 省份。
+ var address = "11x22x35x44x53x12x23x36x45x54x13x31x37x46x61x14x32x41x50x62x15x33x42x51x63x21x34x43x52x64x65x71x81x82x91";
+ if (address.IndexOf(code.Remove(2)) == -1) return false;
+
+ // 生日。
+ var birth = code.Substring(6, 6).Insert(4, "-").Insert(2, "-");
+ var time = new DateTime();
+ if (DateTime.TryParse(birth, out time) == false) return false;
+
+ return true;
+
+ }
+
+ ///
+ public static bool CheckIdCode(string idCode)
+ {
+ if (string.IsNullOrEmpty(idCode)) return false;
+ if (idCode.Length == 18) return CheckIdCode18(idCode);
+ if (idCode.Length == 15) return CheckIdCode15(idCode);
+ return false;
+ }
+
+ }
+
+}
diff --git a/Apewer/Externals/Newtonsoft.Json-11.0.1/Serialization/JsonPropertyCollection.cs b/Apewer/Externals/Newtonsoft.Json-11.0.1/Serialization/JsonPropertyCollection.cs
index 08ed321..cd58f66 100644
--- a/Apewer/Externals/Newtonsoft.Json-11.0.1/Serialization/JsonPropertyCollection.cs
+++ b/Apewer/Externals/Newtonsoft.Json-11.0.1/Serialization/JsonPropertyCollection.cs
@@ -140,7 +140,7 @@ namespace Newtonsoft.Json.Serialization
return property;
}
-#if NETFX
+#if NETFX || NETSTANDARD2_0
private bool TryGetValue(string key, out JsonProperty item)
#else
private new bool TryGetValue(string key, out JsonProperty item)
diff --git a/Apewer/Internals/Interop/AdvApi32.cs b/Apewer/Internals/Interop/AdvApi32.cs
index 8caac06..0a74784 100644
--- a/Apewer/Internals/Interop/AdvApi32.cs
+++ b/Apewer/Internals/Interop/AdvApi32.cs
@@ -9,15 +9,16 @@ namespace Apewer.Internals.Interop
{
[DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
- internal static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr phtok);
+ internal static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall, ref TokenPrivilege newst, int len, IntPtr prev, IntPtr relen);
+
+ [DllImport("advapi32.dll", SetLastError = true)]
+ internal static extern bool LookupPrivilegeValue(string host, string name, ref long pluid);
[DllImport("advapi32.dll", SetLastError = true)]
- internal static extern bool LookupPrivilegeValueA
- (string host, string name, ref long pluid);
+ internal static extern bool LookupPrivilegeValueA(string host, string name, ref long pluid);
[DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
- internal static extern bool
- AdjustTokenPrivileges(IntPtr htok, bool disall, ref TokenPrivilege newst, int len, IntPtr prev, IntPtr relen);
+ internal static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr phtok);
}
diff --git a/Apewer/Internals/Interop/Constant.cs b/Apewer/Internals/Interop/Constant.cs
index 934a7c9..476298e 100644
--- a/Apewer/Internals/Interop/Constant.cs
+++ b/Apewer/Internals/Interop/Constant.cs
@@ -218,6 +218,8 @@ namespace Apewer.Internals.Interop
public const int DMDO_180 = 2;
public const int DMDO_270 = 3;
+ #region 电源。
+
public const int SE_PRIVILEGE_ENABLED = 0x00000002;
public const int TOKEN_QUERY = 0x00000008;
public const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;
@@ -230,6 +232,8 @@ namespace Apewer.Internals.Interop
public const int EWX_POWEROFF = 0x00000008;
public const int EWX_FORCEIFHUNG = 0x00000010;
+ #endregion
+
#region Windows Hook
///
@@ -327,6 +331,109 @@ namespace Apewer.Internals.Interop
#endregion
+ #region vbKey 按键事件
+
+ public const byte vbKeyLButton = 0x1; // 鼠标左键
+ public const byte vbKeyRButton = 0x2; // 鼠标右键
+ public const byte vbKeyCancel = 0x3; // CANCEL 键
+ public const byte vbKeyMButton = 0x4; // 鼠标中键
+ public const byte vbKeyBack = 0x8; // BACKSPACE 键
+ public const byte vbKeyTab = 0x9; // TAB 键
+ public const byte vbKeyClear = 0xC; // CLEAR 键
+ public const byte vbKeyReturn = 0xD; // ENTER 键
+ public const byte vbKeyShift = 0x10; // SHIFT 键
+ public const byte vbKeyControl = 0x11; // CTRL 键
+ public const byte vbKeyAlt = 18; // Alt 键 (键码18)
+ public const byte vbKeyMenu = 0x12; // MENU 键
+ public const byte vbKeyPause = 0x13; // PAUSE 键
+ public const byte vbKeyCapital = 0x14; // CAPS LOCK 键
+ public const byte vbKeyEscape = 0x1B; // ESC 键
+ public const byte vbKeySpace = 0x20; // SPACEBAR 键
+ public const byte vbKeyPageUp = 0x21; // PAGE UP 键
+ public const byte vbKeyEnd = 0x23; // End 键
+ public const byte vbKeyHome = 0x24; // HOME 键
+ public const byte vbKeyLeft = 0x25; // LEFT ARROW 键
+ public const byte vbKeyUp = 0x26; // UP ARROW 键
+ public const byte vbKeyRight = 0x27; // RIGHT ARROW 键
+ public const byte vbKeyDown = 0x28; // DOWN ARROW 键
+ public const byte vbKeySelect = 0x29; // Select 键
+ public const byte vbKeyPrint = 0x2A; // PRINT SCREEN 键
+ public const byte vbKeyExecute = 0x2B; // EXECUTE 键
+ public const byte vbKeySnapshot = 0x2C; // SNAPSHOT 键
+ public const byte vbKeyDelete = 0x2E; // Delete 键
+ public const byte vbKeyHelp = 0x2F; // HELP 键
+ public const byte vbKeyNumlock = 0x90; // NUM LOCK 键
+
+ public const byte vbKeyA = 65;
+ public const byte vbKeyB = 66;
+ public const byte vbKeyC = 67;
+ public const byte vbKeyD = 68;
+ public const byte vbKeyE = 69;
+ public const byte vbKeyF = 70;
+ public const byte vbKeyG = 71;
+ public const byte vbKeyH = 72;
+ public const byte vbKeyI = 73;
+ public const byte vbKeyJ = 74;
+ public const byte vbKeyK = 75;
+ public const byte vbKeyL = 76;
+ public const byte vbKeyM = 77;
+ public const byte vbKeyN = 78;
+ public const byte vbKeyO = 79;
+ public const byte vbKeyP = 80;
+ public const byte vbKeyQ = 81;
+ public const byte vbKeyR = 82;
+ public const byte vbKeyS = 83;
+ public const byte vbKeyT = 84;
+ public const byte vbKeyU = 85;
+ public const byte vbKeyV = 86;
+ public const byte vbKeyW = 87;
+ public const byte vbKeyX = 88;
+ public const byte vbKeyY = 89;
+ public const byte vbKeyZ = 90;
+
+ public const byte vbKey0 = 48;
+ public const byte vbKey1 = 49;
+ public const byte vbKey2 = 50;
+ public const byte vbKey3 = 51;
+ public const byte vbKey4 = 52;
+ public const byte vbKey5 = 53;
+ public const byte vbKey6 = 54;
+ public const byte vbKey7 = 55;
+ public const byte vbKey8 = 56;
+ public const byte vbKey9 = 57;
+
+ public const byte vbKeyNumpad0 = 0x60;
+ public const byte vbKeyNumpad1 = 0x61;
+ public const byte vbKeyNumpad2 = 0x62;
+ public const byte vbKeyNumpad3 = 0x63;
+ public const byte vbKeyNumpad4 = 0x64;
+ public const byte vbKeyNumpad5 = 0x65;
+ public const byte vbKeyNumpad6 = 0x66;
+ public const byte vbKeyNumpad7 = 0x67;
+ public const byte vbKeyNumpad8 = 0x68;
+ public const byte vbKeyNumpad9 = 0x69;
+ public const byte vbKeyMultiply = 0x6A; // MULTIPLICATIONSIGN (*)
+ public const byte vbKeyAdd = 0x6B; // PLUS SIGN (+)
+ public const byte vbKeySeparator = 0x6C; // ENTER
+ public const byte vbKeySubtract = 0x6D; // MINUS SIGN (-)
+ public const byte vbKeyDecimal = 0x6E; // DECIMAL POINT (.)
+ public const byte vbKeyDivide = 0x6F; // DIVISION SIGN (/)
+
+ public const byte vbKeyF1 = 0x70;
+ public const byte vbKeyF2 = 0x71;
+ public const byte vbKeyF3 = 0x72;
+ public const byte vbKeyF4 = 0x73;
+ public const byte vbKeyF5 = 0x74;
+ public const byte vbKeyF6 = 0x75;
+ public const byte vbKeyF7 = 0x76;
+ public const byte vbKeyF8 = 0x77;
+ public const byte vbKeyF9 = 0x78;
+ public const byte vbKeyF10 = 0x79;
+ public const byte vbKeyF11 = 0x7A;
+ public const byte vbKeyF12 = 0x7B;
+
+ #endregion
+
}
}
diff --git a/Apewer/Internals/Interop/DWM_BLURBEHIND.cs b/Apewer/Internals/Interop/DWM_BLURBEHIND.cs
new file mode 100644
index 0000000..e452b7d
--- /dev/null
+++ b/Apewer/Internals/Interop/DWM_BLURBEHIND.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace Apewer.Internals.Interop
+{
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal class DWM_BLURBEHIND
+ {
+ public uint dwFlags;
+ [MarshalAs(UnmanagedType.Bool)]
+ public bool fEnable;
+ public IntPtr hRegionBlur;
+ [MarshalAs(UnmanagedType.Bool)]
+ public bool fTransitionOnMaximized;
+
+ public const uint DWM_BB_ENABLE = 0x00000001;
+ public const uint DWM_BB_BLURREGION = 0x00000002;
+ public const uint DWM_BB_TRANSITIONONMAXIMIZED = 0x00000004;
+ }
+
+}
diff --git a/Apewer/Internals/Interop/DWM_THUMBNAIL_PROPERTIES.cs b/Apewer/Internals/Interop/DWM_THUMBNAIL_PROPERTIES.cs
new file mode 100644
index 0000000..2ec5cae
--- /dev/null
+++ b/Apewer/Internals/Interop/DWM_THUMBNAIL_PROPERTIES.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace Apewer.Internals.Interop
+{
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal class DWM_THUMBNAIL_PROPERTIES
+ {
+ public uint dwFlags;
+ public RECT rcDestination;
+ public RECT rcSource;
+ public byte opacity;
+ [MarshalAs(UnmanagedType.Bool)]
+ public bool fVisible;
+ [MarshalAs(UnmanagedType.Bool)]
+ public bool fSourceClientAreaOnly;
+ public const uint DWM_TNP_RECTDESTINATION = 0x00000001;
+ public const uint DWM_TNP_RECTSOURCE = 0x00000002;
+ public const uint DWM_TNP_OPACITY = 0x00000004;
+ public const uint DWM_TNP_VISIBLE = 0x00000008;
+ public const uint DWM_TNP_SOURCECLIENTAREAONLY = 0x00000010;
+ }
+
+}
diff --git a/Apewer/Internals/Interop/DwmApi.cs b/Apewer/Internals/Interop/DwmApi.cs
new file mode 100644
index 0000000..f369047
--- /dev/null
+++ b/Apewer/Internals/Interop/DwmApi.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace Apewer.Internals.Interop
+{
+
+ class DwmApi
+ {
+
+ [DllImport("dwmapi.dll", PreserveSig = false)]
+ public static extern void DwmEnableBlurBehindWindow(IntPtr hWnd, DWM_BLURBEHIND pBlurBehind);
+
+ [DllImport("dwmapi.dll", PreserveSig = false)]
+ public static extern void DwmEnableComposition(bool bEnable);
+
+ [DllImport("dwmapi.dll", PreserveSig = false)]
+ public static extern void DwmExtendFrameIntoClientArea(IntPtr hwnd, ref MARGINS margins);
+
+ [DllImport("dwmapi.dll", PreserveSig = false)]
+ public static extern void DwmExtendFrameIntoClientArea(IntPtr hWnd, MARGINS pMargins);
+
+ [DllImport("dwmapi.dll", PreserveSig = false)]
+ public static extern void DwmGetColorizationColor(out int pcrColorization, [MarshalAs(UnmanagedType.Bool)] out bool pfOpaqueBlend);
+
+ [DllImport("dwmapi.dll", PreserveSig = false)]
+ public static extern bool DwmIsCompositionEnabled();
+
+ [DllImport("dwmapi.dll", PreserveSig = false)]
+ public static extern void DwmQueryThumbnailSourceSize(IntPtr hThumbnail, out Size size);
+
+ [DllImport("dwmapi.dll", PreserveSig = false)]
+ public static extern IntPtr DwmRegisterThumbnail(IntPtr dest, IntPtr source);
+
+ [DllImport("dwmapi.dll", PreserveSig = false)]
+ public static extern void DwmUnregisterThumbnail(IntPtr hThumbnail);
+
+ [DllImport("dwmapi.dll", PreserveSig = false)]
+ public static extern void DwmUpdateThumbnailProperties(IntPtr hThumbnail, DWM_THUMBNAIL_PROPERTIES props);
+
+ }
+
+}
diff --git a/Apewer/Internals/Interop/MARGINS.cs b/Apewer/Internals/Interop/MARGINS.cs
new file mode 100644
index 0000000..4fb9500
--- /dev/null
+++ b/Apewer/Internals/Interop/MARGINS.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace Apewer.Internals.Interop
+{
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct MARGINS
+ {
+ public int Left;
+ public int Right;
+ public int Top;
+ public int Bottom;
+ }
+
+}
diff --git a/Apewer/Internals/Interop/RECT.cs b/Apewer/Internals/Interop/RECT.cs
new file mode 100644
index 0000000..2e214d5
--- /dev/null
+++ b/Apewer/Internals/Interop/RECT.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace Apewer.Internals.Interop
+{
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct RECT
+ {
+ public int Left;
+ public int Top;
+ public int Right;
+ public int Bottom;
+
+ public RECT(int left, int top, int right, int bottom)
+ {
+ Left = left;
+ Top = top;
+ Right = right;
+ Bottom = bottom;
+ }
+ }
+
+}
diff --git a/Apewer/Internals/Interop/SHCore.cs b/Apewer/Internals/Interop/SHCore.cs
new file mode 100644
index 0000000..18c7b0d
--- /dev/null
+++ b/Apewer/Internals/Interop/SHCore.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace Apewer.Internals.Interop
+{
+
+ internal class SHCore
+ {
+
+ [DllImport("SHCore.dll", SetLastError = true)]
+ public static extern bool SetProcessDpiAwareness(PROCESS_DPI_AWARENESS awareness);
+
+ [DllImport("SHCore.dll", SetLastError = true)]
+ public static extern void GetProcessDpiAwareness(IntPtr hprocess, out PROCESS_DPI_AWARENESS awareness);
+
+ public enum PROCESS_DPI_AWARENESS
+ {
+ Process_DPI_Unaware = 0,
+ Process_System_DPI_Aware = 1,
+ Process_Per_Monitor_DPI_Aware = 2
+ }
+
+ }
+
+}
diff --git a/Apewer/Internals/Interop/User32.cs b/Apewer/Internals/Interop/User32.cs
index ee4613a..ab15a30 100644
--- a/Apewer/Internals/Interop/User32.cs
+++ b/Apewer/Internals/Interop/User32.cs
@@ -131,6 +131,14 @@ namespace Apewer.Internals.Interop
[DllImport("user32.dll")]
public static extern void keybd_Callback(byte bVk, byte bScan, uint dwFlags, uint dwExtraInfo);
+ /// 发送键盘事件。
+ /// 按键的虚拟键值
+ /// 扫描码,一般不用设置,用 0 代替。
+ /// 选项标志:0 按下,2 释放。
+ /// 扩展信息,默认为 0。
+ [DllImport("user32.dll")]
+ public static extern void keybd_event(byte bVk, byte bScan, int dwFlags, int dwExtraInfo);
+
///
///
///
diff --git a/Apewer/Logger.cs b/Apewer/Logger.cs
index 56f2469..cf55562 100644
--- a/Apewer/Logger.cs
+++ b/Apewer/Logger.cs
@@ -16,7 +16,6 @@ namespace Apewer
public class Logger
{
- private string _key = Guid.NewGuid().ToString("n");
private string _target = "";
private bool _enabled = true;
private Func _preoutput = null;
@@ -25,15 +24,15 @@ namespace Apewer
private bool _usefile = false;
private bool _uselock = false;
+ /// 当前日志记录器的名称。
+ public virtual string Name { get; set; }
+
/// 已启用。
public virtual bool Enabled { get; set; }
/// 在后台线程处理日志。默认值:FALSE。
internal virtual bool Background { get; set; } = false;
- /// 捕获并丢弃事件中的异常。默认值:FALSE。
- public virtual bool Catch { get; set; } = false;
-
/// 使用控制台输出。默认值:TRUE。
public virtual bool UseConsole
{
@@ -70,15 +69,17 @@ namespace Apewer
{
if (Background)
{
- RuntimeUtility.InBackground(action);
+ RuntimeUtility.InBackground(action, true);
return;
}
- action.Invoke();
+ try { action.Invoke(); } catch { }
}
/// 记录异常。
public virtual void Exception(object sender, Exception exception) => Invoke(() =>
{
+ if (!Enabled) return;
+
if (OnException != null)
{
OnException(sender, exception);
@@ -109,11 +110,13 @@ namespace Apewer
}
if (UseConsole) ToConsole(item);
- if (UseFile) ToFile(item);
+ if (UseFile) ToFile(item, this);
});
private void Colorful(object sender, string tag, Nullable color, object[] content, Event defined) => Invoke(() =>
{
+ if (!Enabled) return;
+
if (defined != null)
{
defined.Invoke(sender, MergeContent(content));
@@ -127,7 +130,7 @@ namespace Apewer
item.Content = MergeContent(content);
if (UseConsole) ToConsole(item);
- if (UseFile) ToFile(item);
+ if (UseFile) ToFile(item, this);
});
/// 记录错误。多个 Content 参数将以“ | ”分隔。
@@ -149,8 +152,9 @@ namespace Apewer
/// 创建新实例。
public Logger() { }
- internal Logger(bool useConsole, bool useFile, bool enabled)
+ private Logger(string name, bool useConsole, bool useFile, bool enabled)
{
+ Name = name;
_useconsole = useConsole;
_usefile = useFile;
_uselock = true;
@@ -162,6 +166,9 @@ namespace Apewer
internal static object FileLocker = new object();
internal static object ConsoleLocker = new object();
+ /// 获取用于保存日志文件的路径。
+ public static Func FilePathGetter { get; set; }
+
private static string MergeContent(params object[] content) => TextUtility.Join(" | ", content);
private static string FormatSender(object sender)
@@ -254,29 +261,30 @@ namespace Apewer
}
// 向日志文件输出文本,文件名按日期自动生成。
- private static string ToFile(string plain)
+ private static string ToFile(string plain, Logger logger)
{
var text2 = TextUtility.Trim(plain) + "\r\n";
lock (FileLocker)
{
- var path = GetFileDir();
+ var path = GetFilePath(logger);
if (string.IsNullOrEmpty(path)) return "写入日志文件失败:无法获取日志文件路径。";
var bytes = TextUtility.ToBinary(TextUtility.Merge(plain, "\r\n"));
if (!StorageUtility.AppendFile(path, bytes)) return "写入日志文件失败。";
}
return null;
-
}
// 向日志文件输出文本,文件名按日期自动生成。
- private static string ToFile(LogItem item) => ToFile(ToText(item));
+ private static string ToFile(LogItem item, Logger logger) => ToFile(ToText(item), logger);
/// 获取日志文件路径发生错误时返回 NULL 值。
- /// d:\app\log\2020-02-02.log
- /// d:\www\app_data\log\2020-02-02.log
- public static string GetFileDir()
+ /// 默认例:
d:\app\log\1970-01-01.log
d:\www\app_data\log\1970-01-01.log
+ public static string GetFilePath(Logger logger = null)
{
+ var getter = FilePathGetter;
+ if (getter != null) try { return getter.Invoke(); } catch { }
+
// 找到 App_Data 目录。
var appDir = RuntimeUtility.ApplicationPath;
var dataDir = Path.Combine(appDir, "app_data");
@@ -303,18 +311,18 @@ namespace Apewer
#region 默认实列。
- private static Logger _default = new Logger(true, false, true);
- private static Logger _console = new Logger(true, false, true);
- private static Logger _web = new Logger(true, false, true);
+ private static Logger _default = new Logger("Apewer.Logger.Default", true, false, true);
+ private static Logger _console = new Logger("Apewer.Logger.Console", true, false, true);
+ private static Logger _web = new Logger("Apewer.Logger.Web", true, false, true);
#if DEBUG
- internal static Logger _internals = new Logger(true, true, true);
+ internal static Logger _internals = new Logger("Apewer.Logger.Internals", true, true, true);
#else
- internal static Logger _internals = new Logger(true, false, false);
+ internal static Logger _internals = new Logger("Apewer.Logger.Internals", true, false, false);
#endif
/// 内部的日志记录程序。
- public static Logger Internals { get => _internals; }
+ internal static Logger Internals { get => _internals; }
/// 默认的日志记录程序。
public static Logger Default { get => _default; }
@@ -325,9 +333,9 @@ namespace Apewer
/// 用于 Web 的日志记录程序。
public static Logger Web { get => _web; }
-#endregion
+ #endregion
-#region 静态调用。
+ #region 静态调用。
/// 使用 Logger.Default 写入日志,自动添加时间和日期,多个 Content 参数将以“ | ”分隔。
public static void Write(params object[] content) => Default.Invoke(() =>
@@ -340,7 +348,7 @@ namespace Apewer
item.Content = MergeContent(content);
if (console) ToConsole(item);
- if (file) ToFile(item);
+ if (file) ToFile(item, Default);
}
});
@@ -356,11 +364,11 @@ namespace Apewer
item.Content = MergeContent(content);
if (console) ToConsole(item);
- if (file) ToFile(item);
+ if (file) ToFile(item, Default);
}
});
-#endregion
+ #endregion
}
diff --git a/Apewer/NetworkUtility.cs b/Apewer/NetworkUtility.cs
index ac3caba..a401f74 100644
--- a/Apewer/NetworkUtility.cs
+++ b/Apewer/NetworkUtility.cs
@@ -69,9 +69,7 @@ namespace Apewer
return GetUtcFromNtp(endpoint, timeout);
}
}
- catch (Exception ex)
- {
- }
+ catch { }
return ClockUtility.Zero;
}
@@ -103,9 +101,7 @@ namespace Apewer
var utc = (new DateTime(1900, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).AddMilliseconds(milliseconds);
return utc;
}
- catch (Exception ex)
- {
- }
+ catch { }
return ClockUtility.Zero;
}
@@ -122,10 +118,9 @@ namespace Apewer
#region ARP
-#if NET40 || NET461
-
/// 发送 ARP 请求,获取目标 IP 地址对应设备的 MAC 地址,MAC 格式为 01-23-45-67-89-AB。
/// 目标 IP 地址。
+ /// 注:
仅 Windows 可用。
/// MAC 地址。
public static string SendARP(string ip)
{
@@ -146,8 +141,6 @@ namespace Apewer
return "";
}
-#endif
-
#endregion
#region Puny Code
diff --git a/Apewer/ObjectSet.cs b/Apewer/ObjectSet.cs
index 9f7a46a..3086a5c 100644
--- a/Apewer/ObjectSet.cs
+++ b/Apewer/ObjectSet.cs
@@ -14,9 +14,9 @@ namespace Apewer
{
///
- public ObjectSet(bool autotrim)
+ public ObjectSet(bool autoTrim)
{
- AutoTrim = autotrim;
+ AutoTrim = autoTrim;
}
///
@@ -62,9 +62,9 @@ namespace Apewer
}
/// 构造函数。
- public ObjectSet(bool autotrim)
+ public ObjectSet(bool autoTrim)
{
- _autotrim = autotrim;
+ _autotrim = autoTrim;
_origin = new Dictionary();
}
diff --git a/Apewer/RuntimeUtility.cs b/Apewer/RuntimeUtility.cs
index 5116277..b57b642 100644
--- a/Apewer/RuntimeUtility.cs
+++ b/Apewer/RuntimeUtility.cs
@@ -707,40 +707,35 @@ namespace Apewer
if (milliseconds > 0) Thread.Sleep(milliseconds);
}
- /// 在后台线程中执行,指定 Try 将忽略异常。
- [MethodImpl(MethodImplOptions.NoInlining)]
- public static Thread InBackground(Action action, bool @try = false)
+ private static void Invoke(Action action, bool @try = false)
{
- if (action == null) return null;
- Thread thread;
+ if (action == null) return;
if (@try)
{
- thread = new Thread(delegate (object v)
- {
- try
- {
- ((Action)v)();
- }
- catch { }
- });
+ try { action.Invoke(); } catch { }
}
- else
+ else { action.Invoke(); }
+ }
+
+ /// 在后台线程中执行,指定 Try 将忽略 Action 抛出的异常。
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static Thread InBackground(Action action, bool @try = false)
+ {
+ if (action == null) return null;
+ var thread = new Thread(delegate (object v)
{
- thread = new Thread(delegate (object v)
- {
- ((Action)v)();
- });
- }
+ Invoke(() => ((Action)v)(), @try);
+ });
thread.IsBackground = true;
thread.Start(action);
return thread;
}
- /// 启动线程。
- public static Thread StartThread(Action action, bool background = true)
+ /// 启动线程,指定 Try 将忽略 Action 抛出的异常。
+ public static Thread StartThread(Action action, bool background = true, bool @try = false)
{
if (action == null) return null;
- var thread = new Thread(new ThreadStart(action));
+ var thread = new Thread(new ThreadStart(() => Invoke(action, @try)));
thread.IsBackground = background;
thread.Start();
return thread;
diff --git a/Apewer/StorageUtility.cs b/Apewer/StorageUtility.cs
index 6862697..ce1457d 100644
--- a/Apewer/StorageUtility.cs
+++ b/Apewer/StorageUtility.cs
@@ -172,43 +172,42 @@ namespace Apewer
}
/// 获取指定目录的子目录,可指定递归子目录。
- public static List GetSubDirectories(string directory, bool recursive = false, bool recurPrecedence = true) => GetSubDirectories(directory, recursive ? -1 : 0, recurPrecedence);
+ public static List GetSubDirectories(string dirPath, bool recursive = false, bool recurPrecedence = true) => GetSubDirectories(dirPath, recursive ? -1 : 0, recurPrecedence);
/// 获取指定目录下子目录的路径。
- /// 顶级目录。
+ /// 顶级目录。
/// 子目录递归深度,指定为 0 时不递归,指定为 -1 时无限递归。
/// 优先排列递归项。
- public static List GetSubDirectories(string directory, int recurDepth, bool recurPrecedence = true)
+ public static List GetSubDirectories(string dirPath, int recurDepth, bool recurPrecedence = true)
{
var list = new List();
- if (string.IsNullOrEmpty(directory)) return list;
+ if (string.IsNullOrEmpty(dirPath)) return list;
var directories = new string[0];
-
try
{
- directories = Directory.GetDirectories(directory);
+ directories = Directory.GetDirectories(dirPath);
}
catch { }
- foreach (var d in directories)
+ foreach (var dir in directories)
{
var recurlist = new List();
if (recurDepth != 0)
{
var depth = (recurDepth > 0) ? recurDepth - 1 : recurDepth;
- var subrecur = GetSubDirectories(d, depth, recurPrecedence);
+ var subrecur = GetSubDirectories(dir, depth, recurPrecedence);
recurlist.AddRange(subrecur);
}
if (recurPrecedence)
{
list.AddRange(recurlist);
- list.Add(d);
+ list.Add(dir);
}
else
{
- list.Add(d);
+ list.Add(dir);
list.AddRange(recurlist);
}
}
@@ -217,33 +216,33 @@ namespace Apewer
}
/// 获取指定目录的子文件,可指定递归子目录。
- public static List GetSubFiles(string path, bool recursive = false, bool recurPrecedence = true) => GetSubFiles(path, recursive ? -1 : 0, recurPrecedence);
+ public static List GetSubFiles(string dirPath, bool recursive = false, bool recurPrecedence = true) => GetSubFiles(dirPath, recursive ? -1 : 0, recurPrecedence);
/// 获取指定目录下子文件的路径。
- /// 顶级目录。
+ /// 顶级目录。
/// 子目录递归深度,指定为 0 时不递归,指定为 -1 时无限递归。
/// 优先排列递归项。
- public static List GetSubFiles(string directory, int recurDepth, bool recurPrecedence = true)
+ public static List GetSubFiles(string dirPath, int recurDepth, bool recurPrecedence = true)
{
var list = new List();
- if (string.IsNullOrEmpty(directory)) return list;
+ if (string.IsNullOrEmpty(dirPath)) return list;
var dirs = new List();
if (recurDepth == 0)
{
- dirs.Add(directory);
+ dirs.Add(dirPath);
}
else
{
- var recurdicrotylist = GetSubDirectories(directory, recurDepth, recurPrecedence);
+ var recurdicrotylist = GetSubDirectories(dirPath, recurDepth, recurPrecedence);
if (recurPrecedence)
{
dirs.AddRange(recurdicrotylist);
- dirs.Add(directory);
+ dirs.Add(dirPath);
}
else
{
- dirs.Add(directory);
+ dirs.Add(dirPath);
dirs.AddRange(recurdicrotylist);
}
}
@@ -459,16 +458,61 @@ namespace Apewer
}
/// 读取文件,获取文件内容。当文件为 UTF-8 文本文件时,可去除 BOM 头。
+ /// 注:
字节数组最大为 2GB;
此方法不抛出异常,读取失时返回空字节数组。
public static byte[] ReadFile(string path, bool wipeBom = false)
{
+ const int Buffer = 1048576;
+
if (!FileExists(path)) return Constant.EmptyBytes;
lock (Locker)
{
try
{
- var bytes = File.ReadAllBytes(path);
- if (wipeBom) bytes = BinaryUtility.WipeTextBom(bytes);
- return bytes;
+ var result = new byte[0];
+ using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
+ {
+ if (!stream.CanRead) return Constant.EmptyBytes;
+ stream.Position = 0;
+
+ var length = stream.Length;
+ if (length < 1) return Constant.EmptyBytes;
+ if (length > int.MaxValue) return Constant.EmptyBytes;
+
+ var offset = 0L;
+ if (wipeBom && length >= 3)
+ {
+ var head = new byte[3];
+ stream.Read(head, 0, 3);
+ if (BinaryUtility.ContainsBOM(head))
+ {
+ var capacity = length - 3;
+ result = new byte[capacity];
+ }
+ else
+ {
+ result = new byte[length];
+ result[0] = head[0];
+ result[1] = head[1];
+ result[2] = head[2];
+ offset = 3;
+ }
+ }
+ else
+ {
+ result = new byte[length];
+ }
+
+ var block = new byte[Buffer];
+ while (true)
+ {
+ var read = stream.Read(block, 0, Buffer);
+ if (read < 1) break;
+
+ Array.Copy(block, 0, result, offset, read);
+ offset += read;
+ }
+ }
+ return result;
}
catch { }
}
diff --git a/Apewer/Surface/FormsUtility.cs b/Apewer/Surface/FormsUtility.cs
index a701b47..9deaa94 100644
--- a/Apewer/Surface/FormsUtility.cs
+++ b/Apewer/Surface/FormsUtility.cs
@@ -6,6 +6,7 @@ using System.IO;
using System.Runtime.InteropServices;
using System.Security;
using System.Reflection;
+using System.Diagnostics;
#if NETFX || NETCORE
using System.Drawing.Drawing2D;
@@ -14,6 +15,8 @@ using System.Drawing.Text;
using System.Windows.Forms;
#endif
+using static Apewer.Internals.Interop.SHCore;
+
namespace Apewer.Surface
{
@@ -25,15 +28,40 @@ namespace Apewer.Surface
/// 线程锁。
public static object ThreadLocker = new object();
+ /// 已使用系统 DPI 设置。
+ public static bool UsedSystemDPI { get; private set; }
+
+ internal static Nullable DpiScale { get; set; }
+
#if NETFX || NETCORE
/// 窗体启动初始化。
[STAThread]
- public static void StartInitialization()
+ public static void StartInitialization(bool useSystemDPI = false)
{
Control.CheckForIllegalCrossThreadCalls = false;
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
+ // Application.SetCompatibleTextRenderingDefault(true);
+
+ if (useSystemDPI) UseSystemDPI();
+ }
+
+ /// 使用系统的 DPI 设置。
+ public static void UseSystemDPI()
+ {
+ if (UsedSystemDPI) return;
+ UsedSystemDPI = true;
+
+#if NETCORE
+ Application.SetHighDpiMode(HighDpiMode.PerMonitorV2);
+#else
+ SetProcessDpiAwareness(PROCESS_DPI_AWARENESS.Process_System_DPI_Aware);
+ Marshal.GetLastWin32Error();
+ PROCESS_DPI_AWARENESS awareness;
+ GetProcessDpiAwareness(Process.GetCurrentProcess().Handle, out awareness);
+ Marshal.GetLastWin32Error();
+#endif
}
/// 禁用跨线程调用检查。
@@ -376,6 +404,16 @@ namespace Apewer.Surface
#endregion
+ #region Aero
+
+ /// 获取 DWM 的 Composition 启用状态。
+ public static bool DwmIsCompositionEnabled
+ {
+ get => DwmApi.DwmIsCompositionEnabled();
+ }
+
+ #endregion
+
#region 窗体。
#if NETFX || NETCORE
diff --git a/Apewer/Surface/NormalForm.cs b/Apewer/Surface/NormalForm.cs
index 0ed8064..221110d 100644
--- a/Apewer/Surface/NormalForm.cs
+++ b/Apewer/Surface/NormalForm.cs
@@ -1,8 +1,11 @@
#if NETFX || NETCORE
+using Apewer.Internals.Interop;
using System;
using System.Collections.Generic;
+using System.Drawing;
using System.Text;
+using System.Windows.Forms;
namespace Apewer.Surface
{
@@ -47,6 +50,69 @@ namespace Apewer.Surface
InitializeComponent();
}
+ /// 获取 DPI 缩放比例。
+ public double DpiScale
+ {
+ get
+ {
+ if (FormsUtility.DpiScale == null)
+ {
+ using (var g = CreateGraphics())
+ {
+ FormsUtility.DpiScale = g.DpiX / 96F;
+ }
+ }
+ return FormsUtility.DpiScale.Value;
+ }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ protected override void WndProc(ref Message m)
+ {
+ // WM_DPICHANGED = 0x02E0
+ base.WndProc(ref m);
+ }
+
+ #region Aero
+
+ private bool AeroEnabled = false;
+
+ /// 启用 Aero 透明效果。
+ private void EnableAero()
+ {
+ AeroEnabled = true;
+ Load += (s, e) =>
+ {
+ if (!FormsUtility.DwmIsCompositionEnabled) return;
+
+ var width = Width;
+ var height = Height;
+
+ var margins = new MARGINS();
+ // margins.Right = -1; // 全窗体透明。
+ margins.Right = margins.Left = margins.Top = margins.Bottom = width + height;
+ DwmApi.DwmExtendFrameIntoClientArea(Handle, ref margins);
+ };
+ }
+
+ ///
+ protected override void OnPaintBackground(PaintEventArgs e)
+ {
+ base.OnPaintBackground(e);
+ if (AeroEnabled)
+ {
+ if (FormsUtility.DwmIsCompositionEnabled)
+ {
+ e.Graphics.Clear(Color.Black);
+ }
+ }
+ }
+
+ #endregion
+
}
}
diff --git a/Apewer/SystemUtility.cs b/Apewer/SystemUtility.cs
index 5b461d1..14fc07b 100644
--- a/Apewer/SystemUtility.cs
+++ b/Apewer/SystemUtility.cs
@@ -18,6 +18,11 @@ using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
+using static Apewer.Internals.Interop.Constant;
+using static Apewer.Internals.Interop.AdvApi32;
+using static Apewer.Internals.Interop.Kernel32;
+using static Apewer.Internals.Interop.User32;
+
namespace Apewer
{
@@ -48,6 +53,36 @@ namespace Apewer
/// 当前操作系统是 Linux。
public static bool IsLinux { get => CheckOsType() == 3; }
+#if NETFX
+
+ /// 获取内存容量。
+ public static long RamTotal
+ {
+ get
+ {
+ try
+ {
+ var mos = new ManagementObjectSearcher();
+ mos.Query = new SelectQuery("win32_physicalmemory", "", new string[] { "capacity" });
+ var moc = mos.Get();
+ var total = 0L;
+ foreach (var mbo in moc)
+ {
+ var value = mbo.Properties["capacity"].Value;
+ if (value != null)
+ {
+ total += long.Parse(value.ToString());
+ }
+ }
+ return total;
+ }
+ catch { }
+ return 0;
+ }
+ }
+
+#endif
+
#endregion
#region 进程。
@@ -323,40 +358,30 @@ namespace Apewer
#endregion
- #region 硬件。
+ #region 硬件 & 电源。
private static void ExitWindows(int flag)
{
- bool ok;
TokenPrivilege tp;
- IntPtr hproc = Kernel32.GetCurrentProcess();
+ IntPtr hproc = GetCurrentProcess();
IntPtr htok = IntPtr.Zero;
- ok = AdvApi32.OpenProcessToken(hproc, Constant.TOKEN_ADJUST_PRIVILEGES | Constant.TOKEN_QUERY, ref htok);
+ OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok);
tp.Count = 1;
tp.Luid = 0;
- tp.Attr = Constant.SE_PRIVILEGE_ENABLED;
- ok = AdvApi32.LookupPrivilegeValueA(null, Constant.SE_SHUTDOWN_NAME, ref tp.Luid);
- ok = AdvApi32.AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero);
- ok = User32.ExitWindowsEx(flag, 0);
+ tp.Attr = SE_PRIVILEGE_ENABLED;
+ LookupPrivilegeValue(null, SE_SHUTDOWN_NAME, ref tp.Luid);
+ AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero);
+ ExitWindowsEx(flag, 0);
}
- /// 强制关机。
- public static void Shutdown()
- {
- ExitWindows(Constant.EWX_FORCE | Constant.EWX_POWEROFF);
- }
+ /// 关机。
+ public static void Shutdown() => ExitWindows(EWX_FORCE | EWX_POWEROFF);
- /// 强制重启。
- public static void Reboot()
- {
- ExitWindows(Constant.EWX_FORCE | Constant.EWX_REBOOT);
- }
+ /// 重启。
+ public static void Reboot() => ExitWindows(EWX_FORCE | EWX_REBOOT);
- /// 强制注销。
- public static void LogOff()
- {
- ExitWindows(Constant.EWX_FORCE | Constant.EWX_LOGOFF);
- }
+ /// 注销。
+ public static void LogOff() => ExitWindows(EWX_FORCE | EWX_LOGOFF);
#if NETFX
@@ -561,12 +586,22 @@ namespace Apewer
User32.mouse_Callback(MouseCallbackFlag.RightUp, x, y, 0, UIntPtr.Zero);
}
+ private static IntPtr GetHINSTANCE(Module m)
+ {
+ if ((object)m == null)
+ {
+ throw new ArgumentNullException("m");
+ }
+ return (IntPtr)(-1);
+ }
+
/// 钩住鼠标。当前线程必须具有消息循环。
private static int HookMouse(Func func)
{
if (func == null) return 0;
var proc = new HookProc(LowLevelMouseProc);
- var hInstance = Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]);
+ // var hInstance = Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]);
+ var hInstance = GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]);
var hook = 0;
hook = User32.SetWindowsHookEx(Constant.WH_MOUSE_LL, (int nCode, IntPtr wParam, IntPtr lParam) =>
{
diff --git a/Apewer/Web/ApiProgram.cs b/Apewer/Web/ApiProgram.cs
index a0d2059..73355ad 100644
--- a/Apewer/Web/ApiProgram.cs
+++ b/Apewer/Web/ApiProgram.cs
@@ -23,7 +23,7 @@ namespace Apewer.Web
#if NETFX || NETCORE
/// 在当前线程运行标准应用程序消息循环。
- public static void RunMessageLoop() => Application.Run();
+ protected static void RunMessageLoop() => Application.Run();
#endif
@@ -106,7 +106,6 @@ namespace Apewer.Web
public partial class ApiProgram : IHttpHandler, IHttpModule
{
- private bool _initialized = false;
private Action _before = null;
/// 仅运行在 IIS 中时会对此类型创建实例。
@@ -116,7 +115,25 @@ namespace Apewer.Web
_before = before;
}
- #region IHttpHandler
+ #region Initialize
+
+ private static object InitLocker = new object();
+ private bool _initialized = false;
+
+ private void Initialize(Action action)
+ {
+ if (_initialized) return;
+ lock (InitLocker)
+ {
+ if (_initialized) return;
+ action?.Invoke();
+ _initialized = true;
+ }
+ }
+
+ #endregion
+
+ #region IHttpHandler
///
public bool IsReusable { get { return false; } }
@@ -138,18 +155,13 @@ namespace Apewer.Web
// return;
//}
- if (!_initialized)
- {
- _initialized = true;
- var before = _before;
- before?.Invoke();
- }
+ Initialize(_before);
ApiInvoker.IIS(context);
}
- #endregion
+ #endregion
- #region IHttpModule
+ #region IHttpModule
///
public void Dispose() { }
@@ -166,7 +178,7 @@ namespace Apewer.Web
};
}
- #endregion
+ #endregion
}
diff --git a/Apewer/Web/CronLog.cs b/Apewer/Web/CronLog.cs
index 68f5447..547b31b 100644
--- a/Apewer/Web/CronLog.cs
+++ b/Apewer/Web/CronLog.cs
@@ -44,7 +44,7 @@ namespace Apewer.Web
}
/// 向控制台写入文本的 Log 处理程序。
- public static void Console(string text) => Logger.Console.Text(nameof (CronLog), text);
+ public static void Console(string text) => Logger.Web.Text("Cron", text);
}
diff --git a/Apewer/Web/StaticController.cs b/Apewer/Web/StaticController.cs
index e4efc92..71301d8 100644
--- a/Apewer/Web/StaticController.cs
+++ b/Apewer/Web/StaticController.cs
@@ -14,11 +14,28 @@ namespace Apewer.Web
public StaticController()
{
AllowFunction = false;
- AfterInitialized = Execute;
+ AfterInitialized = () =>
+ {
+ if (Request == null || Request.Url == null) return;
+ var path = MapPath(Request.Url.AbsolutePath);
+ if (StorageUtility.DirectoryExists(path))
+ {
+ ExecuteDirectory(path);
+ return;
+ }
+ if (StorageUtility.FileExists(path))
+ {
+ ExecuteFile(path);
+ return;
+ }
+ Respond404(path);
+ };
}
+ #region property
+
/// 获取此静态站点的目录。
- public virtual string GetSiteDirectory()
+ protected virtual string GetSiteDirectory()
{
var app = WebUtility.AppDirectory;
var www = StorageUtility.CombinePath(app, "www");
@@ -26,6 +43,34 @@ namespace Apewer.Web
return www;
}
+ /// 响应 403 状态。
+ protected virtual void Respond403(string path) => Response.Error("403");
+
+ /// 响应 404 状态。
+ protected virtual void Respond404(string path) => Response.Error("404");
+
+ /// 从扩展名获取内容类型。
+ /// 例:
GetContentType("txt") = "text/plain"
+ protected virtual string GetContentType(string extension) => Mime(extension);
+
+ /// 按扩展名获取过期时间,单位为秒。默认值为 0(不缓存)。
+ /// 例:
GetExpires("txt") = 0
+ protected virtual int GetExpires(string extension) => 0;
+
+ /// 访问目录时,允许列出子项。默认值:false,不允许。
+ protected virtual bool AllowListChildren() => false;
+
+ string PrivateGetContentType(string extension)
+ {
+ var result = GetContentType(extension);
+ if (string.IsNullOrEmpty(result)) result = Mime(extension);
+ return result;
+ }
+
+ #endregion
+
+ #region common
+
/// 解析 URL 的路径,获取本地路径。
protected string MapPath(string urlPath)
{
@@ -43,24 +88,6 @@ namespace Apewer.Web
return path;
}
- /// 处理请求。
- public virtual void Execute()
- {
- if (Request == null || Request.Url == null) return;
- var path = MapPath(Request.Url.AbsolutePath);
- if (StorageUtility.DirectoryExists(path))
- {
- ExecuteDirectory(path);
- return;
- }
- if (StorageUtility.FileExists(path))
- {
- ExecuteFile(path);
- return;
- }
- Respond404(path);
- }
-
void ExecuteFile(string path)
{
if (!File.Exists(path))
@@ -78,8 +105,7 @@ namespace Apewer.Web
if (expires > 0) Response.Expires = expires;
// 按扩展名获取 Content-Type。
- var type = GetContentType(ext);
- // var name = Path.GetFileName(path);
+ var type = PrivateGetContentType(ext);
// Server Side Includes
if (ext == "html" || ext == "htm" || ext == "shtml")
@@ -101,13 +127,89 @@ namespace Apewer.Web
Respond404(path);
return;
}
+
var @default = GetDefaultFile(path);
- if (string.IsNullOrEmpty(@default)) Respond403(path);
- else ExecuteFile(@default);
+ if (!string.IsNullOrEmpty(@default))
+ {
+ ExecuteFile(@default);
+ return;
+ }
+
+ if (AllowListChildren())
+ {
+ Response.Data = ListChildren(path);
+ }
+ Respond403(path);
}
+ // Server Side Includes
+ string ReadWithSSI(string path, int recursive = 0)
+ {
+ if (recursive > 10) return "";
+
+ var input = StorageUtility.ReadFile(path, true);
+ if (input == null || input.LongLength < 1) return "";
+
+ // 尝试以 UTF-8 解码。
+ var html = TextUtility.FromBinary(input);
+ if (string.IsNullOrEmpty(html)) return "";
+
+ // 按首尾截取。
+ const string left = "";
+ const string head = "#include virtual=";
+ var sb = new StringBuilder();
+ var text = html;
+ while (true)
+ {
+ var offset = text.IndexOf(left);
+ if (offset < 0)
+ {
+ sb.Append(text);
+ break;
+ }
+ if (offset > 0)
+ {
+ sb.Append(text.Substring(0, offset));
+ text = text.Substring(offset + left.Length);
+ }
+ else text = text.Substring(left.Length);
+ var length = text.IndexOf(right);
+ if (length < 1)
+ {
+ sb.Append(left);
+ sb.Append(text);
+ break;
+ }
+ var inner = text.Substring(0, length);
+ var temp = inner.SafeTrim();
+ if (temp.StartsWith(head))
+ {
+ temp = temp.Substring(head.Length);
+ temp = temp.Replace("\"", "");
+ var subPath = MapPath(temp);
+ var subText = ReadWithSSI(subPath, recursive + 1);
+ if (subText != null && subText.Length > 0) sb.Append(subText);
+ }
+ else
+ {
+ sb.Append(left);
+ sb.Append(inner);
+ sb.Append(right);
+ }
+ text = text.Substring(length + right.Length);
+ }
+
+ var output = sb.ToString();
+ return output;
+ }
+
+ #endregion
+
+ #region static
+
// 获取目录中的默认文件,返回文件的完整路径。
- string GetDefaultFile(string directory)
+ static string GetDefaultFile(string directory)
{
var subs = StorageUtility.GetSubFiles(directory);
if (subs.Count < 0) return null;
@@ -130,18 +232,16 @@ namespace Apewer.Web
}
/// 列出子项。可指定筛选器,传入完整路径。
- public void ListChildren(string directory)
+ static Json ListChildren(string directory)
{
- if (!Directory.Exists(directory)) return;
-
+ if (!Directory.Exists(directory)) return null;
var json = Json.NewObject();
json.SetProperty("directories", ListDirectories(directory));
json.SetProperty("files", ListFiles(directory));
-
- Response.Data = json;
+ return json;
}
- Json ListDirectories(string directory)
+ static Json ListDirectories(string directory)
{
var array = Json.NewArray();
var subs = StorageUtility.GetSubDirectories(directory);
@@ -171,7 +271,7 @@ namespace Apewer.Web
return array;
}
- Json ListFiles(string directory)
+ static Json ListFiles(string directory)
{
var array = Json.NewArray();
var subs = StorageUtility.GetSubFiles(directory);
@@ -199,20 +299,7 @@ namespace Apewer.Web
return array;
}
- /// 响应 403 状态。
- public virtual void Respond403(string path)
- {
- Response.Error("403");
- }
-
- /// 响应 404 状态。
- public virtual void Respond404(string path)
- {
- Response.Error("404");
- }
-
- /// 按扩展名获取 Content-Type。
- public virtual string GetContentType(string extension)
+ static string Mime(string extension)
{
if (!string.IsNullOrEmpty(extension))
{
@@ -335,70 +422,7 @@ namespace Apewer.Web
return "application/octet-stream";
}
- /// 按扩展名获取过期时间,单位为秒,默认值为 0(不缓存)。
- public virtual int GetExpires(string extension) => 0;
-
- // Server Side Includes
- string ReadWithSSI(string path, int recursive = 0)
- {
- if (recursive > 10) return "";
-
- var input = StorageUtility.ReadFile(path, true);
- if (input == null || input.LongLength < 1) return "";
-
- // 尝试以 UTF-8 解码。
- var html = TextUtility.FromBinary(input);
- if (string.IsNullOrEmpty(html)) return "";
-
- // 按首尾截取。
- const string left = "";
- const string head = "#include virtual=";
- var sb = new StringBuilder();
- var text = html;
- while (true)
- {
- var offset = text.IndexOf(left);
- if (offset < 0)
- {
- sb.Append(text);
- break;
- }
- if (offset > 0)
- {
- sb.Append(text.Substring(0, offset));
- text = text.Substring(offset + left.Length);
- }
- else text = text.Substring(left.Length);
- var length = text.IndexOf(right);
- if (length < 1)
- {
- sb.Append(left);
- sb.Append(text);
- break;
- }
- var inner = text.Substring(0, length);
- var temp = inner.SafeTrim();
- if (temp.StartsWith(head))
- {
- temp = temp.Substring(head.Length);
- temp = temp.Replace("\"", "");
- var subPath = MapPath(temp);
- var subText = ReadWithSSI(subPath, recursive + 1);
- if (subText != null && subText.Length > 0) sb.Append(subText);
- }
- else
- {
- sb.Append(left);
- sb.Append(inner);
- sb.Append(right);
- }
- text = text.Substring(length + right.Length);
- }
-
- var output = sb.ToString();
- return output;
- }
+ #endregion
}
diff --git a/Apewer/_ChangeLog.md b/Apewer/_ChangeLog.md
index 183a601..fd15662 100644
--- a/Apewer/_ChangeLog.md
+++ b/Apewer/_ChangeLog.md
@@ -1,6 +1,12 @@
### 最新提交
+### 6.3.3
+- 全局:恢复 .NET Standard 2.0 的输出,用于支持 .NET Framework 4.6.1;
+- StorageUtility:优化了 ReadFile 的内存占用;
+- Web:优化 ApiProgram 的初始化,减少并发时的问题;
+- Web:Static 控制器支持列出子项。
+
### 6.3.2
- RuntimeUtility:调整了 Assembly 的方法名称;
- Surface:迁移获取字形的方法到 FormsUtility,不再提供 FontInstance 类;