用.net开发服务很简单,此处讲解使用VC++开发Windows服务
1. 服务的组成
一个服务由三部分组成:
第一部分Service Control Manager(SCM),每个Windows系统都有一个SCM,SCM存在于Service.exe中,在Windows启动的时候会自动运行,伴随着操作系统的启动和关闭而产生和终止;
第二部分为服务本身,一个服务拥有能从SCM收到信号和命令所必须的特殊代码,并且能在处理后将它的状态回传回SCM;
第三部分是Service Control Dispatcher(SCP),是一个拥有用户界面,允许用户开始、停止、暂停、继续,并且控制一个或多个安装在计算机上服务的Win32应用程序。
2. 三个重要函数
2.1 服务程序入口函数
头文件:Winsvc.h
库:Advapi32.lib
DLL:Advapi32.dll
BOOL WINAPI StartServiceCtrlDispatcher(
_In_ const SERVICE_TABLE_ENTRY *lpServiceTable
);
2.2 服务入口回调函数
头文件:Winsvc.h
VOID WINAPI ServiceMain(
_In_ DWORD dwArgc,
_In_ LPTSTR *lpszArgv
);
2.3 服务控制回调函数
VOID WINAPI CtrlHandler(DWORD fdwControl) //控制命令
下面是一个摘录于<计算机病毒揭秘与对抗>>作者:王倍昌一书中的例子,可以编译到控制台下直接使用,可以看做是编写服务的一个框架。
服务的功能是监控系统中的可移动磁盘,然后向其中写入文件a.txt。
编译后执行 FirstService.exe install 安装服务,然后在服务窗口中启动该服务。
卸载服务 FirstService.exe uninstall
// FirstService.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <Windows.h>
#include <tchar.h>
#define SERVICE_NAME _T("FirstService")
SERVICE_STATUS g_status;
SERVICE_STATUS_HANDLE g_hServiceStatus;
HANDLE g_hEvent = NULL;
void Init()
{
g_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
g_status.dwCurrentState = SERVICE_STOPPED;
// 设置服务可以使用的控制
// 如果希望服务启动后不能停止,去掉SERVICE_ACCEPT_STOP
// SERVICE_ACCEPT_PAUSE_CONTINUE是服务可以“暂停/继续”
g_status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN;
g_status.dwWin32ExitCode = 0;
g_status.dwServiceSpecificExitCode = 0;
g_status.dwCheckPoint = 0;
g_status.dwWaitHint = 0;
//创建初始为有信号的手动内核事件。
g_hEvent = CreateEvent(NULL, TRUE, TRUE, "Pause");
}
void SetStatus(long lCurrentStatus)
{
g_status.dwCurrentState = lCurrentStatus;
SetServiceStatus(g_hServiceStatus, &g_status);
}
void WINAPI Handler(DWORD dwOpcode)
{
switch (dwOpcode)
{
case SERVICE_CONTROL_STOP:
{ //收到停止服务命令停止服务
SetStatus(SERVICE_STOP_PENDING);
SetStatus(SERVICE_STOPPED);
}
break;
case SERVICE_CONTROL_PAUSE:
{
SetStatus(SERVICE_PAUSE_PENDING);
ResetEvent(g_hEvent); //通知RUN函数开始等待
SetStatus(SERVICE_PAUSED);
}
break;
case SERVICE_CONTROL_CONTINUE:
{
SetStatus(SERVICE_CONTINUE_PENDING);
SetEvent(g_hEvent);//通知RUN函数继续执行
SetStatus(SERVICE_RUNNING);
}
break;
case SERVICE_CONTROL_INTERROGATE:
break;
case SERVICE_CONTROL_SHUTDOWN:
{ //关机时停止服务
SetStatus(SERVICE_STOP_PENDING);
SetStatus(SERVICE_STOPPED);
}
break;
default:
break;
}
}
void Run()
{
while (1) //循环扫描系统磁盘
{
TCHAR tcBuffer[4] = {0}; //准备较小内存,先试探出实际需要的内存大小。
DWORD dwNeededSize = GetLogicalDriveStrings(4, tcBuffer); //第一次执行,获得实际需要内存。
if (dwNeededSize > 4)
{ //返回值大于指定内存大小,需要重新准备足够内存再次执行。
TCHAR *pBuffer = new TCHAR[dwNeededSize]; //准备足够大的内存
dwNeededSize = GetLogicalDriveStrings(dwNeededSize, pBuffer); //获得逻辑驱动器字符串
TCHAR *pDrive = pBuffer;
int iPreLength = 0;
while (1)
{
pDrive = pBuffer+iPreLength; //获得下一个驱动器
if (pDrive[0] == 0) //获取驱动器结束
{
break; //退出循环
}
DWORD dwDriveType = GetDriveType(pDrive); //获取驱动器类型
if (dwDriveType == DRIVE_REMOVABLE)
{ //如果是移动驱动器
TCHAR tcFile[MAX_PATH] = {0};
_stprintf(tcFile, _T("%sa.txt"), pDrive);
//打开已存在的a.txt文件
HANDLE hFile = CreateFile(tcFile, GENERIC_WRITE|GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
//打开失败则创建一个。
hFile = CreateFile(tcFile, GENERIC_WRITE|GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD dwWrite = 0;
WriteFile(hFile, _T("Hello"), 5, &dwWrite, NULL);
}
CloseHandle(hFile);
}
iPreLength += _tcslen(pDrive); //跳过当前驱动器
iPreLength += 1;//sizeof(TCHAR); //跳过'\0'。 应该是字符数,加1跳过\0才对
}
if (pBuffer != NULL)
{
delete []pBuffer; //释放内存
pBuffer = NULL;
}
}
Sleep(500); //暂停500毫秒后继续扫描
//如何g_hEvent无信号则暂停执行
WaitForSingleObject(g_hEvent, INFINITE);
}
}
void WINAPI ServiceMain(DWORD dwArgc, LPTSTR* lpszArgv)
{
// 注册控制请求句柄
g_hServiceStatus = RegisterServiceCtrlHandler(SERVICE_NAME, Handler);
if (g_hServiceStatus == NULL) return;
SetStatus(SERVICE_START_PENDING);
SetStatus(SERVICE_RUNNING);
// 当 Run 函数返回时,服务已经结束。
Run();
g_status.dwWin32ExitCode = S_OK;
g_status.dwCheckPoint = 0;
g_status.dwWaitHint = 0;
g_status.dwCurrentState = SERVICE_STOPPED;
//设置服务状态为停止,从而退出服务.
SetServiceStatus(g_hServiceStatus, &g_status);
}
BOOL IsInstalled()
{
BOOL bResult = FALSE;
//打开服务控制管理器
SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hSCM != NULL)
{
//打开服务
SC_HANDLE hService = OpenService(hSCM, SERVICE_NAME, SERVICE_QUERY_CONFIG);
if (hService != NULL)
{
bResult = TRUE;
CloseServiceHandle(hService);
}
CloseServiceHandle(hSCM);
}
return bResult;
}
BOOL Install()
{
if (IsInstalled()) //服务已安装则直接返回真
return TRUE;
//打开服务控制管理器
SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hSCM == NULL)
{
return FALSE;
}
TCHAR szFilePath[MAX_PATH];
DWORD dwLen = GetModuleFileName(NULL, szFilePath, MAX_PATH);
//判断程序路径是否包含空格,如果包含则给路径加上引号.
if (_tcschr(szFilePath, ' ') != NULL)
{
dwLen += 3;
TCHAR *lpFilePath = new TCHAR[dwLen];
if (lpFilePath != NULL)
{
_stprintf(lpFilePath, _T("\"%s\""), szFilePath);
_tcscpy(szFilePath, lpFilePath);
delete []lpFilePath;
}
}
//创建一个手动启动的服务
SC_HANDLE hService = CreateService(
hSCM, SERVICE_NAME, SERVICE_NAME,
SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
szFilePath, NULL, NULL, _T(""), NULL, NULL);
if (hService == NULL)
{
CloseServiceHandle(hSCM);
return FALSE;
}
CloseServiceHandle(hService);
CloseServiceHandle(hSCM);
return TRUE;
}
BOOL Uninstall()
{
if (!IsInstalled()) //如果服务已卸载直接返回真
return TRUE;
SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hSCM == NULL)
{
return FALSE;
}
SC_HANDLE hService = OpenService(hSCM, SERVICE_NAME, SERVICE_STOP | DELETE);
if (hService == NULL)
{
CloseServiceHandle(hSCM);
return FALSE;
}
SERVICE_STATUS status;
//首先停止服务,确保服务能够立即被删除.
ControlService(hService, SERVICE_CONTROL_STOP, &status);
//删除服务
BOOL bDelete = DeleteService(hService);
CloseServiceHandle(hService);
CloseServiceHandle(hSCM);
if (bDelete)
return TRUE;
return FALSE;
}
int main(int argc, char* argv[])
{
Init(); //初始化服务数据信息
//判断参数决定如何执行代码
if (argv[1] != NULL && _tcsicmp(argv[1], _T("install")) == 0)
{
Install();
}
else if (argv[1] != NULL && _tcsicmp(argv[1], _T("uninstall")) == 0)
{
Uninstall();
}
else
{ //如果没有参数则是由SCM启动的服务程序
SERVICE_TABLE_ENTRY st[] =
{
{ SERVICE_NAME, ServiceMain },
{ NULL, NULL }
};
StartServiceCtrlDispatcher(st);
}
CloseHandle(g_hEvent);
return 0;
}
1. 服务的组成
一个服务由三部分组成:
第一部分Service Control Manager(SCM),每个Windows系统都有一个SCM,SCM存在于Service.exe中,在Windows启动的时候会自动运行,伴随着操作系统的启动和关闭而产生和终止;
第二部分为服务本身,一个服务拥有能从SCM收到信号和命令所必须的特殊代码,并且能在处理后将它的状态回传回SCM;
第三部分是Service Control Dispatcher(SCP),是一个拥有用户界面,允许用户开始、停止、暂停、继续,并且控制一个或多个安装在计算机上服务的Win32应用程序。
2. 三个重要函数
2.1 服务程序入口函数
头文件:Winsvc.h
库:Advapi32.lib
DLL:Advapi32.dll
BOOL WINAPI StartServiceCtrlDispatcher(
_In_ const SERVICE_TABLE_ENTRY *lpServiceTable
);
2.2 服务入口回调函数
头文件:Winsvc.h
VOID WINAPI ServiceMain(
_In_ DWORD dwArgc,
_In_ LPTSTR *lpszArgv
);
2.3 服务控制回调函数
VOID WINAPI CtrlHandler(DWORD fdwControl) //控制命令
下面是一个摘录于<计算机病毒揭秘与对抗>>作者:王倍昌一书中的例子,可以编译到控制台下直接使用,可以看做是编写服务的一个框架。
服务的功能是监控系统中的可移动磁盘,然后向其中写入文件a.txt。
编译后执行 FirstService.exe install 安装服务,然后在服务窗口中启动该服务。
卸载服务 FirstService.exe uninstall
// FirstService.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <Windows.h>
#include <tchar.h>
#define SERVICE_NAME _T("FirstService")
SERVICE_STATUS g_status;
SERVICE_STATUS_HANDLE g_hServiceStatus;
HANDLE g_hEvent = NULL;
void Init()
{
g_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
g_status.dwCurrentState = SERVICE_STOPPED;
// 设置服务可以使用的控制
// 如果希望服务启动后不能停止,去掉SERVICE_ACCEPT_STOP
// SERVICE_ACCEPT_PAUSE_CONTINUE是服务可以“暂停/继续”
g_status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN;
g_status.dwWin32ExitCode = 0;
g_status.dwServiceSpecificExitCode = 0;
g_status.dwCheckPoint = 0;
g_status.dwWaitHint = 0;
//创建初始为有信号的手动内核事件。
g_hEvent = CreateEvent(NULL, TRUE, TRUE, "Pause");
}
void SetStatus(long lCurrentStatus)
{
g_status.dwCurrentState = lCurrentStatus;
SetServiceStatus(g_hServiceStatus, &g_status);
}
void WINAPI Handler(DWORD dwOpcode)
{
switch (dwOpcode)
{
case SERVICE_CONTROL_STOP:
{ //收到停止服务命令停止服务
SetStatus(SERVICE_STOP_PENDING);
SetStatus(SERVICE_STOPPED);
}
break;
case SERVICE_CONTROL_PAUSE:
{
SetStatus(SERVICE_PAUSE_PENDING);
ResetEvent(g_hEvent); //通知RUN函数开始等待
SetStatus(SERVICE_PAUSED);
}
break;
case SERVICE_CONTROL_CONTINUE:
{
SetStatus(SERVICE_CONTINUE_PENDING);
SetEvent(g_hEvent);//通知RUN函数继续执行
SetStatus(SERVICE_RUNNING);
}
break;
case SERVICE_CONTROL_INTERROGATE:
break;
case SERVICE_CONTROL_SHUTDOWN:
{ //关机时停止服务
SetStatus(SERVICE_STOP_PENDING);
SetStatus(SERVICE_STOPPED);
}
break;
default:
break;
}
}
void Run()
{
while (1) //循环扫描系统磁盘
{
TCHAR tcBuffer[4] = {0}; //准备较小内存,先试探出实际需要的内存大小。
DWORD dwNeededSize = GetLogicalDriveStrings(4, tcBuffer); //第一次执行,获得实际需要内存。
if (dwNeededSize > 4)
{ //返回值大于指定内存大小,需要重新准备足够内存再次执行。
TCHAR *pBuffer = new TCHAR[dwNeededSize]; //准备足够大的内存
dwNeededSize = GetLogicalDriveStrings(dwNeededSize, pBuffer); //获得逻辑驱动器字符串
TCHAR *pDrive = pBuffer;
int iPreLength = 0;
while (1)
{
pDrive = pBuffer+iPreLength; //获得下一个驱动器
if (pDrive[0] == 0) //获取驱动器结束
{
break; //退出循环
}
DWORD dwDriveType = GetDriveType(pDrive); //获取驱动器类型
if (dwDriveType == DRIVE_REMOVABLE)
{ //如果是移动驱动器
TCHAR tcFile[MAX_PATH] = {0};
_stprintf(tcFile, _T("%sa.txt"), pDrive);
//打开已存在的a.txt文件
HANDLE hFile = CreateFile(tcFile, GENERIC_WRITE|GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
//打开失败则创建一个。
hFile = CreateFile(tcFile, GENERIC_WRITE|GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD dwWrite = 0;
WriteFile(hFile, _T("Hello"), 5, &dwWrite, NULL);
}
CloseHandle(hFile);
}
iPreLength += _tcslen(pDrive); //跳过当前驱动器
iPreLength += 1;//sizeof(TCHAR); //跳过'\0'。 应该是字符数,加1跳过\0才对
}
if (pBuffer != NULL)
{
delete []pBuffer; //释放内存
pBuffer = NULL;
}
}
Sleep(500); //暂停500毫秒后继续扫描
//如何g_hEvent无信号则暂停执行
WaitForSingleObject(g_hEvent, INFINITE);
}
}
void WINAPI ServiceMain(DWORD dwArgc, LPTSTR* lpszArgv)
{
// 注册控制请求句柄
g_hServiceStatus = RegisterServiceCtrlHandler(SERVICE_NAME, Handler);
if (g_hServiceStatus == NULL) return;
SetStatus(SERVICE_START_PENDING);
SetStatus(SERVICE_RUNNING);
// 当 Run 函数返回时,服务已经结束。
Run();
g_status.dwWin32ExitCode = S_OK;
g_status.dwCheckPoint = 0;
g_status.dwWaitHint = 0;
g_status.dwCurrentState = SERVICE_STOPPED;
//设置服务状态为停止,从而退出服务.
SetServiceStatus(g_hServiceStatus, &g_status);
}
BOOL IsInstalled()
{
BOOL bResult = FALSE;
//打开服务控制管理器
SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hSCM != NULL)
{
//打开服务
SC_HANDLE hService = OpenService(hSCM, SERVICE_NAME, SERVICE_QUERY_CONFIG);
if (hService != NULL)
{
bResult = TRUE;
CloseServiceHandle(hService);
}
CloseServiceHandle(hSCM);
}
return bResult;
}
BOOL Install()
{
if (IsInstalled()) //服务已安装则直接返回真
return TRUE;
//打开服务控制管理器
SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hSCM == NULL)
{
return FALSE;
}
TCHAR szFilePath[MAX_PATH];
DWORD dwLen = GetModuleFileName(NULL, szFilePath, MAX_PATH);
//判断程序路径是否包含空格,如果包含则给路径加上引号.
if (_tcschr(szFilePath, ' ') != NULL)
{
dwLen += 3;
TCHAR *lpFilePath = new TCHAR[dwLen];
if (lpFilePath != NULL)
{
_stprintf(lpFilePath, _T("\"%s\""), szFilePath);
_tcscpy(szFilePath, lpFilePath);
delete []lpFilePath;
}
}
//创建一个手动启动的服务
SC_HANDLE hService = CreateService(
hSCM, SERVICE_NAME, SERVICE_NAME,
SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
szFilePath, NULL, NULL, _T(""), NULL, NULL);
if (hService == NULL)
{
CloseServiceHandle(hSCM);
return FALSE;
}
CloseServiceHandle(hService);
CloseServiceHandle(hSCM);
return TRUE;
}
BOOL Uninstall()
{
if (!IsInstalled()) //如果服务已卸载直接返回真
return TRUE;
SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hSCM == NULL)
{
return FALSE;
}
SC_HANDLE hService = OpenService(hSCM, SERVICE_NAME, SERVICE_STOP | DELETE);
if (hService == NULL)
{
CloseServiceHandle(hSCM);
return FALSE;
}
SERVICE_STATUS status;
//首先停止服务,确保服务能够立即被删除.
ControlService(hService, SERVICE_CONTROL_STOP, &status);
//删除服务
BOOL bDelete = DeleteService(hService);
CloseServiceHandle(hService);
CloseServiceHandle(hSCM);
if (bDelete)
return TRUE;
return FALSE;
}
int main(int argc, char* argv[])
{
Init(); //初始化服务数据信息
//判断参数决定如何执行代码
if (argv[1] != NULL && _tcsicmp(argv[1], _T("install")) == 0)
{
Install();
}
else if (argv[1] != NULL && _tcsicmp(argv[1], _T("uninstall")) == 0)
{
Uninstall();
}
else
{ //如果没有参数则是由SCM启动的服务程序
SERVICE_TABLE_ENTRY st[] =
{
{ SERVICE_NAME, ServiceMain },
{ NULL, NULL }
};
StartServiceCtrlDispatcher(st);
}
CloseHandle(g_hEvent);
return 0;
}