Windows API: Service Program (HelperService.exe)
This is a good example of a Windows Service written in C. A better example would use C++ and the "Event Viewer".
This Windows Service installs itself and uninstalls itself. Run "HelperService.exe /help" for install instructions.
This service executes an executable at a time everyday specified in its INI file. Hence the name "Helper". This is useful because the programmer can write the executable in higher generation languages like C# or VB.NET where productivity is high.
Download the 64-bit executable w/INI file and README text file: HELPERSERVICE.zip.
#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <winsvc.h>
#define ALLOC(siz) HeapAlloc(GetProcessHeap(), 0, siz)
#define FREE(mem) HeapFree(GetProcessHeap(), 0, mem)
char *ini, *szServiceName, *szProcessToCreate, *szProcessArguments, *szTimeToExecute;
HANDLE hLog;
void printf(const char *s)
{
unsigned long i;
for (i = 0; s[i]; i++);
WriteFile(CreateFileA("CONOUT$", GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL), s, i, &i, NULL);
}
SERVICE_STATUS ServStatus;
SERVICE_STATUS_HANDLE ServStatusHandle;
const char szDefaultINI[] = "ServiceName=\r\nExeToRun=\r\nArguments=\r\nTimeToExecute=6:00\r\nLogFile=";
void WriteLog(char *szText)
{
if (hLog != INVALID_HANDLE_VALUE)
{
DWORD dw;
WriteFile(hLog, szText, strlen(szText), &dw, NULL);
}
}
void PrintLastErrorToLog(char *szAddOn)
{
LPSTR lpMsgBuf;
int i = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&lpMsgBuf, 0, NULL);
while (lpMsgBuf[i - 1] == '\r' || lpMsgBuf[i - 1] == '\n')i--;
lpMsgBuf[i] = 0;
WriteLog(lpMsgBuf);
LocalFree(lpMsgBuf);
if (szAddOn)
WriteLog(szAddOn);
}
void WaitForInput()
{
DWORD dw;
ReadFile(CreateFileA("CONIN$", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL), &dw, 1, &dw, NULL);
}
void PrintLastErrorToScreen()
{
LPSTR lpMsgBuf;
FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&lpMsgBuf, 0, NULL);
printf("Error: ");
printf(lpMsgBuf);
LocalFree(lpMsgBuf);
WaitForInput();
}
void __stdcall ServCtrlHandler(DWORD Opcode)
{
switch (Opcode)
{
case SERVICE_CONTROL_STOP:
ServStatus.dwWin32ExitCode = 0;
ServStatus.dwCurrentState = SERVICE_STOP_PENDING;
ServStatus.dwCheckPoint = 0;
ServStatus.dwWaitHint = 3000;
if (!SetServiceStatus(ServStatusHandle, &ServStatus))
PrintLastErrorToLog(" : SetServiceStatus()\r\n");
Sleep(ServStatus.dwWaitHint);
if (INVALID_HANDLE_VALUE != hLog)
CloseHandle(hLog);
FREE(ini);
ServStatus.dwWaitHint = 0;
ServStatus.dwCurrentState = SERVICE_STOPPED;
break;
case SERVICE_CONTROL_INTERROGATE:
break;
default:
WriteLog("Unrecognized Control Handler Code\r\n");
}
// Send current status.
if (!SetServiceStatus(ServStatusHandle, &ServStatus))
PrintLastErrorToLog(" : SetServiceStatus()\r\n");
return;
}
void MakeTime(SYSTEMTIME *st, char *output)
{
GetLocalTime(st);
int i = 0;
_itoa(st->wYear, &output[i], 10);
while (output[++i]);
output[i++] = '\\';
if (st->wMonth < 10)
output[i++] = '0';
_itoa(st->wMonth, &output[i], 10);
while (output[++i]);
output[i++] = '\\';
if (st->wDay < 10)
output[i++] = '0';
_itoa(st->wDay, &output[i], 10);
while (output[++i]);
output[i++] = ' ';
if (st->wHour < 10)
output[i++] = '0';
_itoa(st->wHour, &output[i], 10);
while (output[++i]);
output[i++] = ':';
if (st->wMinute < 10)
output[i++] = '0';
_itoa(st->wMinute, &output[i], 10);
while (output[++i]);
output[i++] = ':';
if (st->wSecond < 10)
output[i++] = '0';
_itoa(st->wSecond, &output[i], 10);
}
BOOL ExecuteProcess()
{
STARTUPINFOA *si = (STARTUPINFOA *)ALLOC(sizeof(STARTUPINFOA));
PROCESS_INFORMATION *pi = (PROCESS_INFORMATION *)ALLOC(sizeof(PROCESS_INFORMATION));
ZeroMemory(si, sizeof(STARTUPINFOA));
si->cb = sizeof(STARTUPINFOA);
ZeroMemory(pi, sizeof(PROCESS_INFORMATION));
char *szCmdLine = (char *)ALLOC(strlen(szProcessToCreate) + strlen(szProcessArguments) + 4);
strcpy(szCmdLine, szProcessToCreate);
strcat(szCmdLine, " ");
strcat(szCmdLine, szProcessArguments);
if (!CreateProcessA(NULL, szCmdLine, NULL, NULL, FALSE, 0, NULL, NULL, si, pi))
{
PrintLastErrorToLog(" : CreateProcess()");
FREE(si);
FREE(pi);
return FALSE;
}
WaitForSingleObject(pi->hProcess, INFINITE);
CloseHandle(pi->hProcess);
CloseHandle(pi->hThread);
FREE(pi);
FREE(si);
return TRUE;
}
void __stdcall StartServ(DWORD argc, LPSTR *argv)
{
int i;
SYSTEMTIME st;
char tim[20];
long hr, min;
ServStatus.dwServiceType = SERVICE_WIN32;
ServStatus.dwCurrentState = SERVICE_START_PENDING;
ServStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
ServStatus.dwWin32ExitCode = 0;
ServStatus.dwServiceSpecificExitCode = 0;
ServStatus.dwCheckPoint = 0;
ServStatus.dwWaitHint = 0;
ServStatusHandle = RegisterServiceCtrlHandlerA(szServiceName, ServCtrlHandler);
if (ServStatusHandle == (SERVICE_STATUS_HANDLE)0)
{
PrintLastErrorToLog(" : RegisterServiceCtrlHandler()\r\n");
goto exit;
}
WriteLog("Service started at ");
MakeTime(&st, tim);
WriteLog(tim);
WriteLog("\r\n");
// Initialization complete - report running status.
ServStatus.dwCurrentState = SERVICE_RUNNING;
ServStatus.dwCheckPoint = 0;
ServStatus.dwWaitHint = 0;
if (!SetServiceStatus(ServStatusHandle, &ServStatus))
PrintLastErrorToLog(" : SetServiceStatus()\r\n");
for (i = 0; szTimeToExecute[i] && szTimeToExecute[i] != ':' && szTimeToExecute[i] >= '0' && szTimeToExecute[i] <= '9'; i++);
if (':' == szTimeToExecute[i])
{
szTimeToExecute[i++] = 0;
hr = atol(szTimeToExecute);
szTimeToExecute = &szTimeToExecute[i];
min = atol(szTimeToExecute);
}
else
hr = min = 0;
while (SERVICE_RUNNING == ServStatus.dwCurrentState)
{
GetLocalTime(&st);
if (st.wHour == hr && st.wMinute == min && st.wSecond == 0)
{
MakeTime(&st, tim);
WriteLog(tim);
WriteLog(" Execute process initiated.\r\n");
ExecuteProcess();
//PrintLastErrorToLog(" : QueryServiceStatus() (1)");
}
Sleep(1000); // MUST SLEEP FOR ONE SECOND
}
exit:
return;
}
#define INSTALL_FOUND 1
#define UNINSTALL_FOUND 2
#define HELP_FOUND 4
#define SERVICE_NAME 1
#define PROCESS_TO_CREATE 2
#define LOG_FILE 3
#define TIME_TO_EXECUTE 4
#define PROCESS_ARGUMENTS 5
int FindINIParam(char *nam)
{
if (0 == _stricmp(nam, "ServiceName"))
return SERVICE_NAME;
else if (0 == _stricmp(nam, "ExeToRun"))
return PROCESS_TO_CREATE;
else if (0 == _stricmp(nam, "LogFile"))
return LOG_FILE;
else if (0 == _stricmp(nam, "TimeToExecute"))
return TIME_TO_EXECUTE;
else if (0 == _stricmp(nam, "Arguments"))
return PROCESS_ARGUMENTS;
return 0;
}
void ParseINI()
{
char *sz, *szLog = NULL;
int i = 0;
for (i = 0; ini[i];)
{
while ('\r' == ini[i] || '\n' == ini[i] || ' ' == ini[i])
{
if (0 == ini[i++])
goto exit;
}
sz = &ini[i];
while ('=' != ini[i])
{
if (0 == ini[i++])
goto exit;
}
ini[i++] = 0;
switch (FindINIParam(sz))
{
case SERVICE_NAME:
szServiceName = &ini[i];
break;
case PROCESS_TO_CREATE:
szProcessToCreate = &ini[i];
break;
case LOG_FILE:
szLog = &ini[i];
break;
case TIME_TO_EXECUTE:
szTimeToExecute = &ini[i];
break;
case PROCESS_ARGUMENTS:
szProcessArguments = &ini[i];
break;
}
while ('\r' != ini[i] && '\n' != ini[i])
{
if (0 == ini[i++])
goto exit;
}
ini[i++] = 0;
}
exit:
if (szLog)
{
hLog = CreateFileA(szLog, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (INVALID_HANDLE_VALUE != hLog)
SetFilePointer(hLog, 0, NULL, FILE_END);
}
}
const char * HELP_PART_1 = "\r\nService Name: ";
const char * HELP_PART_2 = "\r\n\r\nUSAGE\r\n\r\nInstall Service: HelperService.exe /install\r\n\r\nUninstall Service: HelperService.exe /uninstall\r\n\r\n";
int main(int argc, char *argv[])
{
int action;
int i;
HANDLE hTmp;
char *sz = (char *)ALLOC(strlen(argv[0]) + 2);
strcpy(sz, argv[0]);
for (i = strlen(sz) - 1; sz[i] != '\\'; i--);
sz[i] = 0;
SetCurrentDirectoryA(sz);
sz[i] = '\\';
for (i = strlen(sz) - 1; sz[i] != '.'; i--);
strcpy(&(sz[i + 1]), "INI");
hTmp = CreateFileA(sz, GENERIC_WRITE | GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (INVALID_HANDLE_VALUE != hTmp)
{
FREE(sz);
DWORD dw = GetFileSize(hTmp, NULL);
if (dw <= strlen(szDefaultINI))
{
printf("INI file not setup");
if (dw < strlen(szDefaultINI))
WriteFile(hTmp, szDefaultINI, strlen(szDefaultINI), &dw, NULL);
CloseHandle(hTmp);
return 0;
}
ini = (char *)ALLOC(dw + 1);
if (!ReadFile(hTmp, ini, dw, &dw, NULL))
{
CloseHandle(hTmp);
printf("INI file read error");
FREE(ini);
return 0;
}
CloseHandle(hTmp);
if (0 == dw)
{
printf("INI file not read");
FREE(ini);
return 0;
}
ini[dw] = 0;
ParseINI();
if (NULL == szServiceName || 0 == szServiceName[0] || NULL == szProcessToCreate || 0 == szProcessToCreate[0] || NULL == szTimeToExecute || 0 == szTimeToExecute[0])
{
printf("INI file incomplete");
FREE(ini);
return 0;
}
}
else
{
FREE(sz);
printf("Error opening INI file");
return 0;
}
SERVICE_TABLE_ENTRYA DispatchTable[] =
{
{ (char *)szServiceName, StartServ },
{ NULL, NULL }
};
for (action = i = 0; i < argc; i++)
{
if (0 == _stricmp(argv[i], "/install"))
action |= INSTALL_FOUND;
else if (0 == _stricmp(argv[i], "/uninstall"))
action |= UNINSTALL_FOUND;
else if (0 == _stricmp(argv[i], "/help"))
action |= HELP_FOUND;
else if (0 == _stricmp(argv[i], "/?"))
action |= HELP_FOUND;
else if (0 == _stricmp(argv[i], "help"))
action |= HELP_FOUND;
else if (0 == _stricmp(argv[i], "?"))
action |= HELP_FOUND;
}
if (((action & INSTALL_FOUND) || (action & UNINSTALL_FOUND)) && 2 == argc)
{
ShellExecuteA(NULL, "runas", argv[0], ((action & INSTALL_FOUND) != 0 ? "/install /admin" : "/uninstall /admin"), NULL, SW_SHOWNORMAL);
}
else if ((action & INSTALL_FOUND) && 3 == argc)
{
SC_HANDLE scm = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);
if (scm != NULL)
{
SC_HANDLE serv = CreateServiceA(scm, szServiceName, szServiceName, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, argv[0], NULL, NULL, NULL, NULL, NULL);
if (NULL != serv)
{
if (!StartService(serv, 0, NULL))
PrintLastErrorToScreen();
else
{
printf("'");
printf(szServiceName);
printf("' installed and running\r\n\r\n");
WaitForInput();
}
CloseServiceHandle(serv);
}
else
PrintLastErrorToScreen();
CloseServiceHandle(scm);
}
else
PrintLastErrorToScreen();
}
else if ((action & UNINSTALL_FOUND) && 3 == argc)
{
SC_HANDLE scm = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
if (scm != NULL)
{
SC_HANDLE serv = OpenServiceA(scm, szServiceName, SERVICE_ALL_ACCESS);
if (NULL != serv)
{
SERVICE_STATUS stat;
if (QueryServiceStatus(serv, &stat))
{
if (stat.dwCurrentState != SERVICE_STOPPED)
{
if (ControlService(serv, SERVICE_CONTROL_STOP, &stat))
{
while (stat.dwCurrentState != SERVICE_STOP_PENDING && stat.dwCurrentState != SERVICE_STOPPED)
{
Sleep(1000);
if (!QueryServiceStatus(serv, &stat))
{
PrintLastErrorToLog("\r\n");
break;
}
}
while (stat.dwCurrentState != SERVICE_STOPPED)
{
Sleep(1000);
if (!QueryServiceStatus(serv, &stat))
{
PrintLastErrorToLog("\r\n");
break;
}
}
}
}
}
if (!DeleteService(serv))
PrintLastErrorToScreen();
else
printf("Service uninstalled\r\n\r\n");
CloseServiceHandle(serv);
WaitForInput();
}
else
PrintLastErrorToScreen();
CloseServiceHandle(scm);
}
else
PrintLastErrorToScreen();
}
else if (action & HELP_FOUND)
{
printf(HELP_PART_1);
printf(szServiceName);
printf(HELP_PART_2);
WaitForInput();
}
else
{
SC_HANDLE scm = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
if (scm != NULL)
{
SC_HANDLE serv = OpenServiceA(scm, szServiceName, SERVICE_ALL_ACCESS);
if (NULL != serv)
{
CloseServiceHandle(serv);
CloseServiceHandle(scm);
printf("\r\nAttempting to start service.");
StartServiceCtrlDispatcherA(DispatchTable);
}
else
{
switch (GetLastError())
{
case ERROR_ACCESS_DENIED:
printf("\r\nAccess denied.");
break;
case ERROR_INVALID_NAME:
printf("\r\nInvalid Service Name.");
break;
case ERROR_SERVICE_DOES_NOT_EXIST:
printf("\r\nService not installed.\r\n");
printf(HELP_PART_1);
printf(szServiceName);
printf(HELP_PART_2);
break;
}
}
CloseServiceHandle(scm);
}
WaitForInput();
}
return 0;
}