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 { /// 通用托盘。 public sealed class Tray : IDisposable { #region icon NotifyIcon _origin = null; /// 通知图标。 NotifyIcon Icon { get => _origin; } /// 鼠标点击通知图标时执行的程序。 public Action OnClick { get; set; } /// 鼠标点击通知图标时执行的程序。 public Action OnDoubleClick { get; set; } /// 设置图标。 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; } }); } /// 设置标题。 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); } }); } /// 获取 EXE 文件的图标。 public static Icon GetExeIcon() => System.Drawing.Icon.ExtractAssociatedIcon(RuntimeUtility.ExecutablePath); /// 获取 EXE 文件的图标。 /// /// 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 /// 鼠标点击弹出提示时执行的程序。 public Action OnTipClick { get; set; } /// 显示悬浮通知。 /// 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 /// 清除右键菜单。 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); } }); } /// 设置右键菜单。 public void SetMenu(MenuItem[] menu) { ClearMenu(); if (menu == null) return; #if NETFRAMEWORK SetMenu(menu.ContextMenu()); #else SetMenu(menu.ContextMenuStrip()); #endif } #if NETFRAMEWORK /// 设置右键菜单。 public void SetMenu(ContextMenu menu) { if (_disposed) throw new ObjectDisposedException(ToString()); ClearMenu(); SyncInvoke(() => { if (menu != null) _origin.ContextMenu = menu; }); } #endif /// 设置右键菜单。 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 _action = null; /// /// public Tray(Action 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; } /// 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); }); } /// public void SyncInvoke(Action action) { if (action == null) return; if (_sync == null) action.Invoke(); else _sync.Send(state => action.Invoke(), null); } /// public void FormInvoke(Action action) { if (action == null) return; if (_form == null) return; if (_form.InvokeRequired) { _form.Invoke(action); return; } action.Invoke(); } /// 显示对话框,并获取结果。 /// 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(); } /// public void Exit() { if (!_disposed) Dispose(); Application.Exit(); } #endregion #region run /// 已启动的服务名称。 public static string ServiceName { get; private set; } /// 在当前线程运行托盘程序,并启动消息循环。 /// 启动托盘后执行的程序。 /// [STAThread] public static void Run(Action 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(); } /// 启动服务。 /// 服务启动后执行的程序。 /// 停止服务时执行的程序。 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 } }