这一篇我们来介绍如何编程控制Windows服务,主要介绍两方面的内容,一是枚举系统所有服务,二是编程对服务进行控制,下面我们分别进行讲解。
枚举服务
我们主要用到两个函数:EnumServicesStatus和QueryServiceConfig,其中函数EnumServicesStatus可以获取系统中所有服务的基本信息,包括服务名称、显示名称、当前状态,如果想获取更加详细的信息,就需要QueryServiceConfig函数的帮助了,它需要传递一个SC_HANDLE句柄,这个句柄可以用OpenService获得,而OpenService需要的服务名称参数可以通过前面的EnumServicesStatus函数获得,他们配合使用就可以获取非常详尽的信息;相关结构体如下所示,它已经在Windows.h头文件中声明过了,我们可以直接使用:
typedef struct _QUERY_SERVICE_CONFIG {
DWORD dwServiceType;
DWORD dwStartType;
DWORD dwErrorControl;
LPTSTR lpBinaryPathName;
LPTSTR lpLoadOrderGroup;
DWORD dwTagId;
LPTSTR lpDependencies;
LPTSTR lpServiceStartName;
LPTSTR lpDisplayName;
} QUERY_SERVICE_CONFIG, *LPQUERY_SERVICE_CONFIG;
细心的读者可能会看出来,虽然上面的结构已经非常详尽了,但是它好像还是缺少了点什么?对,是服务描述,当我们在Windows的服务管理器中选中某个服务后,就会显示出该服务的相关描述,更好地帮助用户。
那么我们该如何获取服务描述呢?答案是使用QueryServiceConfig2函数,它需要用到一个SERVICE_DESCRIPTION结构(同样不需要我们自己定义),与QueryServiceConfig函数一样,它同样需要一个标明服务的SC_HANDLE句柄作为参数,如下代码显示了获取系统所有服务详细信息的方法:
LPENUM_SERVICE_STATUS st; // 基本信息
LPQUERY_SERVICE_CONFIG sc; // 详细信息
LPSERVICE_DESCRIPTION sd; // 服务描述
DWORD ret = 0;
DWORD size= 0;
char szState[30]; // 状态
char szServiceName[MAX_PATH]; // 名称
char szStartType[30]; // 启动类型
char szApplication[MAX_PATH]; // 应用程序
char szDescription[MAX_PATH*4]; // 服务描述
// 为st分配足够空间
st = (LPENUM_SERVICE_STATUS)LocalAlloc(LPTR,64 * 1024);
HANDLE hSCManager = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
EnumServicesStatus(hSCManager,SERVICE_WIN32,SERVICE_STATE_ALL,st,1024 * 64,&size,&ret,NULL);
for(DWORD i = 0; i lpBinaryPathName);
if(sc->dwStartType == SERVICE_AUTO_START)
{
sprintf(szStartType,"自动");
}
else if(sc->dwStartType == SERVICE_DEMAND_START)
{
sprintf(szStartType,"手动");
}
else if(sc->dwStartType == SERVICE_DISABLED)
{
sprintf(szStartType,"已禁用");
}
else
{
sprintf(szStartType," ");
}
// 查询服务描述信息
char temp[1024];
QueryServiceConfig2(hService,SERVICE_CONFIG_DESCRIPTION,(LPBYTE)temp,sizeof(temp),&nRet);
sd = (LPSERVICE_DESCRIPTION)temp;
sprintf(szDescription,"%s",sd->lpDescription);
// 输出到列表框
m_lstService.InsertItem(i, szServiceName, NULL);
m_lstService.SetItemText(i,1, szState);
m_lstService.SetItemText(i,2, szStartType);
m_lstService.SetItemText(i,3, szApplication);
m_lstService.SetItemText(i,4, szDescription);
}
::CloseServiceHandle(hService);
}
::CloseServiceHandle(hSCManager);
上述代码有一个需要注意的地方,在调用EnumServicesStatus时需要分配足够大的空间,本程序是64*1024,而在调用QueryServiceConfig同样要分配足够的空间,但注意,这里不能过大,我起初也是给它分配64*1024,但这里的调用总是失败,百思不得其解,后来偶然才给解决;其实仔细想想也对,前面是需要枚举所有的服务信息,分配的空间当然要大,但后面是单个服务的相信描述,显然用不了那么大的空间。
服务控制
服务控制的函数较多,MSDN里面有很专门的Service Functions,非常详细,因此这里就不再赘述各函数的用法了,至少每个函数的作用,你看下名字就大概知道了,还不知道的就去百度一下。
我们主要用到下面这些函数:OpenSCManager、OpenService、CreateService、StartService、DeleteService、ControlService、ChangeServiceConfig等函数。其中需要注意的几点是:(1)StartService可以用来启动一个服务,但并没有相应的暂停、停止服务函数,除了启动之外的操作都需要调用ControlService函数,通过向其发送一个控制码来使系统改变服务状态;(2)在修改服务配置时,需要首先调用LockServiceDatabase锁定数据库,以免多个线程同时操作而引起不可预料的后果,当然在修改完毕后别忘了调用UnlockServiceDatabase进行解锁。下面是我写的一个服务管理函数:
//
// 服务控制函数
//
// 参数flag1控制服务状态,0-忽略,1-启动,2-停止
//
// 参数flag2控制启动类型,0-忽略,1-自动,2-手动,3-禁用
//
// 运行成功返回TRUE,否则返回FALSE
//
BOOL CDlgService::ControlService(const char *szServiceName,int flag1, int flag2)
{
HANDLE hSCManager = ::OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
if(!hSCManager)
{
MessageBox("OpenSCManager Error!");
return FALSE;
}
SC_HANDLE hService = ::OpenService(hSCManager,szServiceName,SERVICE_ALL_ACCESS);
if(!hService)
{
MessageBox("OpenService Error!");
return FALSE;
}
// 改变服务状态
if(flag1 != 0)
{
// 查询服务状态
SERVICE_STATUS status;
::QueryServiceStatus(hService,&status);
if((flag1 == 1) && (status.dwCurrentState != SERVICE_RUNNING)) // 启动服务
{
if(!(::StartService(hService,NULL,NULL)))
return FALSE;
}
if((flag1 == 2) && (status.dwCurrentState != SERVICE_STOPPED)) // 停止服务
{
if(!(::ControlService(hService,SERVICE_CONTROL_STOP,&status)))
return FALSE;
}
}
// 配置服务启动方式
if(flag2 != 0)
{
// 锁定
SC_LOCK sclLock;
sclLock = LockServiceDatabase(hSCManager);
if (sclLock == NULL)
{
MessageBox("LockServiceDatabase Error!");
return FALSE;
}
DWORD dwStartType;
switch(flag2)
{
case 1:
dwStartType = SERVICE_AUTO_START;
break;
case 2:
dwStartType = SERVICE_DEMAND_START;
break;
case 3:
dwStartType = SERVICE_DISABLED;
break;
default:
return FALSE;
}
if(!ChangeServiceConfig(hService,SERVICE_NO_CHANGE,dwStartType,SERVICE_NO_CHANGE,NULL,NULL,NULL,NULL,NULL,NULL,NULL))
{
UnlockServiceDatabase(sclLock);
return FALSE;
}
// 解锁
UnlockServiceDatabase(sclLock);
}
::CloseServiceHandle(hService);
::CloseServiceHandle(hSCManager);
return TRUE;
}