用C#创建简单的服务程序(Service)及其编译自动化

本文描述的内容主要涵盖两个方面:
1. 如何用C#创建一个简单的服务程序
2. 如何让服务在编译的时候自动安装、卸载、启动、停止
3. 可能会遇到的问题

本文操作环境:Win7 + Visual Studio 2008
请注意:Visual Studio 2008需要以管理员权限启动,因为我们的命令都需要这个权限。

1. 对于前一个问题,可以参考以下几篇文章:
MSDN:Visual Studio 2010 如何:创建 Windows 服务
用Visual C#创建Windows服务程序
用C#创建Windows服务(Windows Services)
如何用.NET创建Windows服务
这几篇文章主要描述了服务程序的一些概念和step by step的操作。对于不了解Service的同学来说应该,应该会有一个比较初步的了解。 但是,用Visual C#创建Windows服务程序 这篇文章的例子比较久远,我下载编译了该例子,并安装、启动了编译出来的服务,不知为 何会同时有两个服务在任务管理器中出现。所以,这篇文章的代码没有进行仔细的研究,而是自己建立了一个工程,一步一步的做下来。

2. 如何让服务在编译的时候自动安装、卸载、启动、停止?
首先,需要了解一下如何手动的进行服务的安装和卸载,它用到VS自带的一个工具:InstallUtil.exe。服务的启动和停止用到了net命令或者使用sc命令。
操作:用管理员权限打开Visual Studio 2008 Command Prompt(VS自带的命令行)->进入到编译生成可执行文件的目录
安装:输入命令 InstallUtil 服务名称
卸载:输入命令 InstallUtil /u 服务名称
启动:输入命令 net start 服务名称 或者 sc start 服务名称
停止:输入命令 net stop 服务名称 或者 sc stop 服务名称
我们的服务程序编译OK后,每次都需要重复如上的操作,相当繁琐,也大大降低了我们开发的效率,如果我们能让这些操作完全自动化,在编译完成后就能直接使用,这是一件相当美妙的事情。
最开始,我参照了.Net下的Windows服务程序开发指南.这篇文章,文章提供了一个思路,将如下几行命令放在:工程属性->Build Event->Post-build event command line里面,这样编译的时候就能够进行一些上述操作。

   1:  net stop ServiceName
   2:  "%SystemRoot%/Microsoft.NET/Framework/v2.0.50727/InstallUtil.exe" /u $(ProjectDir)bin/$(ConfigurationName)/$(TargetFileName)
   3:  "%SystemRoot%/Microsoft.NET/Framework/v2.0.50727/InstallUtil.exe"  $(ProjectDir)bin/$(ConfigurationName)/$(TargetFileName)
   4:  net start ServiceName

但是,该方法还是不够完美,原因有二:
一是在第一次编译后该服务后,由于本地机器从来没有安装过ServiceName这个服务,此时就会报错, 服务名无效。请键入 NET HELPMSG 2185 以获得更多的帮助。因此,第一次使用时,要手动安装。
二是在第二次将要编译的时候,如果该服务没有停掉(即进程并没有结束),那么此时也会报错,因为没有权限修改这个可执行文件,它正在被另外一个进程占用。
于是,我稍微改进了一下,将1,2句放到Per-build event command line里,3,4句放到Post-build event command line,虽然或多或少的有一些改进,但不是不如意。

最根本的原因其实在于自己写的CMD实在是太弱了,而且命令行本身对于返回值的支持貌似不怎么好。因此,最好是有一种能够支持复杂逻辑的脚本,并能够很好的处理返回值。 显然,Windows Powershell是最好的选择。 但是,要使用Win7自带的PowerShell来自动化处理我们的编译,还需要解决两个问题。
1. PowerShell本身是否支持InstallUtil, sc等等命令,如果不支持,是否有对应的替代方法?
2. 能否将PowerShell脚本嵌入到Per-build or Post-build event command line中去,如果不能,是否有对应的替代方法?

第一个问题,我的答案是不能,至少我没调查出来,但是我们可以设置相关的环境变量到PowerShell里面去,让它能够支持这些命令。参考文章:Visual Studio 2008 PowerShell
第二个问题,很遗憾,我还是没有调查出来,但是我们可以在Per-build or Post-build event command line调用PowerShell命令执行其脚本文件。参考PowerShell的Help文档。

OK,先看脚本文件的代码:

   1:  function Get-Batchfile ($file) {
   2:      $cmd = "`"$file`" & set"
   3:      cmd /c $cmd | Foreach-Object {
   4:          $p, $v = $_.split('=')
   5:          Set-Item -path env:$p -value $v
   6:      }
   7:  }
   8:   
   9:  function VsVars32()
  10:  {
  11:      $vs90comntools = (Get-ChildItem env:VS90COMNTOOLS).Value
  12:      $batchFile = [System.IO.Path]::Combine($vs90comntools, "vsvars32.bat")
  13:      Get-Batchfile $BatchFile
  14:      
  15:      [System.Console]::Title = "Visual Studio 2008 Windows PowerShell"
  16:  }
  17:   
  18:  function BuildEvent([Boolean]$isPreBuild, $ServiceName, $ServiceFullPath)
  19:  {
  20:      #如果是编译前($isPreBuild==True),
  21:      #    则先获取所有服务,再遍历所有服务,如果找到该服务,且该服务正处于运行状态,则先Stop该服务,然后再UnInstall该服务
  22:      #如果是编译后($isPreBuild==False),
  23:      #    则直接先安装服务,再Start该服务
  24:      if ($isPreBuild)
  25:      {
  26:          $Rts = Get-Service 
  27:          foreach ($Item in $Rts)
  28:          {
  29:              if ($Item.Name -eq $ServiceName -and $Item.Status -eq "Running")
  30:              {
  31:                  Stop-Service $ServiceName
  32:                  echo "Stop $ServiceName Success!"
  33:                  InstallUtil /u $ServiceFullPath
  34:                  echo "UnInstall $ServiceName Success!"
  35:              }
  36:          }
  37:      }
  38:      else
  39:      {
  40:          InstallUtil $ServiceFullPath
  41:          echo "Install $ServiceName Success!"
  42:          Start-Service $ServiceName
  43:          echo "Start $ServiceName Success!"
  44:      }
  45:  }
  46:   
  47:  ############################################################################
  48:  ############################################################################
  49:   
  50:  #程序从这里入口
  51:  "Visual Studio 2008 Windows PowerShell"
  52:  ""
  53:  #将VS的相关环境变量设置到Windows PowerShell中去
  54:  #Set Visual Studio 2008 Command Prompt's environment variable into Windows PowerShell
  55:  VsVars32
  56:   
  57:  #解析从命令行传过来的参数: 
  58:  #    参数1:是否是Pre-Build,需要传入true/false的字符串
  59:  #    参数2:服务名称
  60:  #    参数3:服务对应的可执行文件全路径(绝对路径)
  61:  if ($args.Count -eq 3)
  62:  {
  63:      if ($args[0] -ne "true" -and $args[0] -ne "false")
  64:      {
  65:          echo "Argument error!!! Please check it."
  66:          exit
  67:      }
  68:     
  69:      echo "Pre-Build or Post-Build event command line[True:Pre-Build, False:Post-Build]:" $args[0]
  70:      echo "Service Name:"      $args[1]
  71:      echo "Service Full Path:" $args[2]
  72:          
  73:      [Boolean]$isPreBuild = $true
  74:      if ($args[0] -eq "false")
  75:      {
  76:          [Boolean]$isPreBuild = $false
  77:      }
  78:          
  79:      #调用处理函数
  80:      BuildEvent $isPreBuild $args[1] $args[2]
  81:  }

然后,我们需要让VS能够调用该脚本,并传入相应的参数即可:
在Per-build event command line中,添加:  powershell -File $(ProjectDir)Pre-BuildAndPost-BuildService.ps1 true $(TargetName) $(TargetPath)
在Post-build event command line中,添加: powershell -File $(ProjectDir)Pre-BuildAndPost-BuildService.ps1 false $(TargetName) $(TargetPath)

3. 可能会遇到的问题
3.1 如何调试:
    
①打log
     ②直接Attach到进程
     需要注意的是,Windows 服务管理器将所有尝试启动服务的时间限制在 30 秒内,如果想要调试OnStart方法,我的做法比较山寨,是在OnStart的时候加入一个Thread.Sleep(20000),然后迅速启动服务,迅速Attach到服务进程。MS推荐使用临时服务加载真正要处理的服务,请参考:如何:调试 Windows 服务应用程序
3.2 在计算机->管理->服务中,我能够看到对应的服务,但为什么状态为disabled(禁用),无法启动,此时,如果启动该服务,则会出现错误对话框:
     The specified service has been marked for deletion(指定服务已标记为删除)。那么,如何删掉该服务?其实,此时你什么都不用做,关掉这个服务的窗口,关掉其控制命令行,再重新打开服务管理窗口,发现一切正常。实在不行,只有用杀手锏----重启电脑。
3.3 如何删除一个服务:①sc delete ServiceName ②在注册表HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services下找到对应的服务,删除即可。

貌似用Windows Live Writter发布的日志还算不错。但是没有摘要啊。。。郁闷。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值