Windows 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;
}