Windows Service服务程序的原理及实现(1)实现对服务的控制和管理

来源:互联网 发布:mac安装axure出错 编辑:程序博客网 时间:2024/06/01 09:45

摘要:上一篇文章《Windows Service服务程序(0)服务主函数 & 控制处理函数》对Windows服务开发做了初步讲解。服务编写完成后,还需要服务管理程序才可以运行。一种方式是上篇文章中介绍的使用系统服务管理工具,如sc.exe等;另一种方式是使用Windows服务管理API自行编写服务管理程序。


目录:

  • 创建服务
  • 删除服务
  • 启动服务
  • 向服务发送控制请求
  • 停止服务运行
  • 服务控制与管理测试

本文介绍如何通过系统提供的API函数,对上一篇文章中开发的服务程序进行控制。

创建服务

向系统创建服务,创建时指定相应的服务属性,CreateService函数参数基本上从名称就能知道具体意思了,原型如下:

CreateServiceA(    _In_        SC_HANDLE    hSCManager,                            //SCM句柄    _In_        LPCSTR     lpServiceName,                           //服务名    _In_opt_    LPCSTR     lpDisplayName,                           //显示的服务名    _In_        DWORD        dwDesiredAccess,                       //存取权限    _In_        DWORD        dwServiceType,                         //服务类别    _In_        DWORD        dwStartType,                           //启动类别    _In_        DWORD        dwErrorControl,                        //错误控制类别    _In_opt_    LPCSTR     lpBinaryPathName,                        //服务的可执行文件路径    _In_opt_    LPCSTR     lpLoadOrderGroup,                        //用户组    _Out_opt_   LPDWORD      lpdwTagId,                             //标签    _In_opt_    LPCSTR     lpDependencies,                          //服务独立性    _In_opt_    LPCSTR     lpServiceStartName,                      //系统账户    _In_opt_    LPCSTR     lpPassword                               //密码    );

CreateService被宏定义为两套函数,分别为CreateServiceW和CreateServiceA,主要区别是前面为Unicode编码,后面为ASCII编码。以下直接使用CreateServiceA函数,一般不这么用,而用CreateService函数。

/*创建服务*/int createService(SC_HANDLE schSCManager, LPSTR szPath, LPSTR szServiceName){    SC_HANDLE schService = CreateServiceA(schSCManager, szServiceName, "demo_srv",        SERVICE_ALL_ACCESS,         //存取权限        SERVICE_WIN32_OWN_PROCESS,  //服务类别        SERVICE_DEMAND_START,       //启动类型        SERVICE_ERROR_NORMAL,       //错误控制类别        szPath, NULL, NULL, NULL, NULL, NULL);    if (schService == NULL){        printf("Create service failed (%d).\n", GetLastError());        return -1;    }    printf("Create service succeeded.\n");    CloseServiceHandle(schService);    schService = NULL;    return 0;}

删除服务

调用OpenService函数打开服务,获得服务句柄,然后使用DeleteService删除服务,该原型如下:

DeleteService(    _In_        SC_HANDLE   hService    );

删除服务是最简单的操作,可以封装成以下函数:

/*删除服务*/int deleteService(SC_HANDLE schSCManager, LPSTR szServiceName){    SC_HANDLE schService = OpenServiceA(schSCManager, szServiceName, DELETE);    if (schService == NULL){        printf("OpenService failed (%d).\n", GetLastError());        return -1;    }    if (!DeleteService(schService)){        printf("Delete service failed (%d).\n", GetLastError());        return -2;    }    printf("Delete service succeeded.\n");    CloseServiceHandle(schService);    schService = NULL;    return 0;}

启动服务

启动服务StartService,有三个参数:服务的句柄,参数个数,参数列表指针。具体函数原型如下:

StartServiceA(    _In_            SC_HANDLE            hService,                   //服务句柄    _In_            DWORD                dwNumServiceArgs,           //参数的个数    _In_reads_opt_(dwNumServiceArgs)                    LPCSTR             *lpServiceArgVectors          //参数列表指针    );

使用QueryServiceStatusEx API函数可以实时获取服务当前的状态。启动服务也较简单,其中多了个while循环对服务状态进行查询,确定PENDING ([‘pendɪŋ] adj.待定的;待决的;即将发生的)状态结束。启动服务实现如下:

/*启动服务*/int startService(SC_HANDLE schSCManager, LPSTR szServiceName){    SC_HANDLE schService;    SERVICE_STATUS_PROCESS ssp;    DWORD dwOldCheckPoint;    DWORD dwStartTickCount;    DWORD dwWaitTime;    DWORD dwBytesNeeded;    schService = OpenServiceA(schSCManager, szServiceName, SERVICE_ALL_ACCESS);    if (schService == NULL){        printf("Open service failed (%d).\n", GetLastError());        return -1;    }    int ret = StartServiceA(schService,        0,   //argc         NULL //argv        );    if (!ret){        printf("Service start failed (%d).\n", GetLastError());        return -2;    }    printf("Service start pending.\n");    //验证状态    if (!QueryServiceStatusEx(schService, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,     sizeof(SERVICE_STATUS_PROCESS), &dwBytesNeeded)){        printf("Query service status failed (%d).\n", GetLastError());        return -3;    }    dwStartTickCount = GetTickCount();    dwOldCheckPoint = ssp.dwCheckPoint;    //查询状态,确定Pending状态结束    while (ssp.dwCurrentState == SERVICE_START_PENDING){        dwWaitTime = ssp.dwWaitHint / 10;        if (dwWaitTime < 1000){            dwWaitTime = 1000;        }        else if (dwWaitTime > 10000){            dwWaitTime = 10000;        }        Sleep(dwWaitTime);        //再次查询        if (!QueryServiceStatusEx(schService, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,         sizeof(SERVICE_STATUS_PROCESS), &dwBytesNeeded)) {            printf("Query service status failed (%d).\n", GetLastError());            break;        }        if (ssp.dwCheckPoint > dwOldCheckPoint){            //进程创建中            dwStartTickCount = GetTickCount();            dwOldCheckPoint = ssp.dwCheckPoint;        }        else if (GetTickCount() - dwStartTickCount > ssp.dwWaitHint){            //WaitHint时间到            break;        }    }    CloseServiceHandle(schService);    schService = NULL;    if (ssp.dwCurrentState != SERVICE_RUNNING){        printf("Start service failed (%d).\n", GetLastError());        return -4;    }    printf("Start service success.\n");    return 0;}

向服务发送控制请求

在服务管理程序向服务程序发送了控制码后,服务所注册的控制处理程序(回调函数),上一篇文章中的serviceCtrlHandlerProc函数就会被调用,并将对应的控制码发送给控制处理程序,由控制处理程序来决定作出何种相应。
ControlService函数原型如下:

ControlService(    _In_        SC_HANDLE           hService,              //服务句柄    _In_        DWORD               dwControl,             //控制码    _Out_       LPSERVICE_STATUS    lpServiceStatus        //返回的服务状态    );

如需要停止服务,可以发送SERVICE_CONTROL_STOP控制码。

/*向服务发送控制码*/int controlService(SC_HANDLE schSCManager, DWORD fdwControl, LPSTR szServiceName){    SC_HANDLE schService;    SERVICE_STATUS status;    DWORD fdwAccess;    switch (fdwControl){    case SERVICE_CONTROL_STOP:        fdwAccess = SERVICE_STOP; break;    case SERVICE_CONTROL_PAUSE:    case SERVICE_CONTROL_CONTINUE:        fdwAccess = SERVICE_PAUSE_CONTINUE; break;    default:        fdwAccess = SERVICE_INTERROGATE; break;    }    schService = OpenServiceA(schSCManager, szServiceName, fdwAccess);    if (schService == NULL){        printf("Open service failed (%d).\n", GetLastError());        return -1;    }    //发送控制码    if (!ControlService(schService, fdwControl, &status)){        printf("Control service failed (%d).\n", GetLastError());        return -2;    }    printf("Control service success.\n");    return 0;}

停止服务运行

服务与服务之间所存在的依赖关系是指一个服务的运行需要其他服务的支持。EnumDependentServices API函数可以列举出依赖于某服务的所有服务。故可以实现,暂停一个服务之前将所有依赖于该服务的其他服务全部终止掉。
EnumDependentServicesA函数原型如下:

EnumDependentServicesA(    _In_            SC_HANDLE               hService,    _In_            DWORD                   dwServiceState,    _Out_writes_bytes_opt_(cbBufSize)                    LPENUM_SERVICE_STATUSA  lpServices,    _In_            DWORD                   cbBufSize,    _Out_           LPDWORD                 pcbBytesNeeded,    _Out_           LPDWORD                 lpServicesReturned    );

以下是一个完整的停止服务函数:

/*停止服务*/DWORD stopService(SC_HANDLE schSCManager, LPSTR szServiceName, bool fStopDependcies, DWORD dwTimeout){    SERVICE_STATUS_PROCESS ssp;    SERVICE_STATUS status;    DWORD dwStartTime = GetTickCount();    DWORD dwBytesNeeded;    SC_HANDLE schService = OpenServiceA(schSCManager, szServiceName, SERVICE_ALL_ACCESS);    if (schService == NULL){        printf("Open service failed (%d).\n", GetLastError());        return -2;    }    if (!QueryServiceStatusEx(schService, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,     sizeof(SERVICE_STATUS_PROCESS), &dwBytesNeeded)){        printf("Query service status failed (%d).\n", GetLastError());        return -3;    }    if (ssp.dwCurrentState == SERVICE_STOPPED){        printf("Stop service success.\n");        return 0;    }    //正在结束,只需等待即可    while (ssp.dwCurrentState == SERVICE_STOP_PENDING){        Sleep(ssp.dwWaitHint);        if (!QueryServiceStatusEx(schService, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,         sizeof(SERVICE_STATUS_PROCESS), &dwBytesNeeded)){            printf("Query service status failed (%d).\n", GetLastError());            return -4;        }        if (ssp.dwCurrentState == SERVICE_STOPPED){            printf("Stop service success.\n");            return 0;        }        if (GetTickCount() - dwStartTime > dwTimeout){            printf("Time Out.\n");            return -5;        }    }    //先结束依赖服务    if (fStopDependcies){        DWORD i;        DWORD dwBytes;        DWORD dwCount;        LPENUM_SERVICE_STATUSA lpDependencies = NULL;        ENUM_SERVICE_STATUSA ess;        SC_HANDLE hDependentService;        //如果EnumDependentService返回成功,说明没有依赖服务        if (!EnumDependentServicesA(schService, SERVICE_ACTIVE,         lpDependencies, 0, &dwBytes, &dwCount)){            if (GetLastError() != ERROR_MORE_DATA){                printf("Enum depend service failed (%d).\n", GetLastError());                return -6;            }            //分配缓冲区存储依赖服务的的数据            lpDependencies = (LPENUM_SERVICE_STATUSA)HeapAlloc(GetProcessHeap(),             HEAP_ZERO_MEMORY, dwBytes);            if (!lpDependencies){                printf("Heap alloc failed (%d).\n", GetLastError());                return -7;            }            __try{                if (!EnumDependentServicesA(schService, SERVICE_ACTIVE, lpDependencies,                 dwBytes, &dwBytes, &dwCount)){                    printf("Enum dependent service failed (%d).\n", GetLastError());                    return -8;                }                for (i = 0; i < dwCount; i++){                    ess = *(lpDependencies + i);                    //打开服务                    hDependentService = OpenServiceA(schSCManager, ess.lpServiceName,                     SERVICE_STOP | SERVICE_QUERY_STATUS);                    if (!hDependentService){                        printf("Open service failed (%d).\n", GetLastError());                        return -9;                    }                    __try{                        //结束服务                        if (!ControlService(hDependentService, SERVICE_CONTROL_STOP,                         &status)){                            printf("Control service failed (%d).\n", GetLastError());                            return -10;                        }                        while (status.dwCurrentState != SERVICE_STOPPED){                            Sleep(status.dwWaitHint);                            if (!QueryServiceStatusEx(hDependentService,                             SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,                             sizeof(SERVICE_STATUS_PROCESS), &dwBytes)){                                printf("Query service status failed (%d).\n", GetLastError());                                return -11;                            }                            if (ssp.dwCurrentState == SERVICE_STOPPED){                                printf("Stop service success.\n");                                break;                            }                            if (GetTickCount() - dwStartTime > dwTimeout){                                printf("Time Out.\n");                                return -12;                            }                        }                    }                    __finally{                        //关闭服务                        CloseServiceHandle(hDependentService);                    }                }            }            __finally{                //释放内存                HeapFree(GetProcessHeap(), 0, lpDependencies);            }        }    }    //所有的依赖服务已经结束,结束指定服务    if (!ControlService(schService, SERVICE_CONTROL_STOP, &status)){        printf("Control service failed (%d).\n", GetLastError());        return -13;    }    while (status.dwCurrentState != SERVICE_STOPPED){        Sleep(status.dwWaitHint);        if (!QueryServiceStatusEx(schService, SC_STATUS_PROCESS_INFO,         (LPBYTE)&ssp, sizeof(SERVICE_STATUS_PROCESS), &dwBytesNeeded)){            printf("Query service status failed (%d).\n", GetLastError());            return -14;        }        if (status.dwCurrentState == SERVICE_STOPPED){            printf("Stop service success.\n");            break;        }        if (GetTickCount() - dwStartTime > dwTimeout){            printf("Time Out.\n");            return -15;        }    }    if (status.dwCurrentState == SERVICE_STOPPED){        printf("Stop service success.\n");        return 0;    }    return -1;}

服务控制与管理测试

到此,已经基本实现了对服务的控制和管理的所有子函数,下面对其所有功能进行测试。
1. 首先给出服务执行体所在的exe地址;
2. 打开SCM获得句柄;
3. 创建服务;
4. 启动服务;
5. 发送控制请求;
6. 停止服务;
7. 删除服务;

int main(void){    char szBinFilePath[MAX_PATH] = "E:/Code/VS2013/Win32Service/Win32Service.exe";    DWORD dwStopError;    LPSTR szServiceName = "demo_srv";    //打开SCM    SC_HANDLE schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);    if (schSCManager == NULL){        printf("Open SCManager failed (%d).\n", GetLastError());        return -1;    }    //创建服务    int ret = createService(schSCManager, szBinFilePath, szServiceName);    if (ret){        printf("Create service failed (%d).\n", GetLastError());        return -2;    }    //启动服务    ret = startService(schSCManager, szServiceName);    if (ret){        printf("Start service failed (%d).\n", GetLastError());        return -3;    }    //发送请求控制    ret = controlService(schSCManager, SERVICE_CONTROL_INTERROGATE, szServiceName);    if (ret){        printf("Control service failed (%d).\n", GetLastError());        return -4;    }    ret = controlService(schSCManager, SERVICE_CONTROL_CONTINUE, szServiceName);    if (ret){        printf("Control service failed (%d).\n", GetLastError());        return -5;    }    dwStopError = stopService(schSCManager, szServiceName, TRUE, 1000);    if (dwStopError){        printf("Stop service failed (%u).\n", dwStopError);        return -6;    }    printf("Stop service success.\n");    //删除服务    ret = deleteService(schSCManager, szServiceName);    if (ret){        printf("Delete service failed (%d).\n", ret);        return -7;    }    CloseServiceHandle(schSCManager);    schSCManager = NULL;    return 0;}
0 0
原创粉丝点击