ServiceBase 类
为将作为服务应用程序的一部分而存在的服务提供基类。在创建新的服务类时,必须从 ServiceBase 派生。
**命名空间:**System.ServiceProcess
**程序集:**System.ServiceProcess(在 system.serviceprocess.dll 中)
语法
声明
Public Class ServiceBase
Inherits Component
用法
Dim instance As ServiceBase
public class ServiceBase : Component
public ref class ServiceBase : public Component
public class ServiceBase extends Component
public class ServiceBase extends Component
备注
当在服务应用程序中定义服务类时从 ServiceBase 派生。任何有用的服务均将重写 OnStart 和 OnStop 方法。对于其他功能,可以用特定行为重写 OnPause 和 OnContinue 来响应服务状态的更改。
服务是长时间运行的可执行文件,它不支持用户界面,在登录的用户帐户下可能无法运行。服务可以在没有任何用户登录计算机的情况下运行。
默认情况下,服务在“系统”帐户下运行,该帐户与“管理员”帐户不同。不能更改“系统”帐户的权限。或者,可以使用 ServiceProcessInstaller 指定运行服务时将使用的用户帐户。
一个可执行文件可以包含多项服务,但对每项服务均必须包含一个单独的 ServiceInstaller。ServiceInstaller 实例在系统中注册服务。安装程序还将每项服务与一个事件日志关联,您可以使用该日志记录服务命令。可执行文件中的 main()
函数定义哪些服务应该运行。服务的当前工作目录是系统目录,而不是可执行文件所位于的目录。
当启动某项服务时,系统将定位相应的可执行文件,并运行该服务的 OnStart 方法(它包含在可执行文件内)。但是,运行服务与运行可执行文件并不相同。可执行文件仅加载服务。服务则通过“服务控制管理器”访问(例如启动和停止)。
当您对服务首次调用“开始”时,可执行文件调用 ServiceBase 派生类的构造函数。在构造函数执行之后将立即调用 OnStart 命令处理方法。在服务首次加载之后,构造函数不会再次执行,因此有必要将构造函数执行的处理和 OnStart 执行的处理分开。可以由 OnStop 释放的任何资源都应在 OnStart 中创建。如果服务在 OnStop 释放资源后再次启动,那么,在构造函数中创建资源会妨碍这些资源的正确创建。
“服务控制管理器”(SCM) 提供与服务交互的方式。可以使用 SCM 将“开始”(Start)、“停止”(Stop)、“暂停”(Pause)、“继续”(Continue) 或自定义命令传递到服务中。SCM 使用 CanStop 和 CanPauseAndContinue 的值,决定服务是否接受“停止”、“暂停”或“继续”命令。仅当服务类中相应的属性 CanStop 或 CanPauseAndContinue 为 true 时,才会在 SCM 的上下文菜单中启用“停止”、“暂停”或“继续”。如果已启用,则相应的命令将传递到服务,并且调用 OnStop、OnPause 或 OnContinue。如果 CanStop、CanShutdown 或 CanPauseAndContinue 为 false,则即使已实现相应的命令处理方法(如 OnStop),也不会予以处理。
可以使用 ServiceController 类通过编程实现 SCM 使用用户界面实现的功能。可以自动处理控制台中可用的任务。如果 CanStop、CanShutdown 或 CanPauseAndContinue 为 true,但尚未实现相应的命令处理方法(如 OnStop),则系统引发异常并忽略该命令。
不必在 ServiceBase 中实现 OnStart、OnStop 或其他任何方法。然而,服务的行为在 OnStart 中加以描述,因此至少应重写该成员。必须在可执行文件的 main()
函数中设置服务的服务名称。在 main()
中设置的服务名称必须与服务安装程序的 ServiceName 属性完全匹配。
可以使用 InstallUtil.exe 在系统中安装服务。
示例
下面的示例从 ServiceBase 类中派生简单的服务实现。该服务处理各种服务命令,包括“停止”、“开始”、“暂停”、“继续”和自定义命令。
// Turn on logging to the event log.
#define LOGEVENTS
using System;
using System.IO;
using System.Threading;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;
using System.Text;
using Microsoft.Win32;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace ServiceSample
{
// Define custom commands for the SimpleService.
public enum SimpleServiceCustomCommands { StopWorker = 128, RestartWorker, CheckWorker };
[StructLayout(LayoutKind.Sequential)]
public struct SERVICE_STATUS
{
public int serviceType;
public int currentState;
public int controlsAccepted;
public int win32ExitCode;
public int serviceSpecificExitCode;
public int checkPoint;
public int waitHint;
}
public enum State
{
SERVICE_STOPPED = 0x00000001,
SERVICE_START_PENDING = 0x00000002,
SERVICE_STOP_PENDING = 0x00000003,
SERVICE_RUNNING = 0x00000004,
SERVICE_CONTINUE_PENDING = 0x00000005,
SERVICE_PAUSE_PENDING = 0x00000006,
SERVICE_PAUSED = 0x00000007,
}
// Define a simple service implementation.
public class SimpleService : System.ServiceProcess.ServiceBase
{
private static int userCount = 0;
private static ManualResetEvent pause = new ManualResetEvent(false);
[DllImport("ADVAPI32.DLL", EntryPoint = "SetServiceStatus")]
public static extern bool SetServiceStatus(
IntPtr hServiceStatus,
SERVICE_STATUS lpServiceStatus
);
private SERVICE_STATUS myServiceStatus;
private Thread workerThread = null;
public SimpleService()
{
CanPauseAndContinue = true;
CanHandleSessionChangeEvent = true;
ServiceName = "SimpleService";
}
static void Main()
{
#if LOGEVENTS
EventLog.WriteEntry("SimpleService.Main", DateTime.Now.ToLongTimeString() +
" - Service main method starting...");
#endif
// Load the service into memory.
System.ServiceProcess.ServiceBase.Run(new SimpleService());
#if LOGEVENTS
EventLog.WriteEntry("SimpleService.Main", DateTime.Now.ToLongTimeString() +
" - Service main method exiting...");
#endif
}
private void InitializeComponent()
{
// Initialize the operating properties for the service.
this.CanPauseAndContinue = true;
this.CanShutdown = true;
this.CanHandleSessionChangeEvent = true;
this.ServiceName = "SimpleService";
}
// Start the service.
protected override void OnStart(string[] args)
{
IntPtr handle = this.ServiceHandle;
myServiceStatus.currentState = (int)State.SERVICE_START_PENDING;
SetServiceStatus(handle, myServiceStatus);
// Start a separate thread that does the actual work.
if ((workerThread == null) ||
((workerThread.ThreadState &
(System.Threading.ThreadState.Unstarted | System.Threading.ThreadState.Stopped)) != 0))
{
#if LOGEVENTS
EventLog.WriteEntry("SimpleService.OnStart", DateTime.Now.ToLongTimeString() +
" - Starting the service worker thread.");
#endif
workerThread = new Thread(new ThreadStart(ServiceWorkerMethod));
workerThread.Start();
}
if (workerThread != null)
{
#if LOGEVENTS
EventLog.WriteEntry("SimpleService.OnStart", DateTime.Now.ToLongTimeString() +
" - Worker thread state = " +
workerThread.ThreadState.ToString());
#endif
}
myServiceStatus.currentState = (int)State.SERVICE_RUNNING;
SetServiceStatus(handle, myServiceStatus);
}
// Stop this service.
protected override void OnStop()
{
// New in .NET Framework version 2.0.
this.RequestAdditionalTime(4000);
// Signal the worker thread to exit.
if ((workerThread != null) && (workerThread.IsAlive))
{
#if LOGEVENTS
EventLog.WriteEntry("SimpleService.OnStop", DateTime.Now.ToLongTimeString() +
" - Stopping the service worker thread.");
#endif
pause.Reset();
Thread.Sleep(5000);
workerThread.Abort();
}
if (workerThread != null)
{
#if LOGEVENTS
EventLog.WriteEntry("SimpleService.OnStop", DateTime.Now.ToLongTimeString() +
" - OnStop Worker thread state = " +
workerThread.ThreadState.ToString());
#endif
}
// Indicate a successful exit.
this.ExitCode = 0;
}
// Pause the service.
protected override void OnPause()
{
// Pause the worker thread.
if ((workerThread != null) &&
(workerThread.IsAlive) &&
((workerThread.ThreadState &
(System.Threading.ThreadState.Suspended | System.Threading.ThreadState.SuspendRequested)) == 0))
{
#if LOGEVENTS
EventLog.WriteEntry("SimpleService.OnPause", DateTime.Now.ToLongTimeString() +
" - Pausing the service worker thread.");
#endif
pause.Reset();
Thread.Sleep(5000);
}
if (workerThread != null)
{
#if LOGEVENTS
EventLog.WriteEntry("SimpleService.OnPause", DateTime.Now.ToLongTimeString() +
" OnPause - Worker thread state = " +
workerThread.ThreadState.ToString());
#endif
}
}
// Continue a paused service.
protected override void OnContinue()
{
// Signal the worker thread to continue.
if ((workerThread != null) &&
((workerThread.ThreadState &
(System.Threading.ThreadState.Suspended | System.Threading.ThreadState.SuspendRequested)) != 0))
{
#if LOGEVENTS
EventLog.WriteEntry("SimpleService.OnContinue", DateTime.Now.ToLongTimeString() +
" - Resuming the service worker thread.");
#endif
pause.Set();
}
if (workerThread != null)
{
#if LOGEVENTS
EventLog.WriteEntry("SimpleService.OnContinue", DateTime.Now.ToLongTimeString() +
" OnContinue - Worker thread state = " +
workerThread.ThreadState.ToString());
#endif
}
}
// Handle a custom command.
protected override void OnCustomCommand(int command)
{
#if LOGEVENTS
EventLog.WriteEntry("SimpleService.OnCustomCommand", DateTime.Now.ToLongTimeString() +
" - Custom command received: " +
command.ToString());
#endif
// If the custom command is recognized,
// signal the worker thread appropriately.
switch (command)
{
case (int)SimpleServiceCustomCommands.StopWorker:
// Signal the worker thread to terminate.
// For this custom command, the main service
// continues to run without a worker thread.
OnStop();
break;
case (int)SimpleServiceCustomCommands.RestartWorker:
// Restart the worker thread if necessary.
OnStart(null);
break;
case (int)SimpleServiceCustomCommands.CheckWorker:
#if LOGEVENTS
// Log the current worker thread state.
EventLog.WriteEntry("SimpleService.OnCustomCommand", DateTime.Now.ToLongTimeString() +
" OnCustomCommand - Worker thread state = " +
workerThread.ThreadState.ToString());
#endif
break;
default:
#if LOGEVENTS
EventLog.WriteEntry("SimpleService.OnCustomCommand",
DateTime.Now.ToLongTimeString());
#endif
break;
}
}
// Handle a session change notice
protected override void OnSessionChange(SessionChangeDescription changeDescription)
{
#if LOGEVENTS
EventLog.WriteEntry("SimpleService.OnSessionChange", DateTime.Now.ToLongTimeString() +
" - Session change notice received: " +
changeDescription.Reason.ToString() + " Session ID: " +
changeDescription.SessionId.ToString());
#endif
switch (changeDescription.Reason)
{
case SessionChangeReason.SessionLogon:
userCount += 1;
#if LOGEVENTS
EventLog.WriteEntry("SimpleService.OnSessionChange",
DateTime.Now.ToLongTimeString() +
" SessionLogon, total users: " +
userCount.ToString());
#endif
break;
case SessionChangeReason.SessionLogoff:
userCount -= 1;
#if LOGEVENTS
EventLog.WriteEntry("SimpleService.OnSessionChange",
DateTime.Now.ToLongTimeString() +
" SessionLogoff, total users: " +
userCount.ToString());
#endif
break;
case SessionChangeReason.RemoteConnect:
userCount += 1;
#if LOGEVENTS
EventLog.WriteEntry("SimpleService.OnSessionChange",
DateTime.Now.ToLongTimeString() +
" RemoteConnect, total users: " +
userCount.ToString());
#endif
break;
case SessionChangeReason.RemoteDisconnect:
userCount -= 1;
#if LOGEVENTS
EventLog.WriteEntry("SimpleService.OnSessionChange",
DateTime.Now.ToLongTimeString() +
" RemoteDisconnect, total users: " +
userCount.ToString());
#endif
break;
case SessionChangeReason.SessionLock:
#if LOGEVENTS
EventLog.WriteEntry("SimpleService.OnSessionChange",
DateTime.Now.ToLongTimeString() +
" SessionLock");
#endif
break;
case SessionChangeReason.SessionUnlock:
#if LOGEVENTS
EventLog.WriteEntry("SimpleService.OnSessionChange",
DateTime.Now.ToLongTimeString() +
" SessionUnlock");
#endif
break;
default:
break;
}
}
// Define a simple method that runs as the worker thread for
// the service.
public void ServiceWorkerMethod()
{
#if LOGEVENTS
EventLog.WriteEntry("SimpleService.WorkerThread", DateTime.Now.ToLongTimeString() +
" - Starting the service worker thread.");
#endif
try
{
do
{
// Simulate 4 seconds of work.
Thread.Sleep(4000);
// Block if the service is paused or is shutting down.
pause.WaitOne();
#if LOGEVENTS
EventLog.WriteEntry("SimpleService.WorkerThread", DateTime.Now.ToLongTimeString() +
" - heartbeat cycle.");
#endif
}
while (true);
}
catch (ThreadAbortException)
{
// Another thread has signalled that this worker
// thread must terminate. Typically, this occurs when
// the main service thread receives a service stop
// command.
// Write a trace line indicating that the worker thread
// is exiting. Notice that this simple thread does
// not have any local objects or data to clean up.
#if LOGEVENTS
EventLog.WriteEntry("SimpleService.WorkerThread", DateTime.Now.ToLongTimeString() +
" - Thread abort signaled.");
#endif
}
#if LOGEVENTS
EventLog.WriteEntry("SimpleService.WorkerThread", DateTime.Now.ToLongTimeString() +
" - Exiting the service worker thread.");
#endif
}
}
}
// Turn on the constant for trace output.
#define TRACE
#using <System.ServiceProcess.dll>
#using <System.dll>
using namespace System;
using namespace System::ComponentModel;
using namespace System::IO;
using namespace System::ServiceProcess;
using namespace System::Threading;
using namespace System::Diagnostics;
// Define custom commands for the SimpleService.
public enum class SimpleServiceCustomCommands
{
StopWorker = 128,
RestartWorker, CheckWorker
};
// Define a simple service implementation.
public ref class SimpleService: public System::ServiceProcess::ServiceBase
{
private:
Thread^ workerThread;
int userCount;
public:
SimpleService()
{
CanPauseAndContinue = true;
ServiceName = "SimpleService";
workerThread = nullptr;
CanHandleSessionChangeEvent = true;
}
private:
void InitializeComponent()
{
// Initialize the operating properties for the service.
this->CanPauseAndContinue = true;
this->CanShutdown = true;
this->ServiceName = "SimpleService";
this->CanHandleSessionChangeEvent = true;
}
// Start the service.
protected:
virtual void OnStart( array<String^>^ ) override
{
// Start a separate thread that does the actual work.
if ( (workerThread == nullptr) || ((workerThread->ThreadState & (System::Threading::ThreadState::Unstarted | System::Threading::ThreadState::Stopped)) != (System::Threading::ThreadState)0) )
{
Trace::WriteLine( DateTime::Now.ToLongTimeString() + " - Starting the service worker thread.", "OnStart" );
workerThread = gcnew Thread( gcnew ThreadStart( this,&SimpleService::ServiceWorkerMethod ) );
workerThread->Start();
}
if ( workerThread != nullptr )
{
Trace::WriteLine( DateTime::Now.ToLongTimeString() + " - Worker thread state = " + workerThread->ThreadState.ToString(), "OnStart" );
}
}
// Stop this service.
protected:
virtual void OnStop() override
{
// Signal the worker thread to exit.
if ( (workerThread != nullptr) && (workerThread->IsAlive) )
{
Trace::WriteLine( DateTime::Now.ToLongTimeString() + " - Stopping the service worker thread.", "OnStop" );
workerThread->Abort();
// Wait up to 500 milliseconds for the thread to terminate.
workerThread->Join( 500 );
}
if ( workerThread != nullptr )
{
Trace::WriteLine( DateTime::Now.ToLongTimeString() + " - Worker thread state = " + workerThread->ThreadState.ToString(), "OnStop" );
}
}
// Pause the service.
protected:
virtual void OnPause() override
{
// Pause the worker thread.
if ( (workerThread != nullptr) && (workerThread->IsAlive) && ((workerThread->ThreadState & (System::Threading::ThreadState::Suspended | System::Threading::ThreadState::SuspendRequested)) == (System::Threading::ThreadState)0) )
{
Trace::WriteLine( DateTime::Now.ToLongTimeString() + " - Suspending the service worker thread.", "OnPause" );
workerThread->Suspend();
}
if ( workerThread != nullptr )
{
Trace::WriteLine( DateTime::Now.ToLongTimeString() + " - Worker thread state = " + workerThread->ThreadState.ToString(), "OnPause" );
}
}
// Continue a paused service.
protected:
virtual void OnContinue() override
{
// Signal the worker thread to continue.
if ( (workerThread != nullptr) && ((workerThread->ThreadState & (System::Threading::ThreadState::Suspended | System::Threading::ThreadState::SuspendRequested)) != (System::Threading::ThreadState)0) )
{
Trace::WriteLine( DateTime::Now.ToLongTimeString() + " - Resuming the service worker thread.", "OnContinue" );
workerThread->Resume();
}
if ( workerThread != nullptr )
{
Trace::WriteLine( DateTime::Now.ToLongTimeString() + " - Worker thread state = " + workerThread->ThreadState.ToString(), "OnContinue" );
}
}
// Handle a custom command.
protected:
virtual void OnCustomCommand( int command ) override
{
Trace::WriteLine( DateTime::Now.ToLongTimeString() + " - Custom command received: " + command, "OnCustomCommand" );
// If the custom command is recognized,
// signal the worker thread appropriately.
switch ( command )
{
case (int)SimpleServiceCustomCommands::StopWorker:
// Signal the worker thread to terminate.
// For this custom command, the main service
// continues to run without a worker thread.
OnStop();
break;
case (int)SimpleServiceCustomCommands::RestartWorker:
// Restart the worker thread if necessary.
OnStart( nullptr );
break;
case (int)SimpleServiceCustomCommands::CheckWorker:
// Log the current worker thread state.
Trace::WriteLine( DateTime::Now.ToLongTimeString() + " - Worker thread state = " + workerThread->ThreadState.ToString(), "OnCustomCommand" );
break;
default:
Trace::WriteLine( DateTime::Now.ToLongTimeString() + " - Unrecognized custom command ignored!", "OnCustomCommand" );
break;
}
}
protected:
virtual void OnSessionChange(SessionChangeDescription changeDescription) override
{
Trace::WriteLine( DateTime::Now.ToLongTimeString() + " - Change description received: "
+ changeDescription.ToString(), "OnSessionChange" );
switch (changeDescription.Reason)
{
case SessionChangeReason::SessionLogon:
userCount += 1;
Trace::WriteLine( DateTime::Now.ToLongTimeString() +
" SessionLogon, total users: " +
userCount.ToString(), "OnSessionChange" );
break;
case SessionChangeReason::SessionLogoff:
userCount -= 1;
Trace::WriteLine( DateTime::Now.ToLongTimeString() +
" SessionLogoff, total users: " +
userCount.ToString(), "OnSessionChange" );
break;
case SessionChangeReason::RemoteConnect:
userCount += 1;
Trace::WriteLine( DateTime::Now.ToLongTimeString() +
" RemoteConnect, total users: " +
userCount.ToString(), "OnSessionChange" );
break;
case SessionChangeReason::RemoteDisconnect:
userCount -= 1;
Trace::WriteLine( DateTime::Now.ToLongTimeString() +
" RemoteDisconnect, total users: " +
userCount.ToString(), "OnSessionChange" );
break;
case SessionChangeReason::SessionLock:
Trace::WriteLine( DateTime::Now.ToLongTimeString() +
" SessionLock", "OnSessionChange" );
break;
case SessionChangeReason::SessionUnlock:
Trace::WriteLine( DateTime::Now.ToLongTimeString() +
" SessionUnlock", "OnSessionChange" );
break;
default:
Trace::WriteLine( DateTime::Now.ToLongTimeString() +
" - Unhandled session change event.", "OnSessionChange" );
break;
}
}
// Define a simple method that runs as the worker thread for
// the service.
public:
void ServiceWorkerMethod()
{
Trace::WriteLine( DateTime::Now.ToLongTimeString() + " - Starting the service worker thread.", "Worker" );
try
{
for ( ; ; )
{
// Wake up every 10 seconds and write
// a message to the trace output.
Thread::Sleep( 10000 );
Trace::WriteLine( DateTime::Now.ToLongTimeString() + " - heartbeat cycle.", "Worker" );
}
}
catch ( ThreadAbortException^ )
{
// Another thread has signalled that this worker
// thread must terminate. Typically, this occurs when
// the main service thread receives a service stop
// command.
// Write a trace line indicating that the worker thread
// is exiting. Notice that this simple thread does
// not have any local objects or data to clean up.
Trace::WriteLine( DateTime::Now.ToLongTimeString() + " - Thread abort signaled.", "Worker" );
}
Trace::WriteLine( DateTime::Now.ToLongTimeString() + " - Exiting the service worker thread.", "Worker" );
}
};
int main()
{
String^ logFile = "C:\\service_log.txt";
TextWriterTraceListener^ serviceTraceListener = nullptr;
// Create a log file for trace output.
// A new file is created each time. If a
// previous log file exists, it is overwritten.
StreamWriter^ myFile = File::CreateText( logFile );
// Create a new trace listener that writes to the text file,
// and add it to the collection of trace listeners.
serviceTraceListener = gcnew TextWriterTraceListener( myFile );
Trace::Listeners->Add( serviceTraceListener );
Trace::AutoFlush = true;
Trace::WriteLine( DateTime::Now.ToLongTimeString() + " - Service main method starting...", "Main" );
// Load the service into memory.
System::ServiceProcess::ServiceBase::Run( gcnew SimpleService );
Trace::WriteLine( DateTime::Now.ToLongTimeString() + " - Service main method exiting...", "Main" );
// Remove and close the trace listener for this service.
Trace::Listeners->Remove( serviceTraceListener );
serviceTraceListener->Close();
serviceTraceListener = nullptr;
myFile->Close();
}
继承层次结构
System.Object
System.MarshalByRefObject
System.ComponentModel.Component
System.ServiceProcess.ServiceBase
线程安全
此类型的任何公共静态(Visual Basic 中的 Shared)成员都是线程安全的,但不保证所有实例成员都是线程安全的。
平台
Windows 98、Windows 2000 SP4、Windows Server 2003、Windows XP Media Center Edition、Windows XP Professional x64 Edition、Windows XP SP2、Windows XP Starter Edition
.NET Framework 并不是对每个平台的所有版本都提供支持。有关受支持版本的列表,请参见系统要求。
版本信息
.NET Framework
受以下版本支持:2.0、1.1、1.0
请参见
参考
ServiceBase 成员
System.ServiceProcess 命名空间
ServiceProcessInstaller
ServiceInstaller