Using the ServiceMain() and the related functions for Windows services programming

 

Compiler: Visual C++ Express Edition 2005

Compiled on Platform: Windows XP Pro SP2

Target platform: none, just for learning and fun

Header file: Standard and Windows

Additional library: Windows Platform SDK

Additional project setting: Set project to be compiled as C. Click Project menu->your_project_name Properties->Configuration Properties->C/C++->Advanced->Compiled As: Compiled as C Code (/TC)

Other info: non-CLR or unmanaged. Need to add Advapi32.lib (Advapi32.dll) to the project. Click the Project menu->Select the your_project_name Properties... sub menu->Expand the Configuration Properties folder on the left pane->Expand the Linker subfolder->Select the Input subfolder->Select the Additional Dependencies field on the right pane->Click the ... at the end of the field->Type in 'Advapi32.lib' in the empty pane->Click the OK button->Click the OK button second time to close the project Properties dialog.

To do: Using the ServiceMain() and the related functions

To show: The Windows process and service Win32 C program example

 

// For WinXp as a target

#define _WIN32_WINNT 0x0501

#include <windows.h>

#include <tchar.h>

#include <stdio.h>

 

HANDLE hStopEvent;

// Three thread handle...

HANDLE hThreads[3] = {NULL, NULL, NULL};

LPTSTR lpszServiceName;

SERVICE_STATUS_HANDLE ssh;

DWORD WINAPI ThreadProc(LPVOID lpParameter);

 

// Service start, the generic ServiceMain()...

void WINAPI Service_Main(DWORD dwArgc, LPTSTR *lpszArgv);

 

// Service control...

void WINAPI Service_Ctrl(DWORD dwCtrlCode);

 

// Stop service...

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 Service_Main() function returns...

// void wmain(int argc, wchar_t *argv[])

void _tmain(int argc, TCHAR *argv[])

{

SERVICE_TABLE_ENTRY ste[] = {{TEXT("TIntSvr"), (LPSERVICE_MAIN_FUNCTION)Service_Main}, {NULL, NULL}};

TCHAR error[256];

if (!StartServiceCtrlDispatcher(ste))

{

wsprintf(error, TEXT("Error code for StartServiceCtrlDispatcher(): %u.\n"), GetLastError());

// Check your output window for the message and code error after you debug this program

OutputDebugString(error);

}

else

OutputDebugString(TEXT("StartServiceCtrlDispatcher() returned!\n"));

}

 

// Called by the service control manager (SCM) 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, L"ThisIsMyEvent");

 

if (hStopEvent == NULL)

ErrorStopService(TEXT("CreateEvent() error"));

else

printf("CreateEvent() looks OK.\n");

 

// 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() error"));

else

printf("CreateThread() looks OK.\n");

printf("Thread ID: %lu.\n", ThreadId);

}

// The service has started...

SetTheServiceStatus(SERVICE_RUNNING, 0, 0, 0);

OutputDebugString(TEXT("SetTheServiceStatus(), SERVICE_RUNNING.\n"));

 

// Main loop for the service...

while(WaitForSingleObject(hStopEvent, 1000) != WAIT_OBJECT_0)

{

printf("Main loop for the service...\n");

/***************************************************************/

// 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() error"));

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.\n"));

SetTheServiceStatus(SERVICE_STOPPED, NO_ERROR, 0, 0);

}

 

// Handles control signals from the service control manager...

// WINAPI - using __stdcall convention explicitly instead of __cdecl...

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\n"));

 

// Tell Service_Main thread to stop...

if ((dwCtrlCode == SERVICE_CONTROL_STOP) || (dwCtrlCode == SERVICE_CONTROL_SHUTDOWN))

{

if (!SetEvent(hStopEvent))

ErrorStopService(TEXT("SetEvent() error"));

else

OutputDebugString(TEXT("Signalling Service_Main() thread.\n"));

}

}

 

// Thread procedure for all five 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(2000);

wsprintf(szOutput, TEXT("\nThread %d says Beep\n"), nThreadNum);

// Send visual to debugger.

OutputDebugString(szOutput);

}

return 0;

}

 

// Wraps SetServiceStatus()...

void SetTheServiceStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwCheckPoint, DWORD dwWaitHint)

{

// Current status of the service.

SERVICE_STATUS ss;

 

// 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() error"));

else

printf("SetServiceStatus() looks OK.\n");

return;

}

 

// Handle API errors or other problems by ending the service and displaying an error message to the debugger.

void ErrorStopService(LPTSTR lpszAPI)

{

INT t;

// Just dummy initial values...

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);

return;

}

 

Output example:

 

This is not a complete working example. Please refer to : Windows Services Programming with Win32 for a complete working example.

 

 

// Check your output window for the 1063 error, when you debug this program

Error code for StartServiceCtrlDispatcher(): 1063.

 

ERROR_FAILED_SERVICE_CONTROLLER_CONNECT - 1063 The service process could not connect to the service controller.

 

 

 

C and C++ Programming Resources | C & C++ Code Example Index