서비스 응용 프로그램의 일부로 존재할 서비스에 기본 클래스를 제공합니다. ServiceBase는 새 서비스 클래스를 만들 때 파생되어야 합니다.
네임스페이스: System.ServiceProcess
어셈블리: System.ServiceProcess(system.serviceprocess.dll)
언어 : C# / C++
// C# language example
C#
public class ServiceBase : Component
// C++ language example
C++
public ref class ServiceBase : public Component
설명
서비스 응용 프로그램에 있는 서비스 클래스를 정의할 때 ServiceBase에서 파생됩니다. 임의의 유용한 서비스가 OnStart 및 OnStop 메서드를 재정의합니다. 추가 기능의 경우, 서비스 상태의 변경 내용에 대한 특정 동작을 사용하여 OnPause 및 OnContinue를 재정의할 수 있습니다.
서비스는 사용자 인터페이스를 지원하지 않는 장기 실행 파일이며, 사용자 계정으로 로그온한 상태에서는 실행되지 않습니다. 이 서비스는 컴퓨터에 로그온 중인 사용자 없이도 실행될 수 있습니다.
기본적으로 서비스는 System 계정으로 실행됩니다. System 계정은 Administrator 계정과는 다르며, System 계정의 권한은 변경할 수 없습니다. 또한 ServiceProcessInstaller를 사용하여 서비스를 실행할 사용자 계정을 지정할 수 있습니다.
실행 파일은 두 가지 이상의 서비스를 포함할 수 있지만 각 서비스에 대해 별도의 ServiceInstaller를 포함해야 합니다. ServiceInstaller 인스턴스는 서비스를 시스템에 등록합니다. 설치 관리자는 또한 각 서비스를 서비스 명령을 기록하는 데 사용할 수 있는 이벤트 로그와 연결합니다. 실행 파일에 있는 main() 함수는 실행할 서비스를 정의합니다. 서비스의 현재 작업 디렉터리는 실행 파일이 있는 디렉터리가 아니라 시스템 디렉터리입니다.
서비스가 시작되면 시스템에서는 실행 파일을 찾아 해당 파일 내에 포함된 서비스에 대한 OnStart 메서드를 실행시킵니다. 그러나 서비스를 실행하는 것과 실행 파일을 실행하는 것은 다릅니다. 실행 파일은 서비스를 로드만 하지만, 서비스는 서비스 제어 관리자를 통해 시작 및 중지하는 방법으로 액세스됩니다.
사용자가 서비스에서 시작 명령을 처음으로 호출하면 실행 파일에서 ServiceBase 파생 클래스의 생성자를 호출합니다. OnStart 명령 처리 메서드는 생성자가 실행된 직후에 호출됩니다. 생성자는 서비스가 처음 로드된 이후에 다시 실행되지 않으므로 생성자에서 수행한 프로세스와 OnStart에서 수행한 프로세스를 구분해야 합니다. OnStop을 사용하여 해제할 수 있는 리소스는 OnStart를 사용하여 만들어야 합니다. OnStop에서 리소스를 해제한 다음 서비스를 다시 시작하면 생성자에서 리소스를 만들 때 리소스가 제대로 만들어지지 않습니다.
SCM(서비스 제어 관리자)은 서비스와 상호 작용하는 방법을 제공합니다. SCM을 사용하여 시작, 중지, 일시 중지, 계속 또는 사용자 지정 명령을 서비스에 전달할 수 있습니다. SCM은 CanStop 및 CanPauseAndContinue의 값을 사용하여 서비스에서 중지, 일시 중지 또는 계속 명령을 허용할지 여부를 결정합니다. 서비스 클래스에 있는 해당 속성인 CanStop 또는 CanPauseAndContinue가 true인 경우에만 SCM의 상황에 맞는 메뉴에서 중지, 일시 중지 및 계속을 사용할 수 있습니다. 명령을 사용할 수 있는 경우, 명령은 서비스에 전달되고 OnStop, OnPause 또는 OnContinue가 호출됩니다. CanStop, CanShutdown 또는 CanPauseAndContinue가 false이면 OnStop과 같은 해당 명령 처리 메서드가 구현되어 있어도 처리되지 않습니다.
SCM이 사용자 인터페이스를 사용하여 수행하는 작업을 ServiceController 클래스를 사용하여 프로그래밍 방식으로 수행할 수 있습니다. 콘솔에서 사용할 수 있는 작업을 자동화할 수 있습니다. CanStop, CanShutdown 또는 CanPauseAndContinue가 true이지만 해당 명령 처리 메서드(예: OnStop)를 구현하지 않은 경우 시스템에서 예외가 throw되고 해당 명령이 무시됩니다.
ServiceBase에 있는 OnStart, OnStop 또는 다른 메서드는 구현하지 않아도 됩니다. 그러나 서비스의 동작이 OnStart에 설명되어 있으므로 이 멤버를 재정의해야 합니다. 실행 파일의 main() 함수에 서비스의 이름을 설정해야 합니다. main()에 설정한 서비스 이름은 서비스 설치 관리자의 ServiceName 속성과 정확하게 일치해야 합니다.
InstallUtil.exe를 사용하여 시스템에 서비스를 설치할 수 있습니다.
예제
다음 예제에서는 ServiceBase 클래스에서 간단한 서비스 구현을 파생합니다. 이 서비스는 중지, 시작, 일시 중지, 계속 및 사용자 지정 명령을 포함한 다양한 서비스 명령을 처리합니다.
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;
{
// 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;
}
{
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,
}
public class SimpleService : System.ServiceProcess.ServiceBase
{
private static int userCount = 0;
private static ManualResetEvent pause = new ManualResetEvent(false);
public static extern bool SetServiceStatus(
IntPtr hServiceStatus,
SERVICE_STATUS lpServiceStatus
);
private SERVICE_STATUS myServiceStatus;
{
CanPauseAndContinue = true;
CanHandleSessionChangeEvent = true;
ServiceName = "SimpleService";
}
{
#if LOGEVENTS
EventLog.WriteEntry("SimpleService.Main", DateTime.Now.ToLongTimeString() +
" - Service main method starting...");
#endif
System.ServiceProcess.ServiceBase.Run(new SimpleService());
EventLog.WriteEntry("SimpleService.Main", DateTime.Now.ToLongTimeString() +
" - Service main method exiting...");
#endif
{
// Initialize the operating properties for the service.
this.CanPauseAndContinue = true;
this.CanShutdown = true;
this.CanHandleSessionChangeEvent = true;
this.ServiceName = "SimpleService";
}
protected override void OnStart(string[] args)
{
IntPtr handle = this.ServiceHandle;
myServiceStatus.currentState = (int)State.SERVICE_START_PENDING;
SetServiceStatus(handle, myServiceStatus);
((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.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);
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;
}
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
Thread.Sleep(5000);
}
{
#if LOGEVENTS
EventLog.WriteEntry("SimpleService.OnPause", DateTime.Now.ToLongTimeString() +
" OnPause - Worker thread state = " +
workerThread.ThreadState.ToString());
#endif
}
}
protected override void OnContinue()
{
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.");
pause.Set();
}
if (workerThread != null)
{
#if LOGEVENTS
EventLog.WriteEntry("SimpleService.OnContinue", DateTime.Now.ToLongTimeString() +
" OnContinue - Worker thread state = " +
workerThread.ThreadState.ToString());
#endif
}
}
protected override void OnCustomCommand(int command)
{
#if LOGEVENTS
EventLog.WriteEntry("SimpleService.OnCustomCommand", DateTime.Now.ToLongTimeString() +
" - Custom command received: " +
command.ToString());
#endif
// signal the worker thread appropriately.
{
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;
OnStart(null);
break;
#if LOGEVENTS
// Log the current worker thread state.
EventLog.WriteEntry("SimpleService.OnCustomCommand", DateTime.Now.ToLongTimeString() +
" OnCustomCommand - Worker thread state = " +
workerThread.ThreadState.ToString());
#endif
#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
{
case SessionChangeReason.SessionLogon:
userCount += 1;
#if LOGEVENTS
EventLog.WriteEntry("SimpleService.OnSessionChange",
DateTime.Now.ToLongTimeString() +
" SessionLogon, total users: " +
userCount.ToString());
#endif
break;
#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;
#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;
#if LOGEVENTS
EventLog.WriteEntry("SimpleService.OnSessionChange",
DateTime.Now.ToLongTimeString() +
" SessionUnlock");
#endif
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
{
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.
// 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
" - Exiting the service worker thread.");
#endif
}
}
// C++ language example
#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.MarshalByRefObject
System.ComponentModel.Component
System.ServiceProcess.ServiceBase
플랫폼
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에서 지원참고항목
System.ServiceProcess 네임스페이스
ServiceProcessInstaller
ServiceInstaller
출처 : http://msdn.microsoft.com/ko-kr/library/system.serviceprocess.servicebase(v=VS.80).aspx
'IT-개발,DB' 카테고리의 다른 글
[개발/.net] 닷넷 프레임워크 - 프로그래밍 방식으로 윈도우즈 서비스 작성 (0) | 2010.08.18 |
---|---|
[개발] Visual C++ 디버깅 Trace log 파일 작성하기 (0) | 2010.08.17 |
[개발/Visual C++] MFC ActiveX 만들기 할때 필요한 MFC 라이브러리 (0) | 2010.08.13 |
[개발] INF 파일을 이용한 ActiveX inf Hook (0) | 2010.08.12 |
[개발] INF 파일 설명서 (0) | 2010.08.12 |
댓글