反服务,持久化,无 System.ServiceModel





5.00/5 (2投票s)
当您希望应用程序在事件(计时器、系统、文件等)触发时运行,但又不想承受服务管理器的开销

引言
我被要求为我所属的一个组织创建一个小型提醒应用程序。它会每小时提示一个引用。对于类似的情况,我的“工作”答案是创建一个 Windows 服务,使其可以自我安装,非常简单。但我想冒险尝试,因为这主要涉及 U/I,而且我不想处理服务用户帐户,如何在没有服务的情况下完成这项工作呢?本文概述了这次冒险……
背景
此项目的主要组成部分是
- Windows (本例中使用 WinForms,但 WPF 也可以使用),
OnClose
和Timers
- 用于强制单例实例和“关闭”应用程序的套接字/侦听器
- InnoSetup 脚本
Using the Code
- 创建一个新的 Windows 项目(新建项目... Windows)
- 创建一个计时器。拖放一个计时器对象,设置参数,创建一个计时器处理程序。您的项目可能没有使用计时器,没关系,如果一个事件触发窗口显示/隐藏,这就是你想要的
- 创建一个“强制关闭”机制,我创建了一个名为“
CanClose
”的复选框
- 重写
FormClosing
private void TheMessage_FormClosing(object sender, FormClosingEventArgs e) { e.Cancel = !CanClose.Checked; this.Hide(); }
- 创建一个单独的
ForceClose()
方法,该方法设置CanClose
并调用“Close
”public void ForceClose() { CanClose.Checked = true; Close(); }
- 套接字和线程。这变得有趣了。所以我想让应用程序具有单例实例行为。有很多好的方法可以做到这一点,CodeProject 上的互斥锁非常棒。但是,我还需要它能够干净地安装/卸载,这意味着它需要强制关闭自己。为此,我使用了一个专用端口上的套接字侦听器/客户端。这听起来违反直觉,但我首先创建了客户端(是鸡还是蛋?!)
public void Main(string[] args){ ... bool killStream = args.Length > 0 && args[0] == "-kill"; try { using (TcpClient clnt = new TcpClient()) { clnt.Connect("localhost", 7231); if (clnt.Connected) { clnt.GetStream().Write(new byte[] { (byte)(killStream ? 0 : 1) }, 0, 1); // 0 - kill or 1 - show clnt.Close(); return; } } } // I really hate catching an exception as logic, but there's no other way catch (SocketException) { } // nothing listening, not already running if (killStream) // if it's a kill... don't start return;
最后是侦听器...
TcpListener lstn = new TcpListener (System.Net.IPAddress.Parse("127.0.0.1"), 7231); lstn.Start(); // So, we start a long loop listening for incoming messages while (true) { using (TcpClient clnt = lstn.AcceptTcpClient()) { byte[] buff = new byte[1]; if (1 == clnt.GetStream().Read(buff, 0, 1)) { switch (buff[0]) { case 0: // Kill request msgWindow.ForceClose(); return; case 1: // Show request msgWindow.Show(); continue; } } } }
- 最后,您需要一个 InnoSetup 脚本,它将在尝试重新安装/卸载之前调用它,看起来像这样...
[Files] Source: "{Your Project}\bin\Release\AntiService.exe"; DestDir: "{app}"; Flags: ignoreversion; BeforeInstall: StopLBApp ... [Code] procedure StopLBApp(); var FileName : String; ResultCode: Integer; begin Log('Asking any existing processes to stop now'); FileName := ExpandConstant('{app}\AntiService.exe'); if FileExists(FileName) then begin if not ShellExec('', FileName, '-kill', '', SW_SHOWNORMAL, ewNoWait, ResultCode) then MsgBox('DeinitializeSetup:' #13#13 'Execution of ''' + FileName + ''' failed. ' + SysErrorMessage(ResultCode) + '.', mbError, MB_OK); end; end;
摘要
所以你所拥有的是一个应用程序,一旦启动,就会保持运行,并且会在每次启动或发生某些事件时尝试显示一个窗口。以我的情况为例,是计时器。希望你喜欢!
历史
- 2010 年 9 月 9 日:初始帖子