7 changed files with 642 additions and 116 deletions
@ -0,0 +1,83 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Windows.Forms; |
|||
|
|||
namespace Apewer.WinForm |
|||
{ |
|||
|
|||
/// <summary>Windows 窗体工具。</summary>
|
|||
public static class Extensions |
|||
{ |
|||
|
|||
static void MenuItemEventHandler(object sender, EventArgs e) |
|||
{ |
|||
#if NETFRAMEWORK
|
|||
var mi = sender as System.Windows.Forms.MenuItem; |
|||
if (mi != null) |
|||
{ |
|||
var tag = mi.Tag as MenuItem; |
|||
if (tag != null && tag.Action != null) tag.Action.Invoke(tag); |
|||
} |
|||
#endif
|
|||
|
|||
var tsmi = sender as System.Windows.Forms.ToolStripMenuItem; |
|||
if (tsmi != null) |
|||
{ |
|||
var tag = tsmi.Tag as MenuItem; |
|||
if (tag != null && tag.Action != null) tag.Action.Invoke(tag); |
|||
} |
|||
} |
|||
|
|||
#if NETFRAMEWORK
|
|||
|
|||
/// <summary>生成 <see cref="System.Windows.Forms.ContextMenu" /> 实例。</summary>
|
|||
/// <exception cref="ArgumentNullException"></exception>
|
|||
public static ContextMenu ContextMenu(this IEnumerable<MenuItem> items) |
|||
{ |
|||
if (items == null) throw new ArgumentNullException(nameof(items)); |
|||
|
|||
var cm = new ContextMenu(); |
|||
foreach (var item in items) |
|||
{ |
|||
var isLine = item == null || item.Text == null || item.Text == "" || item.Text == "-"; |
|||
var text = isLine ? "-" : item?.Text; |
|||
var mi = new System.Windows.Forms.MenuItem(text, MenuItemEventHandler); |
|||
mi.Enabled = !isLine && item.Action != null; |
|||
mi.Tag = item; |
|||
|
|||
cm.MenuItems.Add(mi); |
|||
} |
|||
|
|||
return cm; |
|||
} |
|||
|
|||
#endif
|
|||
|
|||
/// <summary>生成 <see cref="ContextMenuStrip" /> 实例。</summary>
|
|||
/// <exception cref="ArgumentNullException"></exception>
|
|||
public static ContextMenuStrip ContextMenuStrip(this IEnumerable<MenuItem> items) |
|||
{ |
|||
if (items == null) throw new ArgumentNullException(nameof(items)); |
|||
|
|||
var cms = new ContextMenuStrip(); |
|||
foreach (var item in items) |
|||
{ |
|||
if (item == null) continue; |
|||
|
|||
var isLine = item.Text == null || item.Text == "" || item.Text == "-"; |
|||
var text = isLine ? "-" : item.Text; |
|||
var tsmi = new ToolStripMenuItem(text, item.Image, MenuItemEventHandler); |
|||
tsmi.AutoSize = true; |
|||
tsmi.Height = isLine ? 19 : 30; |
|||
tsmi.Enabled = item.Action != null && item.Action != null; |
|||
tsmi.Tag = item; |
|||
|
|||
cms.Items.Add(tsmi); |
|||
} |
|||
|
|||
return cms; |
|||
} |
|||
|
|||
} |
|||
|
|||
} |
@ -0,0 +1,148 @@ |
|||
using System; |
|||
using System.Runtime.InteropServices; |
|||
using System.Windows.Forms; |
|||
|
|||
namespace Apewer.WinForm |
|||
{ |
|||
|
|||
/// <summary>热键。</summary>
|
|||
public sealed class HotKey : IMessageFilter |
|||
{ |
|||
|
|||
#region instance
|
|||
|
|||
IntPtr _pointer = IntPtr.Zero; |
|||
Keys _key = Keys.None; |
|||
ModifierKey _modifier = ModifierKey.None; |
|||
|
|||
int _id = 0; |
|||
Action<HotKey> _callback = null; |
|||
|
|||
/// <summary>热键的标识符。 </summary>
|
|||
public int Id { get => _id; } |
|||
|
|||
/// <summary>修饰键。</summary>
|
|||
public ModifierKey ModifierKey { get => _modifier; } |
|||
|
|||
/// <summary>按键。</summary>
|
|||
public Keys Key { get => _key; } |
|||
|
|||
private HotKey(IntPtr pointer, Keys key, ModifierKey modifier, Action<HotKey> callback) |
|||
{ |
|||
if (callback == null) throw new ArgumentNullException(nameof(callback), "回调函数不能为空。"); |
|||
switch (key) |
|||
{ |
|||
case Keys.KeyCode: |
|||
case Keys.Modifiers: |
|||
case Keys.None: |
|||
case Keys.Shift: |
|||
case Keys.Control: |
|||
case Keys.Alt: |
|||
throw new ArgumentException($"按键【{key}】无法注册为热键。"); |
|||
} |
|||
|
|||
// 应用程序必须在0x0000到0xBFFF的范围内指定 ID 值。
|
|||
// 共享 DLL 必须通过 0xFFFF(GlobalAddAtom 函数返回的范围)指定0xC000区域中的值。
|
|||
_id = ((int)modifier << 16) & (int)key; |
|||
|
|||
_pointer = pointer; |
|||
_callback = callback; |
|||
_key = key; |
|||
_modifier = modifier; |
|||
} |
|||
|
|||
/// <summary></summary>
|
|||
public override string ToString() |
|||
{ |
|||
var alt = (ModifierKey & ModifierKey.ALT) == ModifierKey.ALT ? "ALT + " : ""; |
|||
var ctrl = (ModifierKey & ModifierKey.CTRL) == ModifierKey.CTRL ? "CTRL + " : ""; |
|||
var shift = (ModifierKey & ModifierKey.SHIFT) == ModifierKey.SHIFT ? "SHIFT + " : ""; |
|||
var win = (ModifierKey & ModifierKey.WIN) == ModifierKey.WIN ? "WIN + " : ""; |
|||
var result = $"{alt}{ctrl}{shift}{win}{Key}"; |
|||
return result; |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
#region bind
|
|||
|
|||
const int WM_HOTKEY = 0x0312; |
|||
|
|||
[DllImport("user32.dll")] |
|||
static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, Keys vk); |
|||
|
|||
[DllImport("user32.dll")] |
|||
static extern bool UnregisterHotKey(IntPtr hWnd, int id); |
|||
|
|||
bool IMessageFilter.PreFilterMessage(ref Message m) |
|||
{ |
|||
if (m.Msg == WM_HOTKEY && (int)m.WParam == _id) |
|||
{ |
|||
_callback?.Invoke(this); |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
void Bind() |
|||
{ |
|||
var success = RegisterHotKey(_pointer, _id, (int)_modifier, _key); |
|||
if (!success) |
|||
{ |
|||
var error = Marshal.GetLastWin32Error(); |
|||
if (error == 1409) throw new Exception($"热键【{ToString()}】已被占用。"); |
|||
throw new Exception($"错误 {error}:注册热键【{ToString()}】失败。"); |
|||
} |
|||
|
|||
Application.AddMessageFilter(this); |
|||
} |
|||
|
|||
/// <summary>释放热键。</summary>
|
|||
public void Release() |
|||
{ |
|||
UnregisterHotKey(Application.OpenForms[0].Handle, _id); |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
// Virtual-Key 代码
|
|||
// https://learn.microsoft.com/zh-cn/windows/win32/inputdev/virtual-key-codes
|
|||
|
|||
/// <summary>绑定热键。</summary>
|
|||
/// <param name="form">将接收热键生成的 WM_HOTKEY(0x0312)消息的窗口句柄。</param>
|
|||
/// <param name="key">热键的虚拟键代码。</param>
|
|||
/// <param name="modifier">修饰键。</param>
|
|||
/// <param name="callback">回调。</param>
|
|||
/// <exception cref="ArgumentNullException" />
|
|||
/// <exception cref="ArgumentException" />
|
|||
/// <exception cref="Exception" />
|
|||
public static HotKey Bind(Form form, Keys key, ModifierKey modifier, Action<HotKey> callback) |
|||
{ |
|||
if (form == null) throw new ArgumentNullException(nameof(form)); |
|||
if (callback == null) throw new ArgumentNullException(nameof(callback)); |
|||
|
|||
var hotkey = new HotKey(form.Handle, key, modifier, callback); |
|||
hotkey.Bind(); |
|||
return hotkey; |
|||
} |
|||
|
|||
/// <summary>绑定热键。</summary>
|
|||
/// <remarks>WM_HOTKEY 消息将发布到调用线程的消息队列,并且必须在消息循环中进行处理。</remarks>
|
|||
/// <param name="key">热键的虚拟键代码。</param>
|
|||
/// <param name="modifier">修饰键。</param>
|
|||
/// <param name="callback">回调。</param>
|
|||
/// <exception cref="ArgumentNullException" />
|
|||
/// <exception cref="ArgumentException" />
|
|||
/// <exception cref="Exception" />
|
|||
public static HotKey Bind(Keys key, ModifierKey modifier, Action<HotKey> callback) |
|||
{ |
|||
if (callback == null) throw new ArgumentNullException(nameof(callback)); |
|||
|
|||
var hotkey = new HotKey(IntPtr.Zero, key, modifier, callback); |
|||
hotkey.Bind(); |
|||
return hotkey; |
|||
} |
|||
|
|||
} |
|||
|
|||
} |
@ -0,0 +1,26 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Drawing; |
|||
|
|||
namespace Apewer.WinForm |
|||
{ |
|||
|
|||
/// <summary>菜单项。</summary>
|
|||
public class MenuItem |
|||
{ |
|||
|
|||
/// <summary>显示的文本,不设置此属性时菜单将显示未为分隔线。</summary>
|
|||
public string Text { get; set; } |
|||
|
|||
/// <summary>点击菜单时要执行的命令。</summary>
|
|||
public Action<MenuItem> Action { get; set; } |
|||
|
|||
/// <summary>菜单中显示的图片。</summary>
|
|||
public Image Image { get; set; } |
|||
|
|||
/// <summary>自定义数据。</summary>
|
|||
public virtual object Data { get; set; } |
|||
|
|||
} |
|||
|
|||
} |
@ -1,123 +1,25 @@ |
|||
using System; |
|||
|
|||
namespace Apewer.WinForm |
|||
namespace Apewer.WinForm |
|||
{ |
|||
|
|||
/// <summary>修饰键。</summary>
|
|||
public struct ModifierKey : IEquatable<ModifierKey> |
|||
public enum ModifierKey : int |
|||
{ |
|||
|
|||
byte _value; |
|||
bool _alt; |
|||
bool _ctrl; |
|||
bool _shift; |
|||
bool _win; |
|||
|
|||
/// <summary>包含 ALT 键。</summary>
|
|||
public bool WithALT { get => _alt; } |
|||
|
|||
/// <summary>包含 CTRL 键。</summary>
|
|||
public bool WithCTRL { get => _ctrl; } |
|||
|
|||
/// <summary>包含 SHIFT 键。</summary>
|
|||
public bool WithSHIFT { get => _shift; } |
|||
|
|||
/// <summary>包含 WIN 键。</summary>
|
|||
public bool WithWIN { get => _win; } |
|||
|
|||
/// <summary>修饰键组合的值。</summary>
|
|||
public byte Value { get => _value; } |
|||
|
|||
/// <exception cref="OverflowException"></exception>
|
|||
private ModifierKey(byte value) |
|||
{ |
|||
_alt = value << 7 >> 7 == 1; |
|||
_ctrl = value << 6 >> 7 == 1; |
|||
_shift = value << 5 >> 7 == 1; |
|||
_win = value << 4 >> 7 == 1; |
|||
_value = Convert.ToByte(value); |
|||
if (_value != value) throw new OverflowException($"参数【{value}】含有无效的键。"); |
|||
} |
|||
|
|||
private ModifierKey(bool alt, bool ctrl, bool shift, bool win) |
|||
{ |
|||
_alt = alt; |
|||
_ctrl = ctrl; |
|||
_shift = shift; |
|||
_win = win; |
|||
|
|||
var value = 0; |
|||
if (alt) value += 0x01; |
|||
if (ctrl) value += 0x02; |
|||
if (shift) value += 0x04; |
|||
if (win) value += 0x05; |
|||
_value = Convert.ToByte(value); |
|||
} |
|||
|
|||
/// <summary></summary>
|
|||
public override int GetHashCode() => _value; |
|||
|
|||
/// <summary></summary>
|
|||
public override bool Equals(object obj) |
|||
{ |
|||
if (obj is ModifierKey b) return _value == b._value; |
|||
return false; |
|||
} |
|||
|
|||
/// <summary></summary>
|
|||
public bool Equals(ModifierKey another) => _value == another._value; |
|||
|
|||
/// <summary></summary>
|
|||
public static implicit operator byte(ModifierKey instance) => instance._value; |
|||
|
|||
/// <summary></summary>
|
|||
/// <exception cref="OverflowException"></exception>
|
|||
public static implicit operator ModifierKey(byte value) => new ModifierKey(value); |
|||
|
|||
/// <summary></summary>
|
|||
/// <exception cref="ArgumentException" />
|
|||
public static ModifierKey operator +(ModifierKey a, ModifierKey b) |
|||
{ |
|||
if (a._alt && b._alt) throw new ArgumentException("相加的两个组合键共同包含了 ALT 键。"); |
|||
if (a._ctrl && b._ctrl) throw new ArgumentException("相加的两个组合键共同包含了 CTRL 键。"); |
|||
if (a._shift && b._shift) throw new ArgumentException("相加的两个组合键共同包含了 SHIFT 键。"); |
|||
if (a._win && b._win) throw new ArgumentException("相加的两个组合键共同包含了 WIN 键。"); |
|||
|
|||
var alt = a._alt || b._alt; |
|||
var ctrl = a._ctrl || b._ctrl; |
|||
var shift = a._shift || b._shift; |
|||
var win = a._win || b._win; |
|||
return new ModifierKey(alt, ctrl, shift, win); |
|||
} |
|||
|
|||
/// <summary></summary>
|
|||
public static ModifierKey operator -(ModifierKey a, ModifierKey b) |
|||
{ |
|||
var alt = a._alt; |
|||
var ctrl = a._ctrl; |
|||
var shift = a._shift; |
|||
var win = a._win; |
|||
|
|||
if (b._alt) alt = false; |
|||
if (b._ctrl) ctrl = false; |
|||
if (b._shift) shift = false; |
|||
if (b._win) win = false; |
|||
|
|||
return new ModifierKey(alt, ctrl, shift, win); |
|||
} |
|||
/// <summary>表示未按下任何修饰键。</summary>
|
|||
None = 0, |
|||
|
|||
/// <summary>表示 ALT 修饰键。</summary>
|
|||
public static ModifierKey ALT { get; } = new ModifierKey(0x01); |
|||
/// <summary>表示按下了 ALT 键。</summary>
|
|||
ALT = 0x0001, |
|||
|
|||
/// <summary>表示 CTRL 修饰键。</summary>
|
|||
public static ModifierKey CTRL { get; } = new ModifierKey(0x02); |
|||
/// <summary>表示按下了 CTRL 键。</summary>
|
|||
CTRL = 0x0002, |
|||
|
|||
/// <summary>表示 SHIFT 修饰键。</summary>
|
|||
public static ModifierKey SHIFT { get; } = new ModifierKey(0x04); |
|||
/// <summary>表示按下了 SHIFT 键。</summary>
|
|||
SHIFT = 0x0004, |
|||
|
|||
/// <summary>表示 WIN 修饰键。</summary>
|
|||
public static ModifierKey WIN { get; } = new ModifierKey(0x08); |
|||
/// <summary>表示按下了 Windows 键。</summary>
|
|||
WIN = 0x0008 |
|||
|
|||
} |
|||
|
|||
} |
|||
} |
@ -0,0 +1,360 @@ |
|||
using System; |
|||
using System.ComponentModel; |
|||
using System.Diagnostics; |
|||
using System.Drawing; |
|||
using System.IO; |
|||
using System.Security; |
|||
using System.Security.Permissions; |
|||
using System.ServiceProcess; |
|||
using System.Threading; |
|||
using System.Windows.Forms; |
|||
|
|||
namespace Apewer.WinForm |
|||
{ |
|||
|
|||
/// <summary>通用托盘。</summary>
|
|||
public sealed class Tray : IDisposable |
|||
{ |
|||
|
|||
#region icon
|
|||
|
|||
NotifyIcon _origin = null; |
|||
|
|||
/// <summary>通知图标。</summary>
|
|||
NotifyIcon Icon { get => _origin; } |
|||
|
|||
/// <summary>鼠标点击通知图标时执行的程序。</summary>
|
|||
public Action OnClick { get; set; } |
|||
|
|||
/// <summary>鼠标点击通知图标时执行的程序。</summary>
|
|||
public Action OnDoubleClick { get; set; } |
|||
|
|||
/// <summary>设置图标。</summary>
|
|||
public void SetIcon(Icon icon) |
|||
{ |
|||
if (_disposed) throw new ObjectDisposedException(ToString()); |
|||
|
|||
ClearIcon(); |
|||
SyncInvoke(() => |
|||
{ |
|||
if (icon == null) |
|||
{ |
|||
_origin.Visible = false; |
|||
} |
|||
else |
|||
{ |
|||
_origin.Icon = icon; |
|||
_origin.Visible = true; |
|||
} |
|||
}); |
|||
} |
|||
|
|||
/// <summary>设置标题。</summary>
|
|||
public void SetTitle(string title) |
|||
{ |
|||
if (_disposed) throw new ObjectDisposedException(ToString()); |
|||
if (_origin != null) |
|||
{ |
|||
SyncInvoke(() => |
|||
{ |
|||
_origin.Text = title; |
|||
}); |
|||
} |
|||
} |
|||
|
|||
void ClearIcon() |
|||
{ |
|||
SyncInvoke(() => |
|||
{ |
|||
if (_origin != null) |
|||
{ |
|||
var old = _origin.Icon; |
|||
_origin.Icon = null; |
|||
RuntimeUtility.Dispose(old); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
/// <summary>获取 EXE 文件的图标。</summary>
|
|||
public static Icon GetExeIcon() => System.Drawing.Icon.ExtractAssociatedIcon(RuntimeUtility.ExecutablePath); |
|||
|
|||
/// <summary>获取 EXE 文件的图标。</summary>
|
|||
/// <exception cref="ArgumentNullException" />
|
|||
/// <exception cref="FileNotFoundException" />
|
|||
public static Icon GetExeIcon(string filePath) |
|||
{ |
|||
if (filePath.IsEmpty()) throw new ArgumentNullException(nameof(filePath)); |
|||
if (!File.Exists(filePath)) throw new FileNotFoundException($"文件【{filePath}】不存在。"); |
|||
var icon = System.Drawing.Icon.ExtractAssociatedIcon(filePath); |
|||
return icon; |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
#region tip
|
|||
|
|||
/// <summary>鼠标点击弹出提示时执行的程序。</summary>
|
|||
public Action OnTipClick { get; set; } |
|||
|
|||
/// <summary>显示悬浮通知。</summary>
|
|||
/// <exception cref="ObjectDisposedException"></exception>
|
|||
public void ShowTip(string text, string title = null, ToolTipIcon icon = ToolTipIcon.None) |
|||
{ |
|||
if (_disposed) throw new ObjectDisposedException(ToString()); |
|||
if (text.IsEmpty()) return; |
|||
_origin?.ShowBalloonTip(5000, title, text, icon); |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
#region menu
|
|||
|
|||
/// <summary>清除右键菜单。</summary>
|
|||
public void ClearMenu() |
|||
{ |
|||
if (_disposed) throw new ObjectDisposedException(ToString()); |
|||
|
|||
SyncInvoke(() => |
|||
{ |
|||
#if NETFRAMEWORK
|
|||
if (_origin.ContextMenu != null) |
|||
{ |
|||
var old = _origin.ContextMenu; |
|||
_origin.ContextMenu = null; |
|||
RuntimeUtility.Dispose(old); |
|||
} |
|||
#endif
|
|||
|
|||
if (_origin.ContextMenuStrip != null) |
|||
{ |
|||
var old = _origin.ContextMenuStrip; |
|||
_origin.ContextMenuStrip = null; |
|||
RuntimeUtility.Dispose(old); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
/// <summary>设置右键菜单。</summary>
|
|||
public void SetMenu(MenuItem[] menu) |
|||
{ |
|||
ClearMenu(); |
|||
if (menu == null) return; |
|||
#if NETFRAMEWORK
|
|||
SetMenu(menu.ContextMenu()); |
|||
#else
|
|||
SetMenu(menu.ContextMenuStrip()); |
|||
#endif
|
|||
} |
|||
|
|||
#if NETFRAMEWORK
|
|||
|
|||
/// <summary>设置右键菜单。</summary>
|
|||
public void SetMenu(ContextMenu menu) |
|||
{ |
|||
if (_disposed) throw new ObjectDisposedException(ToString()); |
|||
|
|||
ClearMenu(); |
|||
SyncInvoke(() => |
|||
{ |
|||
if (menu != null) _origin.ContextMenu = menu; |
|||
}); |
|||
} |
|||
|
|||
#endif
|
|||
|
|||
/// <summary>设置右键菜单。</summary>
|
|||
public void SetMenu(ContextMenuStrip menu) |
|||
{ |
|||
if (_disposed) throw new ObjectDisposedException(ToString()); |
|||
|
|||
ClearMenu(); |
|||
SyncInvoke(() => |
|||
{ |
|||
if (menu != null) _origin.ContextMenuStrip = menu; |
|||
}); |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
#region instance
|
|||
|
|||
bool _disposed = false; |
|||
SynchronizationContext _sync = null; |
|||
Form _form = null; |
|||
Action<Tray> _action = null; |
|||
|
|||
/// <summary></summary>
|
|||
/// <exception cref="InvalidOperationException"></exception>
|
|||
public Tray(Action<Tray> action) |
|||
{ |
|||
_sync = SynchronizationContext.Current; |
|||
if (_sync == null) throw new InvalidOperationException($"当前线程无法启动 {nameof(Tray)} 实例。"); |
|||
|
|||
_origin = new NotifyIcon(); |
|||
_origin.Click += (s, e) => OnClick?.Invoke(); |
|||
_origin.DoubleClick += (s, e) => OnDoubleClick?.Invoke(); |
|||
_origin.BalloonTipClicked += (s, e) => OnTipClick?.Invoke(); |
|||
|
|||
_form = new Form(); |
|||
_form.FormBorderStyle = FormBorderStyle.None; |
|||
_form.Opacity = 0; |
|||
_form.ShowInTaskbar = false; |
|||
_form.Size = new Size(0, 0); |
|||
_form.StartPosition = FormStartPosition.CenterScreen; |
|||
_form.TopMost = true; |
|||
|
|||
_action = action; |
|||
_form.Load += Load; |
|||
_form.Show(); |
|||
_form.Visible = false; |
|||
} |
|||
|
|||
|
|||
/// <summary></summary>
|
|||
public void Dispose() |
|||
{ |
|||
if (_disposed) throw new ObjectDisposedException(ToString()); |
|||
|
|||
ClearMenu(); |
|||
ClearIcon(); |
|||
|
|||
_origin.Dispose(); |
|||
_origin = null; |
|||
|
|||
_form.Dispose(); |
|||
_form = null; |
|||
|
|||
_disposed = true; |
|||
} |
|||
|
|||
void Load(object sender, EventArgs e) |
|||
{ |
|||
RuntimeUtility.InBackground(() => |
|||
{ |
|||
_action?.Invoke(this); |
|||
}); |
|||
} |
|||
|
|||
/// <summary></summary>
|
|||
public void SyncInvoke(Action action) |
|||
{ |
|||
if (action == null) return; |
|||
|
|||
if (_sync == null) action.Invoke(); |
|||
else _sync.Send(state => action.Invoke(), null); |
|||
} |
|||
|
|||
/// <summary></summary>
|
|||
public void FormInvoke(Action action) |
|||
{ |
|||
if (action == null) return; |
|||
if (_form == null) return; |
|||
if (_form.InvokeRequired) |
|||
{ |
|||
_form.Invoke(action); |
|||
return; |
|||
} |
|||
|
|||
action.Invoke(); |
|||
} |
|||
|
|||
/// <summary>显示对话框,并获取结果。</summary>
|
|||
/// <exception cref="ArgumentNullException" />
|
|||
public DialogResult ShowDialog(CommonDialog dialog) |
|||
{ |
|||
if (_disposed) throw new ObjectDisposedException(ToString()); |
|||
if (dialog == null) throw new ArgumentNullException(nameof(dialog)); |
|||
|
|||
if (_form.InvokeRequired) |
|||
{ |
|||
var result = default(DialogResult); |
|||
_form.Invoke(() => |
|||
{ |
|||
_form.Visible = true; |
|||
result = dialog.ShowDialog(); |
|||
_form.Visible = false; |
|||
}); |
|||
return result; |
|||
} |
|||
|
|||
return dialog.ShowDialog(); |
|||
} |
|||
|
|||
/// <summary></summary>
|
|||
public void Exit() |
|||
{ |
|||
if (!_disposed) Dispose(); |
|||
Application.Exit(); |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
#region run
|
|||
|
|||
/// <summary>已启动的服务名称。</summary>
|
|||
public static string ServiceName { get; private set; } |
|||
|
|||
/// <summary>在当前线程运行托盘程序,并启动消息循环。</summary>
|
|||
/// <param name="action">启动托盘后执行的程序。</param>
|
|||
/// <exception cref="ArgumentNullException" />
|
|||
[STAThread] |
|||
public static void Run(Action<Tray> action) |
|||
{ |
|||
if (action == null) throw new ArgumentNullException(nameof(action)); |
|||
|
|||
Control.CheckForIllegalCrossThreadCalls = false; |
|||
Application.EnableVisualStyles(); |
|||
Application.SetCompatibleTextRenderingDefault(false); |
|||
|
|||
if (SynchronizationContext.Current == null) |
|||
{ |
|||
var sc = AsyncOperationManager.SynchronizationContext; |
|||
if (sc == null || sc.GetType() == typeof(SynchronizationContext)) |
|||
{ |
|||
new PermissionSet(PermissionState.Unrestricted).Assert(); |
|||
try { AsyncOperationManager.SynchronizationContext = new WindowsFormsSynchronizationContext(); } |
|||
finally { CodeAccessPermission.RevertAssert(); } |
|||
} |
|||
} |
|||
|
|||
var instance = new Tray(action); |
|||
// action.Invoke(instance);
|
|||
Application.Run(); |
|||
} |
|||
|
|||
/// <summary>启动服务。</summary>
|
|||
/// <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); |
|||
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
|
|||
|
|||
} |
|||
|
|||
} |
Loading…
Reference in new issue