Windows服务小记

来源:互联网 发布:网络主播怎么挣钱 编辑:程序博客网 时间:2024/04/29 04:27
2009-10-19 23:48
服务程序有三个部分:
主函数,service-main函数,处理程序
SCM(Service Control Manager)是用来与服务进行通信的。启动服务就是启动该服务的进程,调用主函数,进程为它的每一个服务注册一个service-main函数,主函数是服务程序的入口,在这里service-main函数的入口必须用SCM注册。

服务的主函数可以注册多个service-main函数,一个服务程序可以有多个服务。Service-main包含服务的功能,它的重要任务是用scm注册处理程序。处理程序必须对来自scm的停止,暂停等事件做出响应。
服务控制程序可以把启动暂停等请求发给SCM。

创建windows服务,新建项目,然后选择创建windows 服务项目会创建一个服务的主程序,还有一个继承自ServiceBase的服务类,如果想在这个服务程序里创建更多的服务,则再添加windows服务。

在服务设计的属性中,可以设置ServiceName,它是要写入windows注册表中的服务名,用它可以控制服务。Autolog是控制是否则服务事件自动写入事件查看器的应用程序日志中。其他的can属性都是控制服务是否对相应的事件做出响应。对这些属性做的修改会在服务类的InitializeComponent()方法中产生相应的代码。

服务的主函数:
/// <summary>
/// 应用程序的主入口点。
/// </summary>
//所有的服务都可以在注册表的hkey_local_machine/system/currentcontrolset/services找到
static void Main()
{
ServiceBase[] ServicesToRun;

// 同一进程中可以运行多个用户服务。若要将
// 另一个服务添加到此进程中,请更改下行以
// 创建另一个服务对象。例如,
//
//   ServicesToRun = new ServiceBase[] {new Service1(), new MySecondUserService()};
//

//初始化要在ServiceBase.Run之前完成,初始化时间不能超过30秒,否则服务启动失败,如果启动        //时间过长,考虑使用线程
ServicesToRun = new ServiceBase[] { new WinServiceTest() };

//进入run方法之后主线程停滞
ServiceBase.Run(ServicesToRun);
}

服务启动:
protected override void OnStart(string[] args)
{
// TODO: 在此处添加代码以启动服务。
//此方法需要立即返回,所以这里一般会应用线程
//一般将真正的处理程序写到其他的类和项目中,对其功能测试没有问题后,在这里直接调用启动服务
NetInfoRecorder.Start();

}

处理程序:
protected override void OnStop()
{
// TODO: 在此处添加代码以执行停止服务所需的关闭操作。
NetInfoRecorder.Stop();
}

//服务的CanPauseAndContinue属性要为真,以下两个方法才能生效
protected override void OnContinue()
{
base.OnContinue();
}

protected override void OnPause()
{
base.OnPause();
}

//windows关闭的时候执行的方法,CanShutdown属性要设为真
protected override void OnShutdown()
{
base.OnShutdown();
}

//为服务控制程序发过来的特定命令进行操作,command的取值范围是128到256,小于128的值是为系统保留的
protected override void OnCustomCommand(int command)
{
if (command == 128)
Trace.WriteLine("OnCustomCommand:a custom command test");
}

//CanHandlePowerEvent为真才能生效,处理电源的事件(主要针对笔记本的电池),电池状态根据参数的枚举值来判断
protected override bool OnPowerEvent(PowerBroadcastStatus powerStatus)
{
Trace.WriteLine("powerstatus:" + powerStatus.ToString());
return base.OnPowerEvent(powerStatus);
}

服务安装:
在服务的设计视图上,从右键菜单中选择add installer可以给服务添加安装程序,这时会创建一个新的projectinstaller类,一个ServiceProcessInstaller和ServiceInstaller实例。
Installer是projectinstaller类的基类,它是基于事务的安装。
[RunInstaller(true)]属性为真,在安装的时候installutil.exe就会调用它修饰的类。

ServiceProcessInstaller是用来配置进程的,ServiceInstaller是用来配置服务的,所以如果有多个服务,则需要有多个ServiceInstaller的实例。ServiceProcessInstaller安装一个可用来执行ServiceBase类的可执行文件。
ServiceProcessInstaller的UserName和Password是设置服务的进程运行在哪个账户下,Account指定账户的类型,其值可以为LocalService(可以给任意远程服务器提供证书),LocalSystem(具有高级权限的特权,但没有网络上的权限),NetworkService(将计算机证书传给远程服务器,可以以非授权用户的身份登录本地系统,这种账户只能从网络上获取资源的服务),User(可以指定用来运行服务的账户)

ServiceInstaller的ServiceName必须和ServiceBase的一样,DisplayName是在服务管理窗口列表中显示的名字,StartType就是启动类型,ServicesDependentOn是这个服务需要依赖的服务的一个列表。

在测试阶段,启动类型最好使用Manual的手动启,不要弄成自动启,有利于测试。

ServiceInstallerDialog类是用来在安装时输入用户名和密码的。但是需要如下:

//
// serviceProcessInstaller1
// 如果ServiceProcessInstaller1组件的Account设置的是user,这两个属性又设为null,则在安装的过程中将弹出set service login对话框
this.serviceProcessInstaller1.Password = null;
this.serviceProcessInstaller1.Username = null;

服务的安装使用installutil.exe实用工具。
安装:installutil service.exe
卸载:installutil /u service.exe
安装失败的话可以查看installutil.installlog和servicename.installlog两个日志文件查找失败原因。

windows上还有net.exe和sc.exe(platfrom sdk中的工具,是visual studio的一部分)两控制服务的命令行工具。
net start servicename
net stop servicename
net pause servicename
net continue servicename

在visual studio 的Server Exploer中也可以控制服务。如果需要控制具体的服务,可以把服务项从server explorer拉到设计面板上,这样会直接生成ServiceController类的实例用来对服务进行控制。

//得到本机所有的windows服务
ServiceController[] services = ServiceController.GetServices();

//绑定还可以这样绑,当从listBox1中取出item的时候,进行类型转换就能得到ServiceController
listBox1.DisplayMember = "DisplayName";
listBox1.DataSource = services;

ServiceController的其他属性都很容易明了,只是ServiceType比较麻烦,是个枚举。
可以用这个方法将枚举值转为字符串,其实这个也很麻烦了,直接type.ToString()就ok了。
protected string GetServiceTypeName(ServiceType type)
{
string result = "";

if ((type & ServiceType.InteractiveProcess) != 0)
{
result = "InteractiveProcess";
type -= ServiceType.InteractiveProcess;//枚举可以这样用
}

switch (type)
{
case ServiceType.Adapter:
result += "Adapter";
break;
case ServiceType.FileSystemDriver:
case ServiceType.KernelDriver:
case ServiceType.RecognizerDriver:
result += "Driver";
break;
case ServiceType.Win32OwnProcess:
result += "win32 service process";
break;
case ServiceType.Win32ShareProcess:
result += "win32 shared process";
break;
default:
result += "unknown type" + type.ToString();
break;
}

return result;
}

要启动停止暂停或向服务发送特定命令,调用ServiceController类的对应的方法即可。
如启动服务
ServiceController controller = (ServiceController)this.listBox1.SelectedItem;
controller.Start();
controller.WaitForStatus(ServiceControllerStatus.Running);//等待服务达到某个状态

执行自定义命令:
ServiceController controller = (ServiceController)this.listBox1.SelectedItem;
controller.ExecuteCommand(128);


创建服务最好的方式就是在创建它之前先创建一个服务所需功能的类,然后创建一个客户应用程序来调用它进行测试,没有问题后再创建服务。

服务中的错误被写入事件日志。

服务不能从调试器启动,但是可以和正在运行的服务联系起来。在服务的代码中设置断点,然后从visual studio的debug菜单中选择process命令来捕获正在运行的服务进程。

性能监视器可以用来监视服务,可以添加自己的性能对像。

配置交互式的服务可以弹出对话框和用户交互。只是在服务器上运行的服务就不需要这样的配置了。要配置交互式服务,在服务的属性里的登录选项页中进行配置。

把ServiceBase类中的AutoLog设为真的时候,在发生启动,停止,暂停等操作的时候,会自动写入事件日志。

在注册表的HKLM_LOCAL_MACHINE/System/CurrentControlSet/Services/EventLog目录下可以看到3个日志。
Application的日志源在HKLM_LOCAL_MACHINE/System/CurrentControlSet/Services/EventLog/Application/[applicationname]

事件源可认定义种类(category),可以用来过滤事件。

要往日志里面写入消息,使用System.Diagnostics.EventLog实例的WriteEntry()方法。
要对应用程序添加事件日志,如果应用程序是基于组件的,可以直接从工具箱中把eventlog的直接拉到设计面板上,然后设置它的Log和Source属性。然后就可以程序里直接调用它的实例的相应方法进行写入日志了。如果是非组件的,就用EventLog本身的方法来创建并写入事件日志。

从EventLog的Add Installer中菜单中可以添加安装程序,添加ProjectInstaller类,来在注册表中对配置事件源。

使用installutil实用工具,可以注册应该程序,并调用ProjectInstaller类来注册事件源。

如果设置了EvenLog的实例的Source属性,则在第一次写事件日志的时候会自动注册事件源。添加了安装程序的程序,可以使用installutil /u来注销事件日志配置。在应用程序里可以调用EventLog.DeleteEventSource()方法来删除事件源,否则其项目会一直存在于注册表中。

所有的跟踪信息都可以写到事件日志里去,但在正常运作的系统中,事件日志会因跟踪信息而发生溢出从而丢失重要的日志信息,所以最好不要用事件日志来记录日志。但可以用来在测试的时候把信息写入事件日志。

EventLogTraceListener el = new EventLogTraceListener(this.eventLog1);
Trace.Listeners.Add(el);
用如上代码即可以添加监听器,然后在trace中写信息的时候,就会写入事件日志。


要创建性能记数器,可以在Visual studio的Server Exploer的性能记数器下,在弹出菜单中选择“创建新类别”然后在弹出窗口中设置类别名,计数器名,类型等信息。或者使用这样的代码在程序中动态的来创建性能计数器。

CounterCreationDataCollection dc = new CounterCreationDataCollection();
CounterCreationData c = new CounterCreationData("data quantity","a counter test",PerformanceCounterType.NumberOfItems32);
dc.Add(c);
PerformanceCounterCategory.Delete("My Test");
PerformanceCounterCategory.Create("My New Test", "a test", PerformanceCounterCategoryType.MultiInstance, dc);

在基于component的应用程序中,可以从server explorer中直接将创建的性能计数器拉到设计面板上使用。

可以在程序里生成PerformanceCounter的实例来调用其方法进行性能计数,如:
PerformanceCounterCategory category =  PerformanceCounterCategory.Create("My New Test", "a test", PerformanceCounterCategoryType.MultiInstance, dc);

PerformanceCounter counter = new PerformanceCounter(category.CategoryName, c.CounterName);
counter.Increment();
counter.IncrementBy(5);

可以在管理工具中的性能里查看这些性能计数器以了解应用程序运行的情况。

Windows服务也可以响应电源事件,例如:
//CanHandlePowerEvent为真才能生效,处理电源的事件(主要针对笔记本的电池),电池状态根据参数的枚举值来判断
protected override bool OnPowerEvent(PowerBroadcastStatus powerStatus)
{
Trace.WriteLine("powerstatus:" + powerStatus.ToString());
return base.OnPowerEvent(powerStatus);
}

PowerBroadcastStatus各值的含义参考msdn,然后在程序里根据当前电源的情况做出具体的反应。关于windows服务,大概就这么多了。