Error 1053: Adventures in writing Windows services.

By Alex Chachanashvili (achacha at hotmail dot com)

The dreaded "Error 1053: The service did not respond to the start or control request in a timely fashion".

While cleaning up the help text of my watchdog service class, I started getting this error when trying to start the process. The error description was not very helpful in helping me figure out what went wrong, afterall, this was working just fine before the cleanup and the only thing I added was to display the help message when the console application was invoked without any parameters.

Searching for the help message on the web yielded a lot of questions about this error and very few obscure patched from Microsoft for their own products (IIS, MSQL, etc).

Adding debug traces to the code showed that the main handler was never called. So I decided to write a service from scratch to determine why I was getting this message.

First I needed to associate the service with the Service Control database:

SC_HANDLE schManager = ::OpenSCManager(

NULL,

SERVICES_ACTIVE_DATABASE,

SC_MANAGER_ALL_ACCESS | SC_MANAGER_CREATE_SERVICE

);

This will get the Service Control Manager which is used in creating services:

SC_HANDLE schService = ::CreateService(

schManager, // SC_HANDLE hSCManager,

TEXT("MyService"), // LPCTSTR lpServiceName,

TEXT("MyService"), // LPCTSTR lpDisplayName,

SERVICE_ALL_ACCESS, // DWORD dwDesiredAccess,

SERVICE_WIN32_OWN_PROCESS, // DWORD dwServiceType,

SERVICE_AUTO_START, // DWORD dwStartType,

SERVICE_ERROR_NORMAL, // DWORD dwErrorControl,

"C:\mypath\myservice.exe", // LPCTSTR lpBinaryPathName,

NULL, // LPCTSTR lpLoadOrderGroup,

NULL, // LPDWORD lpdwTagId,

NULL, // LPCTSTR lpDependencies,

NULL, // LPCTSTR lpServiceStartName,

NULL // LPCTSTR lpPassword

);

The odd part about Windows services is that you first register a DLL (an executable is a DLL) then service control manager calls the main() of your application with no parameters and in doing so you call to register a handler:

SERVICE_TABLE_ENTRY steServiceTable[2];

steServiceTable[0].lpServiceName = TEXT("MyService");

steServiceTable[0].lpServiceProc = myServiceMain;

steServiceTable[1].lpServiceName = NULL; //a_Must be NULL

steServiceTable[1].lpServiceProc = NULL; //a_Must be NULL

if(!::StartServiceCtrlDispatcher(steServiceTable))

{

switch(::GetLastError())

{

case ERROR_FAILED_SERVICE_CONTROLLER_CONNECT :

// Console mode, you tried connecting to the dispatcher from your console program

return 0; // Return of 0 means that your are in console mode

case ERROR_INVALID_DATA :

// Invalid data

return -1;

default :

return -1; //Some other unknown error?!?

}

return 1; // service dispatcher connected

}

If the service control manager calls your main() and you call StartServiceCtrlDispatcher and register an entry point (here it is myServiceMain), then when an event is received from either Services control panel app or sc.exe, your service has a main function that registers event handler and it looks like this:

VOID WINAPI myServiceMain(DWORD argc, LPSTR *argv)

{

SERVICE_STATUS_HANDLE sshService = ::RegisterServiceCtrlHandlerEx(

TEXT("MyService"),

(LPHANDLER_FUNCTION_EX)myServiceHandler,

NULL

);

if(!sshService)

{

// Failed to register service handler

return;

}

// etc

}

And now your service handler:

DWORD WINAPI fcbServiceHandler(DWORD dwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext)

{

// handle dwControl for you service

return NO_ERROR;

}

All that said the problem with my service was that I accidentally added ode that returned from main right away without connecting to the service dispatcher which incurred the dreaded Error 1053 and caused me to dig around Windows SDK to help me understand how services API works.