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

PROWAREtech

Hello there! How can I help you today?
Ask any question

PROWAREtech

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.
ACCEPT REJECT