每一步:改造main入口。
int main(int argc, char *argv[])
{
SERVICE_TABLE_ENTRY ServiceTable[] =
{
{ SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION)ServiceMain },
{ NULL, NULL }
};
::StartServiceCtrlDispatcher(ServiceTable);
return 0;
}
程序入口还是跟普通的控制台程序一样,但是不能在main里直接写程序逻辑(理论上可以,但是如果不能尽快开启派遣并完成服务注册的话,服务控制管理器最终就会强制终止程序)。改造后的main函数主要用于启动服务入口派遣,StartServiceCtrlDispatcher()为SERVICE_TABLE_ENTRY的每一项开启一个线程,并且监视它们的运行状态,只有当所有的服务入口函数/线程退出时,StartServiceCtrlDispatcher()才返回。
第二步:编写服务入口函数。
void WINAPI ServiceMain(int argc, char* argv[])
{
// 注册服务控制句柄
hServiceStatus = ::RegisterServiceCtrlHandler(SERVICE_NAME, CtrlHandler);
if (hServiceStatus == NULL)
{
::OutputDebugStringA("@ServiceMain RegisterServiceCtrlHandler() failed");
return;
}
::OutputDebugStringA("@ServiceMain RegisterServiceCtrlHandler() ok");
// 初使设置启动中状态
ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_PAUSE_CONTINUE;
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwServiceSpecificExitCode = 0;
ServiceStatus.dwCheckPoint = 0;
ServiceStatus.dwWaitHint = 0;
::SetServiceStatus(hServiceStatus, &ServiceStatus);
::OutputDebugStringA("@ServiceMain SetServiceStatus(SERVICE_START_PENDING)");
// 初使化程序逻辑
// 注:在这里调用程序的初使化逻辑,如果初使化失败,需要更新为停止状态
// 这里假设初使化失败,更新为停止状态
// if (!InitServer())
// {
// ServiceStatus.dwCurrentState = SERVICE_STOPPED;
// ::SetServiceStatus(hServiceStatus, &ServiceStatus);
// ::OutputDebugStringA("@ServiceMain init failed");
// return;
// }
// 设置运行状态
ServiceStatus.dwCurrentState = SERVICE_RUNNING;
::SetServiceStatus(hServiceStatus, &ServiceStatus);
::OutputDebugStringA("@ServiceMain SetServiceStatus(SERVICE_RUNNING)");
// 运行程序逻辑,并且等待退出
// 这里RealServer()必须阻塞住,一旦函数返回,下面将自行更改自己的运行状态为停止
// RealServer();
// 设置停止状态
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
::SetServiceStatus(hServiceStatus, &ServiceStatus);
::OutputDebugStringA("@ServiceMain SetServiceStatus(SERVICE_STOPPED)");
::OutputDebugStringA("@ServiceMain exit");
return;
}
从上面的代码及注释可以看到,服务入口函数主要包含两部分操作:
1)注册服务控制句柄,并及时的调整自己的运行状态。
2)调用程序初使化逻辑及运行逻辑。
关于第一部分操作,服务的运行状态包括启动(SERVICE_START_PENDING)、运行中(SERVICE_RUNNING)、停止(SERVICE_STOPPED),服务程序需要根据自己的运行状态及时向服务控制器汇报。
第于第二部分操作,其实就是普通控制台程序main原来的逻辑代码,但是需要配合服务控制器进行状态汇报。
第三步:编写服务控制函数。
void WINAPI CtrlHandler(DWORD request)
{
::OutputDebugStringA("@CtrlHandler call");
switch (request)
{
case SERVICE_CONTROL_STOP:
case SERVICE_CONTROL_SHUTDOWN:
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
::OutputDebugStringA("@CtrlHandler set SERVICE_STOPPED");
break;
case SERVICE_CONTROL_PAUSE:
bPause = true;
ServiceStatus.dwCurrentState = SERVICE_PAUSED;
::OutputDebugStringA("@CtrlHandler set SERVICE_PAUSED");
break;
case SERVICE_CONTROL_CONTINUE:
bPause = false;
ServiceStatus.dwCurrentState = SERVICE_RUNNING;
::OutputDebugStringA("@CtrlHandler set SERVICE_RUNNING");
break;
default:
break;
}
::SetServiceStatus(hServiceStatus, &ServiceStatus);
::OutputDebugStringA("@CtrlHandler exit");
return;
}
这个函数主要用于接收服务管理器发起的状态控制动作,如暂停、恢复、停止,但不包括启动,因为启动动作由main()及服务入口函数来控制。
一般来说,完成以上三个步骤后,控制台程序就可以以windows注册服务的模式来运行了,并且通过控制面板的服务管理器(services.msc)即可控制服务的启动和停止。但是,在此之前,还需要执行服务的安装。
这里补充上面程序改造过程中用到的全局变量定义:
TCHAR SERVICE_NAME[] = _T("mytest");
void WINAPI ServiceMain(int argc, char* argv[]);
void WINAPI CtrlHandler(DWORD request);
SERVICE_STATUS ServiceStatus;
SERVICE_STATUS_HANDLE hServiceStatus;
第四步:安装服务。
有两种方法安装服务:
1)调用sc命令。
2)将安装命令集成到服务程序中。
先讲sc命令的基本用法:
安装服务:
sc create 服务名 binPath= 程序路径
例如:
sc create mytest1 binPath= E:\testservice\Debug\testservice.exe type= own start= auto
[SC] CreateService 成功
移除服务:
sc create 服务名
例如:
sc delete mytest1
[SC] DeleteService 成功
但是,在删除服务之前,需要确保服务已经停止,可以使用服务管理器或者sc stop命令停止服务。
更详细的命令用法请百度。
接下来再讲如何让服务程序支持自安装及删除。
修改main()入口函数,加入对参数的处理。-i参数表示安装服务,-u表示移除服务,-s表示以控制台的方式运行。
int main(int argc, char* argv[])
{
::OutputDebugStringA("@main call");
if (argc >= 2)
{
// 安装服务
if (strcmp(argv[1], "-i") == 0)
{
InstallService(SERVICE_NAME);
}
// 移除服务
else if (strcmp(argv[1], "-u") == 0)
{
RemoveService(SERVICE_NAME);
}
// 以控制台的方式运行
if (strcmp(argv[1], "-s") == 0)
{
RealServer();
}
}
else
{
// 启动服务派遣
SERVICE_TABLE_ENTRY ServiceTable[] =
{
{ SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION)ServiceMain },
{ NULL, NULL }
};
::StartServiceCtrlDispatcher(ServiceTable);
}
::OutputDebugStringA("@main exit");
return 0;
}
然后加上InstallService()和RemoveService()函数的实现。
// 安装服务
int InstallService(TCHAR *SERVICE_NAME)
{
SC_HANDLE hScm;
SC_HANDLE hSrv;
SERVICE_STATUS InstallStatus;
DWORD dwErrCode;
// 取服务完整路径
TCHAR sFilePath[MAX_PATH] = { 0 };
GetModuleFileName(NULL, sFilePath, sizeof(sFilePath));
// 打开服务管理器
hScm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hScm == NULL)
{
::OutputDebugStringA("@InstallService OpenSCManager() failed");
printf("OpenSCManager() failed \n");
return -1;
}
// 尝试创建服务
hSrv = CreateService(hScm, SERVICE_NAME, SERVICE_NAME, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, sFilePath, NULL, NULL, NULL, NULL, NULL);
if (hSrv == NULL)
{
dwErrCode = GetLastError();
if (dwErrCode != ERROR_SERVICE_EXISTS)
{
::OutputDebugStringA("@InstallService CreateService() failed");
printf("CreateService() failed \n");
CloseServiceHandle(hScm);
return -1;
}
// 尝试打开服务
hSrv = OpenService(hScm, SERVICE_NAME, SERVICE_ALL_ACCESS);
if (hSrv == NULL)
{
::OutputDebugStringA("@InstallService OpenService() failed");
printf("OpenService() failed \n");
CloseServiceHandle(hScm);
return -1;
}
}
// 启动服务
if (StartService(hSrv, 0, NULL) == 0)
{
dwErrCode = GetLastError();
if (dwErrCode == ERROR_SERVICE_ALREADY_RUNNING)
{
::OutputDebugStringA("@InstallService StartService() already Running");
printf("StartService() already Running \n");
CloseServiceHandle(hScm);
CloseServiceHandle(hSrv);
return 0;
}
}
// 查询服务状态
while (QueryServiceStatus(hSrv, &InstallStatus) != 0)
{
if (InstallStatus.dwCurrentState != SERVICE_START_PENDING)
break;
Sleep(100);
}
if (InstallStatus.dwCurrentState != SERVICE_RUNNING)
{
::OutputDebugStringA("@InstallService StartService failed");
printf("StartService failed \n");
}
else
{
::OutputDebugStringA("@InstallService StartService success");
printf("StartService success \n");
}
CloseServiceHandle(hScm);
CloseServiceHandle(hSrv);
return 0;
}
// 移除服务
int RemoveService(TCHAR *SERVICE_NAME)
{
SC_HANDLE hScm;
SC_HANDLE hSrv;
SERVICE_STATUS RemoveStatus;
DWORD dwErrCode;
// 打开服务管理器
hScm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hScm == NULL)
{
::OutputDebugStringA("@RemoveService OpenSCManager() failed");
printf("OpenSCManager() failed \n");
return -1;
}
// 尝试打开服务
hSrv = OpenService(hScm, SERVICE_NAME, SERVICE_ALL_ACCESS);
if (hSrv == NULL)
{
dwErrCode = GetLastError();
if (dwErrCode == 1060)
{
::OutputDebugStringA("@RemoveService OpenService() no Exists");
printf("OpenService() no Exists \n");
}
else
{
::OutputDebugStringA("@RemoveService OpenService() no Exists");
printf("OpenService() failed \n");
}
CloseServiceHandle(hScm);
return -1;
}
// 查询服务状态
if (QueryServiceStatus(hSrv, &RemoveStatus) != 0)
{
if (RemoveStatus.dwCurrentState == SERVICE_STOPPED)
{
::OutputDebugStringA("@RemoveService QueryServiceStatus() already Stopped");
printf("already Stopped \n");
}
else
{
// 尝试停止服务
if (ControlService(hSrv, SERVICE_CONTROL_STOP, &RemoveStatus) != 0)
{
while (RemoveStatus.dwCurrentState == SERVICE_STOP_PENDING)
{
Sleep(10);
QueryServiceStatus(hSrv, &RemoveStatus);
}
if (RemoveStatus.dwCurrentState == SERVICE_STOPPED)
{
::OutputDebugStringA("@RemoveService ControlService() StopService Success");
printf("StopService Success \n");
}
else
{
::OutputDebugStringA("@RemoveService ControlService() StopService failed");
printf("StopService failed \n");
}
}
else
{
::OutputDebugStringA("@RemoveService ControlService() StopService failed");
printf("StopService failed \n");
}
}
}
else
{
::OutputDebugStringA("@RemoveService QueryServiceStatus() Query failed");
printf("Query failed \n");
}
// 删除服务
if (DeleteService(hSrv) == 0)
{
::OutputDebugStringA("@RemoveService DeleteService() DeleteService failed");
printf("DeleteService failed \n");
}
else
{
::OutputDebugStringA("@RemoveService DeleteService() DeleteService Success");
printf("DeleteService Success \n");
}
CloseServiceHandle(hScm);
CloseServiceHandle(hSrv);
return 0;
}