6 Commits

  1. 26
      Apewer.Windows/Internals/RegistryHelper.cs
  2. 39
      Apewer.Windows/WinForm/Tray.cs
  3. 66
      Apewer.Windows/WinForm/WindowsService.cs
  4. 4
      Apewer.Windows/WindowsUtility.cs
  5. 4
      Apewer/Web/ApiOptions.cs
  6. 25
      Apewer/Web/ApiProcessor.cs
  7. 9
      Apewer/Web/ApiUtility.cs
  8. 11
      Apewer/_Extensions.cs

26
Apewer.Windows/Internals/RegHelper.cs → Apewer.Windows/Internals/RegistryHelper.cs

@ -7,7 +7,7 @@ namespace Apewer.Internals
{
/// <summary>注册表。</summary>
static class RegHelper
static class RegistryHelper
{
/// <summary>用户登录后的启动项。</summary>
@ -21,12 +21,12 @@ namespace Apewer.Internals
/// <remarks>系统信息,对所有用户生效,设置需要管理员权限。</remarks>
public static RegistryKey LocalMachine { get => Registry.LocalMachine; }
/// <summary>获取字符串。</summary>
/// <summary>获取。</summary>
/// <param name="root">注册表存储区。</param>
/// <param name="key">路径。</param>
/// <param name="name">名称。</param>
/// <returns>字符串的值。获取失败时返回 NULL 值。</returns>
public static string Get(RegistryKey root, string key, string name)
/// <returns>值。获取失败时返回 NULL 值。</returns>
public static object Get(RegistryKey root, string key, string name)
{
try
{
@ -34,18 +34,24 @@ namespace Apewer.Internals
using
#endif
var rkey = root.OpenSubKey(key, RegistryKeyPermissionCheck.ReadSubTree);
var names = rkey.GetSubKeyNames();
var names = rkey.GetValueNames();
if (names.Contains(name))
{
var obj = rkey.GetValue(name, null);
var str = obj as string;
return str;
var value = rkey.GetValue(name, null);
return value;
}
}
catch { }
return null;
}
/// <summary>获取字符串。</summary>
/// <param name="root">注册表存储区。</param>
/// <param name="key">路径。</param>
/// <param name="name">名称。</param>
/// <returns>字符串的值。获取失败时返回 NULL 值。</returns>
public static string GetString(RegistryKey root, string key, string name) => Get(root, key, name) as string;
/// <summary>设置字符串,指定 value 为 NULL 可删除该值。</summary>
/// <param name="root">注册表存储区。</param>
/// <param name="key">路径。</param>
@ -75,7 +81,7 @@ namespace Apewer.Internals
/// <remarks>错误消息。</remarks>
public static void SetRun(string name, string command)
{
var old = Get(CurrentUser, Run, name);
var old = GetString(CurrentUser, Run, name);
if (old.IsEmpty()) Set(CurrentUser, Run, name, command);
}
@ -93,7 +99,7 @@ namespace Apewer.Internals
{
var path = Application.ExecutablePath;
var name = Path.GetFileNameWithoutExtension(path);
var value = Get(CurrentUser, Run, name);
var value = GetString(CurrentUser, Run, name);
return value == path;
}
set

39
Apewer.Windows/WinForm/Tray.cs

@ -311,16 +311,22 @@ namespace Apewer.WinForm
#region run
/// <summary>唯一实例。</summary>
public static Tray Instance { get; private set; }
/// <summary>已启动的服务名称。</summary>
public static string ServiceName { get; private set; }
/// <summary>在当前线程运行托盘程序,并启动消息循环。</summary>
/// <param name="action">启动托盘后执行的程序。</param>
/// <summary>在当前线程运行托盘程序,并启动消息循环。此方法应在主线程中调用,并且此方法将阻塞当前线程。</summary>
/// <remarks>托盘启动后,将在后台线程执行参数中指定的程序。</remarks>
/// <param name="action">(在后台线程执行)启动托盘后执行的程序。</param>
/// <exception cref="ArgumentNullException" />
/// <exception cref="InvalidOperationException" />
[STAThread]
public static void Run(Action<Tray> action)
{
if (action == null) throw new ArgumentNullException(nameof(action));
if (Instance != null) throw new InvalidOperationException($"已存在实例,无法再次启动。");
Control.CheckForIllegalCrossThreadCalls = false;
Application.EnableVisualStyles();
@ -337,42 +343,21 @@ namespace Apewer.WinForm
}
}
var instance = new Tray(action);
Instance = new Tray(action);
// action.Invoke(instance);
Application.Run();
}
/// <summary>启动服务。</summary>
/// <param name="onStart">服务启动后执行的程序。</param>
/// <param name="onStop">停止服务时执行的程序。</param>
/// <param name="onStart">(在后台线程执行)服务启动后执行的程序。</param>
/// <param name="onStop">(同步执行)停止服务时执行的程序。</param>
public static void Service(Action onStart, Action onStop = null)
{
var processName = Process.GetCurrentProcess().ProcessName;
var service = new TrayService(processName, onStart, onStop);
var service = new WindowsService(processName, onStart, onStop);
ServiceBase.Run(service);
}
class TrayService : ServiceBase
{
Action on_start;
Action on_stop;
public TrayService(string serviceName, Action onStart, Action onStop)
{
if (serviceName.IsEmpty()) throw new ArgumentNullException(nameof(serviceName));
if (onStart == null) throw new ArgumentNullException(nameof(onStart));
ServiceName = serviceName;
on_start = onStart;
on_stop = onStop;
}
protected override void OnStart(string[] args) => RuntimeUtility.InBackground(on_start);
protected override void OnStop() => on_stop?.Invoke();
}
#endregion
}

66
Apewer.Windows/WinForm/WindowsService.cs

@ -0,0 +1,66 @@
using Apewer.Internals.Interop;
using System;
using System.Diagnostics;
using System.ServiceProcess;
namespace Apewer.WinForm
{
/// <summary>Windows 服务。</summary>
public class WindowsService : ServiceBase
{
Action on_start = null;
Action on_stop = null;
/// <summary>创建 Windows 服务实例。</summary>
/// <param name="serviceName">服务名称。</param>
/// <param name="onStart">服务启动后,在后台线程执行的程序。</param>
/// <param name="onStop">服务停止前,同步执行的程序。</param>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
public WindowsService(string serviceName, Action onStart, Action onStop)
{
if (serviceName.IsEmpty()) throw new ArgumentNullException(nameof(serviceName));
ServiceName = serviceName;
on_start = onStart;
on_stop = onStop;
}
/// <summary>创建 Windows 服务实例。</summary>
/// <remarks>默认使用当前进程名称作为服务名称。</remarks>
/// <param name="onStart">服务启动后,在后台线程执行的程序。</param>
/// <param name="onStop">服务停止前,同步执行的程序。</param>
/// <exception cref="ArgumentException"></exception>
public WindowsService(Action onStart, Action onStop) : this(Process.GetCurrentProcess().ProcessName, onStart, onStop) { }
/// <summary>服务启动时执行的程序。</summary>
protected override void OnStart(string[] args) => RuntimeUtility.InBackground(on_start);
/// <summary>服务停止时执行的程序。</summary>
protected override void OnStop() => on_stop?.Invoke();
/// <summary>启动 Windows 服务实例。</summary>
/// <param name="serviceName">服务名称。</param>
/// <param name="onStart">服务启动后,在后台线程执行的程序。</param>
/// <param name="onStop">服务停止前,同步执行的程序。</param>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
public static WindowsService Run(string serviceName, Action onStart, Action onStop = null)
{
var service = new WindowsService(serviceName, onStart, onStop);
ServiceBase.Run(service);
return service;
}
/// <summary>启动 Windows 服务实例。</summary>
/// <remarks>默认使用当前进程名称作为服务名称。</remarks>
/// <param name="onStart">服务启动后,在后台线程执行的程序。</param>
/// <param name="onStop">服务停止前,同步执行的程序。</param>
/// <exception cref="ArgumentException"></exception>
public static WindowsService Run(Action onStart, Action onStop = null) => Run(Process.GetCurrentProcess().ProcessName, onStart, onStop);
}
}

4
Apewer.Windows/WindowsUtility.cs

@ -991,11 +991,11 @@ namespace Apewer
/// <summary>设置当前用户的启动项。</summary>
/// <param name="name">启动项的名称。</param>
/// <param name="command">执行的命令。</param>
public static void SetRun(string name, string command) => RegHelper.SetRun(name, command);
public static void SetRun(string name, string command) => RegistryHelper.SetRun(name, command);
/// <summary>取消当前用户的启动项。</summary>
/// <param name="name">启动项的名称。</param>
public static void CancelRun(string name) => RegHelper.CancelRun(name);
public static void CancelRun(string name) => RegistryHelper.CancelRun(name);
#endregion

4
Apewer/Web/ApiOptions.cs

@ -26,6 +26,10 @@ namespace Apewer.Web
/// <remarks>默认值:不允许,响应空。</remarks>
public bool AllowFavIcon { get; set; } = false;
/// <summary>允许解析 OPTIONS 请求。</summary>
/// <remarks>默认值:不允许,直接返回 0 字节的 text/plain 内容。</remarks>
public bool AllowOptions { get; set; } = false;
/// <summary>允许解析 robots.txt 请求。</summary>
/// <remarks>默认值:不允许,拒绝搜索引擎收录根目录。</remarks>
public bool AllowRobots { get; set; } = false;

25
Apewer/Web/ApiProcessor.cs

@ -92,9 +92,16 @@ namespace Apewer.Web
url = _context.Provider.GetUrl();
if (url == null) return "URL 无效。";
// Method
method = _context.Provider.GetMethod();
if (method == HttpMethod.NULL) return "HTTP 方法无效。";
if (method == HttpMethod.OPTIONS) return null;
switch (method)
{
case HttpMethod.NULL:
return "HTTP 方法无效。";
case HttpMethod.OPTIONS:
if (!_context.Options.AllowOptions) return null;
break;
}
// favicon.ico
var lowerPath = TextUtility.AssureStarts(TextUtility.Lower(url.AbsolutePath), "/");
@ -124,6 +131,16 @@ namespace Apewer.Web
// 寻找入口。
void Invoke()
{
// OPTIONS
if (_context.Request.Method == HttpMethod.OPTIONS)
{
if (!_context.Options.AllowOptions)
{
_context.Response.Model = new ApiTextModel("");
return;
}
}
// 路由
if (_context.Options.UseRoute)
{
@ -134,7 +151,6 @@ namespace Apewer.Web
{
_context.ApiAction = action;
Invoke(action);
_context.Response.Duration = Duration(_context.Beginning);
return;
}
}
@ -145,7 +161,6 @@ namespace Apewer.Web
var appName = _context.Request.Application;
var application = _context.Entries.GetApplication(appName);
Invoke(application);
_context.Response.Duration = Duration(_context.Beginning);
return;
}
@ -580,7 +595,7 @@ namespace Apewer.Web
}
// Content-Type 检查。
if (options.WithContentTypeOptions || options.Default != null)
if (options.WithContentTypeOptions)
{
merged.Add("X-Content-Type-Options", "nosniff");
}

9
Apewer/Web/ApiUtility.cs

@ -547,6 +547,15 @@ namespace Apewer.Web
Error(context.Response, text);
}
/// <summary>渲染为 JSON,并设置到 Response 的 Data 属性。</summary>
public static void SetCamelJsonToResponseData(ApiContext context, object result)
{
if (context == null) return;
if (context.Response == null) return;
if (result == null) return;
context.Response.Data = Json.From(result).Camel();
}
#endregion
#region ApiRequest

11
Apewer/_Extensions.cs

@ -358,6 +358,17 @@ public static class Extensions
#region Logger
/// <summary>将日志保存到文件,并设置文件的保留天数。</summary>
/// <param name="logger">日志记录程序。</param>
/// <param name="days">日志文件的保留天数。指定为 -1 时将永久保存。</param>
/// <exception cref="ArgumentNullException"></exception>
public static void UseFile(this Logger logger, int days = 14)
{
if (logger == null) throw new ArgumentNullException(nameof(logger));
logger.UseFile = true;
logger.FileReserved = days;
}
/// <summary>记录文本。多个 Content 参数将以“ | ”分隔。</summary>
public static void Write(this Logger logger, params object[] content) => logger?.Output(null, content, null);

Loading…
Cancel
Save