C语言控制台程序编写Windows服务(一)

服务的概念:
Microsoft Windows 服务(过去称为 NT 服务)允许用户创建可在其自身的 Windows 会话中长时间运行的可执行应用程序。 这些服务可在计算机启动时自动启动,可以暂停和重启,并且不显示任何用户界面。
注:所以一个正常运行的服务程序是不应该执行完就退出的。应该在所有进程中的所有服务均终止时才返回退出。

目标:
编写一个服务,能够正常添加到Windows服务列表并能随系统启动,可正常手动启停。暂时不需要通过服务实现任何实际的功能。

组成部分:
使用C语言通过WindowsAPI编写一个可用的服务至少要包括以下几部分内容:
1. 服务程序的入口点
2. 服务程序的主体ServiceMain函数
3. 服务程序的控制处理功能函数
以上三者缺一不可,否则将无法正常启动。

具体步骤:

1.创建服务
该部分可选,如果忽略创建服务部分可使用手动方式添加服务。
2.编写入口点
第一个重要组成部分,必不可少。

		SERVICE_TABLE_ENTRY	service_table[2];				//为可以在调用过程中运行的服务指定ServiceMain函数
		service_table[0].lpServiceName = L"TestService";			//此服务在进程中运行的名称
		service_table[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain;	//指向ServiceMain函数的指针
		service_table[1].lpServiceName = NULL;					//表中最后一个条目的成员必须具有NULL值才能指定表的末尾
		service_table[1].lpServiceProc = NULL;

		result = StartServiceCtrlDispatcherW(service_table);	//将服务进程的主线程连接到服务控制管理器,该服务控制管理器使该线程成为调用过程的服务控制调度程序线程
		if (result == 0)
		{
			printf("StartServiceCtrlDispatcherW error: %d\n", GetLastError());

			break;
		}

3.编写ServiceMain函数
第二个重要组成部分,必不可少。

VOID WINAPI ServiceMain(DWORD argc, LPWSTR* argv)
{
	service_status_handle = RegisterServiceCtrlHandlerW(L"TestService", (LPHANDLER_FUNCTION)ServiceControlHandler);  //参数2为函数指针,该指针指向的函数定义了如何处理服务控制管理器发来的控制请求
        //填充SERVICE_STATUS结构体,该结构体为全局变量
	service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
	service_status.dwCurrentState = SERVICE_RUNNING;            //如果有需要初始化的内容,此处可以先填SERVICE_START_PENDING,待初始化完成后将值改为SERVICE_RUNNING
	service_status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
	service_status.dwWin32ExitCode = 0;
	service_status.dwServiceSpecificExitCode = 0;
	service_status.dwCheckPoint = 0;
	service_status.dwWaitHint = 0;

	SetServiceStatus(service_status_handle, &service_status);   //该函数用以更新服务控制管理器的的状态信息

	return;

4.编写控制处理程序功能(即前面代码参数2对应的函数)
第三个重要组成部分,必不可少。该函数用于接收服务控制管理器(SCM)发来的指令,然后根据指令让服务做出相应的状态更新。如下面代码,若不增加对停止服务指令的状态更新,则服务无法正常停止。

VOID WINAPI ServiceControlHandler(DWORD parameter)                 //该函数的参数即服务控制管理器传入的控制参数
{
	service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
	service_status.dwCurrentState = SERVICE_RUNNING;
	service_status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
	service_status.dwWin32ExitCode = 0;
	service_status.dwServiceSpecificExitCode = 0;
	service_status.dwCheckPoint = 0;
	service_status.dwWaitHint = 0;

	if (parameter == SERVICE_CONTROL_STOP)                     //根据参数内容更新对应的服务状态
	{
		service_status.dwCurrentState = SERVICE_STOPPED;
	}

	SetServiceStatus(service_status_handle, &service_status);

	return;
}

包含以上内容就完成了作为一个最基本的Windows服务。

以下为该示例完整代码:

#include <stdio.h>
#include <Windows.h>

SERVICE_STATUS           service_status;
SERVICE_STATUS_HANDLE    service_status_handle;

BOOL InstallService()
{
	BOOL		result = FALSE;
	SC_HANDLE	SCM_handle = NULL;
	SC_HANDLE	service_handle = NULL;
	DWORD		return_value = 0;
	TCHAR		Path[MAX_PATH];
	HANDLE		file_handle = NULL;
	char		error[64];
	DWORD		bytes_written = 0;
	DWORD		last_error = 0;

	SecureZeroMemory(error, sizeof(error));

	do
	{
		return_value = GetModuleFileNameW(NULL, Path, MAX_PATH);
		if (return_value == 0)
		{
			printf("GetModuleFileName error: %d\n", GetLastError());

			break;
		}

		SCM_handle = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
		if (SCM_handle == NULL)
		{
			printf("OpenSCManagerW error: %d\n", GetLastError());

			break;
		}

		service_handle = OpenServiceW(SCM_handle, L"TestService", SC_MANAGER_ALL_ACCESS);
		if (service_handle != NULL)
		{
			printf("Test Service Created\n");
		}
		else
		{
			service_handle = CreateServiceW(
				SCM_handle,
				L"TestService",
				L"TestService",
				SC_MANAGER_ALL_ACCESS,
				SERVICE_WIN32_OWN_PROCESS,
				SERVICE_AUTO_START,
				SERVICE_ERROR_IGNORE,
				Path,
				NULL,
				NULL,
				NULL,
				NULL,
				NULL
			);
			if (service_handle == NULL)
			{
				printf("CreateServiceW error: %d\n", GetLastError());

				break;
			}
		}
		result = TRUE;

	} while (0);

	if (SCM_handle != NULL)
	{
		CloseServiceHandle(SCM_handle);
	}
	if (service_handle != NULL)
	{
		CloseServiceHandle(service_handle);
	}

	return result;
}

VOID WINAPI ServiceControlHandler(DWORD parameter)
{
	service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
	service_status.dwCurrentState = SERVICE_RUNNING;
	service_status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
	service_status.dwWin32ExitCode = 0;
	service_status.dwServiceSpecificExitCode = 0;
	service_status.dwCheckPoint = 0;
	service_status.dwWaitHint = 0;

	if (parameter == SERVICE_CONTROL_STOP)
	{
		service_status.dwCurrentState = SERVICE_STOPPED;
	}

	SetServiceStatus(service_status_handle, &service_status);

	return;
}

VOID WINAPI ServiceMain(DWORD argc, LPWSTR* argv)
{
	service_status_handle = RegisterServiceCtrlHandlerW(L"TestService", (LPHANDLER_FUNCTION)ServiceControlHandler);

	service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
	service_status.dwCurrentState = SERVICE_RUNNING;
	service_status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
	service_status.dwWin32ExitCode = 0;
	service_status.dwServiceSpecificExitCode = 0;
	service_status.dwCheckPoint = 0;
	service_status.dwWaitHint = 0;

	SetServiceStatus(service_status_handle, &service_status);

	return;
}

int main(int argc, char* argv[])
{
	BOOL		result = 0;
	HANDLE		file_handle = NULL;
	DWORD		last_error = 0;

	do
	{
		result = InstallService();
		if (result == 0)
		{
			printf("InstallService faild.\n");
			break;
		}

		SERVICE_TABLE_ENTRY	service_table[2];
		service_table[0].lpServiceName = L"TestService";
		service_table[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain;
		service_table[1].lpServiceName = NULL;
		service_table[1].lpServiceProc = NULL;

		result = StartServiceCtrlDispatcherW(service_table);
		if (result == 0)
		{
			printf("StartServiceCtrlDispatcherW error: %d\n", GetLastError());

			break;
		}
	} while (0);

	return 0;
}