PROWAREtech

articles » current » windows » application-programming-interface » helper-service

Windows API: "Helper" Service Program

An example Windows service program that executes another program; written in C/C++.

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 32-bit/x86 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 = NULL, * szProcessToCreate = NULL, * szProcessArguments = NULL, * szTimeToExecute = NULL;
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=1:00\r\nLogFile=";

void WriteLog(const char* szText)
{
	if (hLog != INVALID_HANDLE_VALUE)
	{
		DWORD dw;
		WriteFile(hLog, szText, strlen(szText), &dw, NULL);
	}
}

void PrintLastErrorToLog(const 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(int lastError)
{
	LPSTR lpMsgBuf;
	FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, lastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&lpMsgBuf, 0, NULL);
	printf("Error ");
	char sz[10];
	_itoa(lastError, sz, 10);
	printf(sz);
	printf(": ");
	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));
	if (!CreateProcessA(szProcessToCreate, szProcessArguments, 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();
		}
		Sleep(500); // MUST SLEEP FOR ONE-HALF 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 domain\\user-name user-password\r\nNOTE: The user should have the \"Log on as a service\" right\r\n\r\nInstall Service: HelperService.exe /install null null\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] && sz[i] != '\\'; i--);
	if (sz[i])
	{
		sz[i] = 0;
		SetCurrentDirectoryA(sz);
		sz[i] = '\\';
	}
	for (i = strlen(sz) - 1; sz[i] && sz[i] != '.'; i--);
	if (sz[i])
		strcpy(&(sz[i + 1]), "ini");
	else
		strcat(sz, ".ini");
	hTmp = CreateFileA(sz, GENERIC_WRITE | GENERIC_READ, 0, NULL, OPEN_ALWAYS, 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 & HELP_FOUND))
	{
		if (4 == argc)
		{
			int len = 0;
			for (i = 1; i < argc; i++)
				len += strlen(argv[i]) + 1;
			sz = (char*)ALLOC(len);
			sz[0] = 0;
			for (i = 1; i < argc; i++)
			{
				strcpy(&sz[strlen(sz)], argv[i]);
				strcpy(&sz[strlen(sz)], " ");
			}
			strcpy(&sz[strlen(sz)], " /admin");
			ShellExecuteA(NULL, "runas", argv[0], sz, NULL, SW_SHOWNORMAL);
			FREE(sz);
		}
		else if(5 == 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 | (0 == _stricmp(argv[2], "null") ? SERVICE_INTERACTIVE_PROCESS : 0), SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, argv[0], NULL, NULL, NULL, (0 == _stricmp(argv[2], "null") ? NULL : argv[2]), (0 == _stricmp(argv[3], "null") ? NULL : argv[3]));
				if (NULL != serv)
				{
					if (!StartService(serv, 0, NULL))
					{
						int le = GetLastError();
						if (_stricmp(argv[2], "null") != 0 && ERROR_SERVICE_LOGON_FAILED == le)
						{
							printf("Service is installed but there was a log on failure using: ");
							printf(argv[2]);
							printf(" ");
							printf(argv[3]);
							printf("\r\nThe service is not running...");
							printf(HELP_PART_2);
							WaitForInput();
						}
						else
							PrintLastErrorToScreen(le);
					}
					else
					{
						printf(szServiceName);
						printf(": installed and running\r\n\r\n");
						WaitForInput();
					}
					CloseServiceHandle(serv);
				}
				else
					PrintLastErrorToScreen(GetLastError());
				CloseServiceHandle(scm);
			}
			else
				PrintLastErrorToScreen(GetLastError());
		}
		else
		{
			printf(HELP_PART_1);
			printf(szServiceName);
			printf(HELP_PART_2);
			WaitForInput();
		}
	}
	else if ((action & UNINSTALL_FOUND) && !(action & HELP_FOUND))
	{
		if(2 == argc)
			ShellExecuteA(NULL, "runas", argv[0], "/uninstall /admin", NULL, SW_SHOWNORMAL);
		else if(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(GetLastError());
					else
						printf("Service uninstalled\r\n\r\n");
					CloseServiceHandle(serv);
					WaitForInput();
				}
				else
					PrintLastErrorToScreen(GetLastError());
				CloseServiceHandle(scm);
			}
			else
				PrintLastErrorToScreen(GetLastError());
		}
		else
		{
			printf(HELP_PART_1);
			printf(szServiceName);
			printf(HELP_PART_2);
			WaitForInput();
		}
	}
	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;
}

This site uses cookies. Cookies are simple text files stored on the user's computer. They are used for adding features and security to this site. Read the privacy policy.
CLOSE