创建多线程服务
下面的例子论述了,一个简单的服务怎么样创建工作进程、响应SCM(服务控制管理器)、通知线程退出、不断地向SCM通知当前状态和处理过程、然后向SCM报告服务停止。要安装这个服务,请把它创建成为以给控制台程序,并使用Platform SDK中的SC工具。使用控制面板中的服务控制工具来开启关闭这个服务。
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
HANDLE hStopEvent;
HANDLE hThreads[3] = {NULL,NULL,NULL};
LPTSTR lpszServiceName;
SERVICE_STATUS_HANDLE ssh;
DWORD WINAPI ThreadProc(LPVOID lpParameter);
void WINAPI Service_Main(DWORD dwArgc, LPTSTR *lpszArgv);
void WINAPI Service_Ctrl(DWORD dwCtrlCode);
void ErrorStopService(LPTSTR lpszAPI);
void SetTheServiceStatus(DWORD dwCurrentState,DWORD dwWin32ExitCode,
DWORD dwCheckPoint, DWORD dwWaitHint);
// Entry point for service. Calls StartServiceCtrlDispatcher
// and then blocks until the ServiceMain function returns.
void _tmain(int argc, TCHAR *argv[])
{
SERVICE_TABLE_ENTRY ste[] =
{{TEXT(""),(LPSERVICE_MAIN_FUNCTION)Service_Main}, {NULL,NULL}};
OutputDebugString(TEXT("Entered service code "));
if (!StartServiceCtrlDispatcher(ste))
{
TCHAR error[256];
wsprintf(error,
TEXT("Error code for StartServiceCtrlDispatcher: %u. "),
GetLastError());
OutputDebugString(error);
}
else
OutputDebugString(TEXT("StartServiceCtrlDispatcher OK "));
}
// Called by the service control manager after the call to
// StartServiceCtrlDispatcher.
void WINAPI Service_Main(DWORD dwArgc, LPTSTR *lpszArgv)
{
DWORD ThreadId;
DWORD t;
DWORD dwWaitRes;
// Obtain the name of the service.
lpszServiceName = lpszArgv[0];
// Register the service ctrl handler.
ssh = RegisterServiceCtrlHandler(lpszServiceName,
(LPHANDLER_FUNCTION)Service_Ctrl);
// Create the event to signal the service to stop.
hStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (hStopEvent == NULL)
ErrorStopService(TEXT("CreateEvent"));
//
// Insert one-time work that you want to complete before starting.
//
for (t=0;t<3;t++)
{
hThreads[t] = CreateThread(NULL,0,ThreadProc,
(LPVOID)t,0,&ThreadId);
if (hThreads[t] == INVALID_HANDLE_VALUE)
ErrorStopService(TEXT("CreateThread"));
}
// The service has started.
SetTheServiceStatus(SERVICE_RUNNING, 0, 0, 0);
OutputDebugString(TEXT("SetTheServiceStatus, SERVICE_RUNNING "));
//
// Main loop for the service.
//
while(WaitForSingleObject(hStopEvent, 1000) != WAIT_OBJECT_0)
{
/***************************************************************/
// Main loop for service.
/***************************************************************/
}
// Wait for threads to exit.
for (t=1;TRUE;t++)
{
if ((dwWaitRes = WaitForMultipleObjects(3,hThreads,TRUE,1000))
== WAIT_OBJECT_0)
break;
else if((dwWaitRes == WAIT_FAILED)||(dwWaitRes==WAIT_ABANDONED))
ErrorStopService(TEXT("WaitForMultipleObjects"));
else
SetTheServiceStatus(SERVICE_STOP_PENDING, 0, t, 3000);
}
// Close the event handle and the thread handle.
if (!CloseHandle(hStopEvent))
ErrorStopService(TEXT("CloseHandle"));
if (!CloseHandle(hThreads[0]))
ErrorStopService(TEXT("CloseHandle"));
if (!CloseHandle(hThreads[1]))
ErrorStopService(TEXT("CloseHandle"));
if (!CloseHandle(hThreads[2]))
ErrorStopService(TEXT("CloseHandle"));
// Stop the service.
OutputDebugString(TEXT("SetTheServiceStatus, SERVICE_STOPPED "));
SetTheServiceStatus(SERVICE_STOPPED, NO_ERROR, 0, 0);
}
// Handles control signals from the service control manager.
void WINAPI Service_Ctrl(DWORD dwCtrlCode)
{
DWORD dwState = SERVICE_RUNNING;
switch(dwCtrlCode)
{
case SERVICE_CONTROL_STOP:
dwState = SERVICE_STOP_PENDING;
break;
case SERVICE_CONTROL_SHUTDOWN:
dwState = SERVICE_STOP_PENDING;
break;
case SERVICE_CONTROL_INTERROGATE:
break;
default:
break;
}
// Set the status of the service.
SetTheServiceStatus(dwState, NO_ERROR, 0, 0);
OutputDebugString(
TEXT("SetTheServiceStatus, Service_Ctrl function "));
// Tell service_main thread to stop.
if ((dwCtrlCode == SERVICE_CONTROL_STOP) ||
(dwCtrlCode == SERVICE_CONTROL_SHUTDOWN))
{
if (!SetEvent(hStopEvent))
ErrorStopService(TEXT("SetEvent"));
else
OutputDebugString(TEXT("Signal service_main thread "));
}
}
// Thread procedure for all three worker threads.
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
INT nThreadNum = (INT)lpParameter;
TCHAR szOutput[25];
while(WaitForSingleObject(hStopEvent, 1000) != WAIT_OBJECT_0)
{
// Just to have something to do, it will beep every second.
Sleep(1000);
wsprintf(szOutput,TEXT(" Thread %d says Beep "),nThreadNum);
OutputDebugString(szOutput); //Send visual to debugger.
}
return 0;
}
// Wraps SetServiceStatus.
void SetTheServiceStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode,
DWORD dwCheckPoint, DWORD dwWaitHint)
{
SERVICE_STATUS ss; // Current status of the service.
// Disable control requests until the service is started.
if (dwCurrentState == SERVICE_START_PENDING)
ss.dwControlsAccepted = 0;
else
ss.dwControlsAccepted =
SERVICE_ACCEPT_STOP|SERVICE_ACCEPT_SHUTDOWN;
// Other flags include SERVICE_ACCEPT_PAUSE_CONTINUE
// and SERVICE_ACCEPT_SHUTDOWN.
// Initialize ss structure.
ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
ss.dwServiceSpecificExitCode = 0;
ss.dwCurrentState = dwCurrentState;
ss.dwWin32ExitCode = dwWin32ExitCode;
ss.dwCheckPoint = dwCheckPoint;
ss.dwWaitHint = dwWaitHint;
// Send status of the service to the Service Controller.
if (!SetServiceStatus(ssh, &ss))
ErrorStopService(TEXT("SetServiceStatus"));
}
// Handle API errors or other problems by ending the service and
// displaying an error message to the debugger.
void ErrorStopService(LPTSTR lpszAPI)
{
INT t;
TCHAR buffer[256] = TEXT("");
TCHAR error[1024] = TEXT("");
LPVOID lpvMessageBuffer;
DWORD dwWaitRes;
wsprintf(buffer,TEXT("API = %s, "), lpszAPI);
lstrcat(error, buffer);
ZeroMemory(buffer, sizeof(buffer));
wsprintf(buffer,TEXT("error code = %d, "), GetLastError());
lstrcat(error, buffer);
// Obtain the error string.
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
NULL, GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpvMessageBuffer, 0, NULL);
ZeroMemory((LPVOID)buffer, (DWORD)sizeof(buffer));
wsprintf(buffer,TEXT("message = %s"), (TCHAR *)lpvMessageBuffer);
lstrcat(error, buffer);
// Free the buffer allocated by the system.
LocalFree(lpvMessageBuffer);
// Write the error string to the debugger.
OutputDebugString(error);
// If you have threads running, tell them to stop. Something went
// wrong, and you need to stop them so you can inform the SCM.
SetEvent(hStopEvent);
// Wait for the threads to stop.
for (t=1;TRUE;t++)
{
if ((dwWaitRes = WaitForMultipleObjects(3,hThreads,TRUE,1000))
== WAIT_OBJECT_0)
break;
else if ((dwWaitRes == WAIT_FAILED)||
(dwWaitRes == WAIT_ABANDONED))
break; // Our wait failed
else
{
SetTheServiceStatus(SERVICE_STOP_PENDING, 0, t, 3000);
}
}
// Stop the service.
SetTheServiceStatus(SERVICE_STOPPED, GetLastError(), 0, 0);
}