Click here to Skip to main content
Click here to Skip to main content
Articles » Languages » C# » General » Downloads
 
Add your own
alternative version
Go to top

Self installing .NET service using the Win32 API

, 28 Oct 2005
Sometimes the service classes provided by Visual Studio don't give you the control you need, so why not build your own? And while you're at it, why not make it self-installing? The base class provided gives you full control of the Win32 Services API from a convenient base class and attribute.
hoytsoft_servicebase_src.zip
HoytSoft.ServiceBase.csproj.user
Attributes
Base
bin
Debug
HoytSoft.Service.exe
obj
Debug
SmartClicks.Service.dll
SmartClicks.Service.exe
temp
TempPE
hoytsoft_serviceprocess_src.zip
Example
bin
Debug
HoytSoft.ServiceProcess.dll
Release
HoytSoft.Example.csproj.user
Service Base
Attributes
bin
Debug
Release
HoytSoft.ServiceProcess.csproj.user
HoytSoft.snk
using System;
using System.Runtime.InteropServices;

/*
 A large portion of this code was derived from another class I found online but can't relocate. If you're the author
 and recognize this, thank you! The code was modified from its original form to suit my needs. Only a few function
 calls, constants, delegates, and structs were added.
 */
namespace HoytSoft.ServiceProcess {
	///<summary>A collection of Win32 API functions and structs for use with Win32 services.</summary>
	public class ServicesAPI {
		public delegate void ServiceCtrlHandlerProc(int Opcode);

		[DllImport("advapi32.dll", EntryPoint="RegisterServiceCtrlHandler")]
		public static extern IntPtr RegisterServiceCtrlHandler(string lpServiceName, [MarshalAs(UnmanagedType.FunctionPtr)]ServiceCtrlHandlerProc lpHandlerProc);

		[DllImport("advapi32.dll", SetLastError = true)] 
		public static extern int SetServiceStatus(IntPtr hServiceStatus, ref SERVICE_STATUS lpServiceStatus);

		[DllImport("advapi32.dll", EntryPoint="StartServiceCtrlDispatcher", SetLastError = true)] 
		public static extern int StartServiceCtrlDispatcher(SERVICE_TABLE_ENTRY[] lpServiceStartTable);

		[DllImport("advapi32.dll")]
		public static extern IntPtr CreateService(IntPtr SC_HANDLE, string lpSvcName, string lpDisplayName, 
			int dwDesiredAccess, int dwServiceType, int dwStartType, int dwErrorControl, string lpPathName, 
			string lpLoadOrderGroup, int lpdwTagId, string lpDependencies, string lpServiceStartName, string lpPassword);

		[DllImport("advapi32.dll")]
		public static extern int LockServiceDatabase(int hSCManager);
	
		[DllImport("advapi32.dll")]
		public static extern bool UnlockServiceDatabase(int hSCManager);
	
		[DllImport("kernel32.dll")]
		public static extern void CopyMemory(IntPtr pDst, SC_ACTION[] pSrc,int ByteLen);
	
		[DllImport("advapi32.dll")]
		public static extern bool ChangeServiceConfigA(
			int hService, ServiceType dwServiceType, int dwStartType,
			int dwErrorControl, string lpBinaryPathName, string lpLoadOrderGroup,
			int lpdwTagId, string lpDependencies, string lpServiceStartName,
			string lpPassword, string lpDisplayName);
    
		[DllImport("advapi32.dll")]
		public static extern bool ChangeServiceConfig2A(
			int hService, InfoLevel dwInfoLevel, 
			[MarshalAs(UnmanagedType.Struct)] ref SERVICE_DESCRIPTION lpInfo);

		[DllImport("advapi32.dll")]
		public static extern bool ChangeServiceConfig2A(
			int hService, InfoLevel dwInfoLevel, 
			[MarshalAs(UnmanagedType.Struct)] ref SERVICE_FAILURE_ACTIONS lpInfo);

		[DllImport("advapi32.dll")]
		public static extern int OpenServiceA(
			int hSCManager, string lpServiceName, ACCESS_TYPE dwDesiredAccess);

		[DllImport( "advapi32.dll", EntryPoint = "OpenService" )]
		public static extern IntPtr OpenService(IntPtr hSCManager, string serviceName, ACCESS_TYPE desiredAccess);


		[DllImport("advapi32.dll")]
		public static extern IntPtr OpenSCManagerA(string lpMachineName, string lpDatabaseName, ServiceControlManagerType dwDesiredAccess);

		[DllImport("advapi32.dll")]
		public static extern IntPtr OpenSCManagerA(string lpMachineName,string lpSCDB, int scParameter);

		[DllImport("advapi32.dll")]
		public static extern bool CloseServiceHandle(
			int hSCObject);

		[DllImport("advapi32.dll")]
		public static extern bool CloseServiceHandle(
			IntPtr hSCObject);

		[DllImport("advapi32.dll")]
		public static extern bool QueryServiceConfigA(
			int hService, [MarshalAs(UnmanagedType.Struct)] ref QUERY_SERVICE_CONFIG lpServiceConfig, int cbBufSize,
			int pcbBytesNeeded);

		public const int STANDARD_RIGHTS_REQUIRED = 0xF0000;
		public const int GENERIC_READ = -2147483648;
		public const int ERROR_INSUFFICIENT_BUFFER = 122;
		public const int SERVICE_NO_CHANGE = -1;
		public const int GENERIC_WRITE = 0x40000000;
		public const int DELETE = 0x10000;
		public const int SERVICE_ACCEPT_STOP = 1;
		public const int SERVICE_ACCEPT_PAUSE_CONTINUE = 2;
		public const int SERVICE_ACCEPT_SHUTDOWN = 4;

		public const int SERVICE_STOPPED = 1;
		public const int SERVICE_START_PENDING = 2;
		public const int SERVICE_STOP_PENDING = 3;
		public const int SERVICE_RUNNING = 4;
		public const int SERVICE_CONTINUE_PENDING = 5;
		public const int SERVICE_PAUSE_PENDING = 6;
		public const int SERVICE_PAUSED = 7;
		public const int SERVICE_INTERROGATE = 0x80;

		public const int SERVICE_CONTROL_STOP = 1;
		public const int SERVICE_CONTROL_PAUSE = 2;
		public const int SERVICE_CONTROL_CONTINUE = 3;
		public const int SERVICE_CONTROL_INTERROGATE = 4;
		public const int SERVICE_CONTROL_SHUTDOWN = 5;

		

		public const int SERVICE_WIN32 = (int)(ServiceType.SERVICE_WIN32_OWN_PROCESS | ServiceType.SERVICE_WIN32_SHARE_PROCESS);

		public const int ERROR_FAILED_SERVICE_CONTROLLER_CONNECT = 1063;
		public const int ERROR_INVALID_DATA = 13;
		public const int ERROR_SERVICE_ALREADY_RUNNING = 1057;
		//public const int SERVICE_NO_CHANGE = 0xFFFF;

		public enum ServiceType {
			SERVICE_KERNEL_DRIVER = 0x1,
			SERVICE_FILE_SYSTEM_DRIVER = 0x2,
			SERVICE_WIN32_OWN_PROCESS = 0x10,
			SERVICE_WIN32_SHARE_PROCESS = 0x20,
			SERVICE_INTERACTIVE_PROCESS = 0x100,
			SERVICETYPE_NO_CHANGE = SERVICE_NO_CHANGE
		}

		public enum ServiceStartType:int {
			SERVICE_BOOT_START = 0x0,
			SERVICE_SYSTEM_START = 0x1,
			SERVICE_AUTO_START = 0x2,
			SERVICE_DEMAND_START = 0x3,
			SERVICE_DISABLED = 0x4,
			SERVICESTARTTYPE_NO_CHANGE = SERVICE_NO_CHANGE
		}

		public enum ServiceErrorControl:int {
			SERVICE_ERROR_IGNORE = 0x0,
			SERVICE_ERROR_NORMAL = 0x1,
			SERVICE_ERROR_SEVERE = 0x2,
			SERVICE_ERROR_CRITICAL = 0x3,
			msidbServiceInstallErrorControlVital = 0x8000,
			SERVICEERRORCONTROL_NO_CHANGE = SERVICE_NO_CHANGE
		}

		public enum ServiceStateRequest:int {
			SERVICE_ACTIVE = 0x1,
			SERVICE_INACTIVE = 0x2,
			SERVICE_STATE_ALL = (SERVICE_ACTIVE | SERVICE_INACTIVE)
		}

		public enum ServiceControlType:int {
			SERVICE_CONTROL_STOP = 0x1,
			SERVICE_CONTROL_PAUSE = 0x2,
			SERVICE_CONTROL_CONTINUE = 0x3,
			SERVICE_CONTROL_INTERROGATE = 0x4,
			SERVICE_CONTROL_SHUTDOWN = 0x5,
			SERVICE_CONTROL_PARAMCHANGE = 0x6,
			SERVICE_CONTROL_NETBINDADD = 0x7,
			SERVICE_CONTROL_NETBINDREMOVE = 0x8,
			SERVICE_CONTROL_NETBINDENABLE = 0x9,
			SERVICE_CONTROL_NETBINDDISABLE = 0xA,
			SERVICE_CONTROL_DEVICEEVENT = 0xB,
			SERVICE_CONTROL_HARDWAREPROFILECHANGE = 0xC,
			SERVICE_CONTROL_POWEREVENT = 0xD,
			SERVICE_CONTROL_SESSIONCHANGE = 0xE,
		}

		public enum ServiceState:int {
			SERVICE_STOPPED = 0x1,
			SERVICE_START_PENDING = 0x2,
			SERVICE_STOP_PENDING = 0x3,
			SERVICE_RUNNING = 0x4,
			SERVICE_CONTINUE_PENDING = 0x5,
			SERVICE_PAUSE_PENDING = 0x6,
			SERVICE_PAUSED = 0x7,
		}

		public enum ServiceControlAccepted:int {
			SERVICE_ACCEPT_STOP = 0x1,
			SERVICE_ACCEPT_PAUSE_CONTINUE = 0x2,
			SERVICE_ACCEPT_SHUTDOWN = 0x4,
			SERVICE_ACCEPT_PARAMCHANGE = 0x8,
			SERVICE_ACCEPT_NETBINDCHANGE = 0x10,
			SERVICE_ACCEPT_HARDWAREPROFILECHANGE = 0x20,
			SERVICE_ACCEPT_POWEREVENT = 0x40,
			SERVICE_ACCEPT_SESSIONCHANGE = 0x80
		}

		public enum ServiceControlManagerType:int {
			SC_MANAGER_CONNECT = 0x1,
			SC_MANAGER_CREATE_SERVICE = 0x2,
			SC_MANAGER_ENUMERATE_SERVICE = 0x4,
			SC_MANAGER_LOCK = 0x8,
			SC_MANAGER_QUERY_LOCK_STATUS = 0x10,
			SC_MANAGER_MODIFY_BOOT_CONFIG = 0x20,
			SC_MANAGER_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE | SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_LOCK | SC_MANAGER_QUERY_LOCK_STATUS | SC_MANAGER_MODIFY_BOOT_CONFIG
		}

		public enum ACCESS_TYPE:int {
			SERVICE_QUERY_CONFIG = 0x1,
			SERVICE_CHANGE_CONFIG = 0x2,
			SERVICE_QUERY_STATUS = 0x4,
			SERVICE_ENUMERATE_DEPENDENTS = 0x8,
			SERVICE_START = 0x10,
			SERVICE_STOP = 0x20,
			SERVICE_PAUSE_CONTINUE = 0x40,
			SERVICE_INTERROGATE = 0x80,
			SERVICE_USER_DEFINED_CONTROL = 0x100,
			SERVICE_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG | SERVICE_QUERY_STATUS | SERVICE_ENUMERATE_DEPENDENTS | SERVICE_START | SERVICE_STOP | SERVICE_PAUSE_CONTINUE | SERVICE_INTERROGATE | SERVICE_USER_DEFINED_CONTROL
		}

		[StructLayout(LayoutKind.Sequential)]
		public struct SERVICE_STATUS {
			public int dwServiceType;
			public int dwCurrentState;
			public int dwControlsAccepted;
			public int dwWin32ExitCode;
			public int dwServiceSpecificExitCode;
			public int dwCheckPoint;
			public int dwWaitHint;
		}
	
		[StructLayout(LayoutKind.Sequential)]
		public struct QUERY_SERVICE_CONFIG {
			public int dwServiceType;
			public int dwStartType;
			public int dwErrorControl;
			public string lpBinaryPathName;
			public string lpLoadOrderGroup;
			public int dwTagId;
			public string lpDependencies;
			public string lpServiceStartName;
			public string lpDisplayName;
		}

		public enum SC_ACTION_TYPE:int {
			SC_ACTION_NONE = 0,
			SC_ACTION_RESTART = 1,
			SC_ACTION_REBOOT = 2,
			SC_ACTION_RUN_COMMAND = 3,
		}

		public delegate void ServiceMainProc(int argc, [MarshalAs(UnmanagedType.LPArray)]string[] argv);
		public struct SERVICE_TABLE_ENTRY {
			public string lpServiceName;
			[MarshalAs(UnmanagedType.FunctionPtr)]
			public ServiceMainProc lpServiceProc;

			/*public SERVICE_TABLE_ENTRY(string Name, ServiceMainProc ServiceMainMethod) {
				this.lpServiceName = Name;
				this.lpServiceProc = ServiceMainMethod;
				this.lpServiceNameNull = null;
				this.lpServiceProcNull = null;
			}/**/
		}

		[StructLayout(LayoutKind.Sequential)]
		public struct SC_ACTION {
			public SC_ACTION_TYPE SCActionType;
			public int Delay;
		}

		public enum InfoLevel:int {
			SERVICE_CONFIG_DESCRIPTION = 1,
			SERVICE_CONFIG_FAILURE_ACTIONS = 2
		}

		[StructLayout(LayoutKind.Sequential)]
		public struct SERVICE_DESCRIPTION {
			public string lpDescription;
		}

		[StructLayout(LayoutKind.Sequential)]
		public struct SERVICE_FAILURE_ACTIONS {
			public int dwResetPeriod;
			public string lpRebootMsg;
			public string lpCommand;
			public int cActions;
			public int lpsaActions;
		}
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

DavidHoyt
Software Developer (Senior) Lawrence Livermore National Laboratories
United States United States
I'm a recent graduate of Brigham Young University in Provo, UT and now working for Lawrence Livermore National Laboratories (LLNL). I've been programming since I was 14 and did the amazing Hoyt family website with an animated gif of a spinning globe. I've come a long way since then and now actually use pictures of people.
 
I've been interested in website development and Windows programming since and I haven't stopped except for two years spent in El Salvador as a religious representative for my church.
 
I've done lots of work with C#/C/C++/Java/Python/JavaScript/Scheme/T-SQL/PL-SQL/Visual Basic/etc., web services, windows apps, services, and web apps. It's been a lot of fun!

| Advertise | Privacy | Mobile
Web03 | 2.8.140916.1 | Last Updated 28 Oct 2005
Article Copyright 2005 by DavidHoyt
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid