随笔 - 14, 文章 - 0, 评论 - 3, 引用 - 0
数据加载中……

[转贴]Windows服务程序编程入门

Windows服务程序是在操作系统后台运行的一种程序,要开发该类程序,需要使用windows提供的service API,
MSDN上有对该类api的详细描述,这里简单介绍下windows服务编程的具体步骤:

1.window服务的安装
鼠标右击"我的电脑 -〉管理"可以打开计算机管理器,进入到服务控制管理界面,这里可以看到所有的服务
列表(注意所有这些服务名都存储在window系统数据库中),可以选择每一项服务进行启动或停止等管理操作,我们第一部介绍的就是如何把一项新的服务写入这个服务控制管理页面里,这里我们使用一个installSercices函数实现这一功能,该函数使用OpenSCManager(WIN API)打开服务控制管理器获取的句柄,然后使用CreateService(WIN API)来创建一个新的服务。
///安装服务
int installServices(const char* szName)
{

///这里的path是利用服务启动的对应的exe程序路径
///一般情况下,启动一个服务,任务管理器中你可以可到该服务对应的进程被打开运行起来。
char szPath[1024]={0};
if(!GetModuleFileName(NULL, szPath, 1024))
return -1;

///打开服务控制管理器
SC_HANDLE hSCM = OpenSCManager(
NULL, //主机名,NULL表示本地
NULL, //服务数据库名,NULL默认为 SERVICES_ACTIVE_DATABASE
SC_MANAGER_ALL_ACCESS //期待获取到服务管理器权限,具体权限参考msdn
);
if(!hSCM)
return -1;

///创建一个服务
SC_HANDLE hSS = CreateService(
hSCM,
szName, ///服务名
szName, ///服务控制管理器上的显示名
SC_MANAGER_ALL_ACCESS, ///服务控制权限
SERVICE_WIN32_OWN_PROCESS, /// 服务类型
SERVICE_DEMAND_START, ///服务启动方式SERVICE_DEMAND_START
SERVICE_ERROR_NORMAL, ///服务启动错误处理方式
szPath, ///服务程序路径
NULL,
NULL,
NULL, ///依赖服务
NULL, ///服务所属用户
NULL ///用户密码
);
if(!hSS)
return -1;

if(hSS)
CloseServiceHandle(hSS);

if(hSCM)
CloseServiceHandle(hSCM);

return 0;
}

2. 服务的启动
我们编写一个ServiceStart用来启动服务,启动服务的主要win api为OpenService 打开已存在的服务获取服务句柄,QueryServiceStatus 获取服务当前状态,StartService来启动服务,一般情况下,启动一个服务,任务管理器中你可以可到该服务对应的进程被打开运行起来。像启动apache服务对应的进程为httpd.exe等。该函数就像你在服务控制管理器中选中某一服务项后右键启动。
///启动服务
int ServiceStart(const char* szName)
{
///打开服务控制管理器
SC_HANDLE hSCM = OpenSCManager(
NULL, //主机名,NULL表示本地
NULL, //服务数据库名,NULL默认为 SERVICES_ACTIVE_DATABASE
SC_MANAGER_ALL_ACCESS //期待获取到服务管理器权限,具体权限参考msdn
);
if(!hSCM)
return -1;

///打开服务
SC_HANDLE hSS = OpenService(
hSCM,
szName,
SERVICE_ALL_ACCESS
);
if (!hSS)
goto END;

int iState = SERVICE_START_PENDING;
SERVICE_STATUS status = {0};
status.dwControlsAccepted=SERVICE_ACCEPT_STOP; ///指定该服务允许被停止
status.dwServiceType=SERVICE_WIN32_OWN_PROCESS;
status.dwCurrentState=SERVICE_STOPPED;
///先检测服务程序是否已经运行
for(int i=0;i < 300; i++)
{

if(!QueryServiceStatus(hSS, &status))
goto END;

iState = status.dwCurrentState;
if(status.dwCurrentState == SERVICE_RUNNING)
goto END;

if (status.dwCurrentState == SERVICE_STOPPED)
{
break;
}
if (status.dwCurrentState == SERVICE_START_PENDING
|| status.dwCurrentState == SERVICE_STOP_PENDING)
{
Sleep(10);
continue;
}
}

if(!StartService(
hSS,
0, ///参数个数
NULL ///参数列表
))
{

//goto END;
DWORD err = GetLastError();
err = 0;
}

for(int i=0;i < 300; i++)
{

if(!QueryServiceStatus(hSS, &status))
goto END;

iState = status.dwCurrentState;
if(status.dwCurrentState == SERVICE_RUNNING)
break;

if (status.dwCurrentState == SERVICE_START_PENDING)
{
Sleep(10);
continue;
}
}
if(status.dwCurrentState==SERVICE_RUNNING)
ControlService(hSS, SERVICE_CONTROL_CONTINUE, &status);
END:
if(hSS)
CloseServiceHandle(hSS);
if (hSCM)
CloseServiceHandle(hSCM);

return iState;

}

3. 服务的停止
开启服务后我们也会使用停止服务的功能,这里给出一个停止服务的的函数ServiceStop,可以看出该函数与ServiceSart的代码差别不大,ServiceStop使用ControlService(WIN API )给运行中的程序发送一个SERVICE_CONTROL_STOP消息,运行中的程序收到该消息后便退出。就像你停止了apapche服务后,httpd.exe收到SERVICE_CONTROL_STOP消息并退出进程。该函数就像你在服务控制管理器中选中某一服务项后右键停止效果。ServiceStop函数实现了停止服务和删除服务的功能,所以我们在下一步介绍服务的删除给出代码。

4. 服务的删除
当我们不需要这项服务的时候我们可以在winows服务控制管理器中删除该服务(当然你已可以使用windwo控制台使用命令 sc 命令来实现),使用DeleteService(win api)可以实现这一点,下面是ServiceStop函数,当她的第二个参数被指定为真实,我们在停止服务后会删除该服务。
///停止或删除服务
int ServiceStop(const char* szName,bool bDelService=false)
{
int iState = -1;
///打开服务控制管理器
SC_HANDLE hSCM = OpenSCManager(
NULL, //主机名,NULL表示本地
NULL, //服务数据库名,NULL默认为 SERVICES_ACTIVE_DATABASE
SC_MANAGER_ALL_ACCESS //期待获取到服务管理器权限
);
if(!hSCM)
return -1;

///打开服务
SC_HANDLE hSS = OpenService(
hSCM,
szName,
SERVICE_ALL_ACCESS
);
if (!hSS)
goto END;


///先检测服务程序是否已经运行
for(int i=0;i < 300; i++)
{
SERVICE_STATUS status = {0};
if(!QueryServiceStatus(hSS, &status))
goto END;

iState = status.dwCurrentState;
if(status.dwCurrentState == SERVICE_RUNNING)
break;

if (status.dwCurrentState == SERVICE_STOPPED)
{
goto END;
}
if (status.dwCurrentState == SERVICE_START_PENDING
|| status.dwCurrentState == SERVICE_STOP_PENDING)
{
Sleep(10);
continue;
}
}

SERVICE_STATUS status = {0};
if(!ControlService(hSS,SERVICE_CONTROL_STOP,&status))
goto END;


for(int i=0;i < 300; i++)
{

if(!QueryServiceStatus(hSS, &status))
goto END;

iState = status.dwCurrentState;
if(status.dwCurrentState == SERVICE_STOPPED)
break;

if (status.dwCurrentState == SERVICE_STOP_PENDING)
{
Sleep(10);
continue;
}
}


END:
if(bDelService && hSS)
{
if(!DeleteService(hSS))
{

}
}
if(hSS)
CloseServiceHandle(hSS);
if (hSCM)
CloseServiceHandle(hSCM);

return iState;

}

5 编写windows服务程序的功能代码
事实上,前面4部介绍的函数不一定要出现在我们的服务应用程序代码当中,我们可以使用控制台命令sc来实现控制服务的基本操作,服务程序的功能代码才是我们关注的核心,把一个普通的exe程序改成服务程序,我们只需要在main函数中调用windows指定的service api StartServiceCtrlDispatcher,该函数指定一个windows指定的函数指针,我们把它实现为ServiceMain,如代码:

SERVICE_TABLE_ENTRY sTable[2] = {0};
sTable[0].lpServiceName = (LPSTR)szName; ///该参数被忽略,可以为空字符
sTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain;
sTable[1].lpServiceName = NULL;
sTable[1].lpServiceProc = NULL;
if(!StartServiceCtrlDispatcher((SERVICE_TABLE_ENTRY*)sTable))
err = GetLastError();

ServiceMain才是我们程序功能代码真正实现的地方,这里简单起见,我们把该服务程序的功能写成不断循环写文件,
ServiceMain的函数原形和我们实现的代码如下:
///StartServiceCtrlDispatcher制定的回调函数
void WINAPI ServiceMain(DWORD dwAgrc, LPSTR lpszAgrv)
{
g_statusHandle = RegisterServiceCtrlHandler(g_serviceNmae, (LPHANDLER_FUNCTION)Handler);
if(!g_statusHandle)
return;

SERVICE_STATUS status = {0};
status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
status.dwCurrentState = SERVICE_RUNNING;
status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
if(!SetServiceStatus(g_statusHandle,&status))
goto END;

while (!g_bStop)
{
FILE* pFile = fopen("d://s.service","ab+");
const char* str = "this is service test/r/n";
fwrite(str,1,sizeof("this is service test/r/n"),pFile);
fflush(pFile);
fclose(pFile);
Sleep(10000);
}

END:
status.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus(g_statusHandle,&status);
}

在ServiceMain函数开始,我们立即调用了RegisterServiceCtrlHandler并指定了一个Handler函数指针,这其实是给定了外部与我们服务程序通信的方式,Handler函数就是接收外部消息的的处理函数,我们前面提过的ControlService函数发出的消息就会在这里被接收和处理,SetServiceStatus就是用来设置服务管理控制器界面的服务状态的函数。
void WINAPI Handler(DWORD fdwControl)
{

const char* szName = g_serviceNmae;

SERVICE_STATUS status = {0};
switch(fdwControl)
{
case SERVICE_CONTROL_STOP:
{
g_bStop = true;
status.dwCurrentState = SERVICE_STOPPED;
break;
}
case SERVICE_CONTROL_PAUSE:
status.dwCurrentState = SERVICE_PAUSED;
break;
case SERVICE_CONTROL_CONTINUE:
{
g_bStop= false;
status.dwCurrentState = SERVICE_RUNNING;
break;
}
case SERVICE_CONTROL_INTERROGATE:
break;
default:
break;
}

if(!SetServiceStatus(g_statusHandle,&status))
return;
}

好了,编写windows服务程序的基本步骤入门就是这样子了,下面给出main函数的代码
#include 
#include

static char g_serviceNmae[1024] = "mySampleService";
static SERVICE_STATUS_HANDLE g_statusHandle = NULL;
static bool g_bStop = false;
int main(char argc, char** argv)
{
if(argc >= 3)
{
memset(g_serviceNmae,0,sizeof(char)*1024);
strcpy(g_serviceNmae, argv[2]);
}
const char* szName = g_serviceNmae;
DWORD err = 0;
if(argc > 1){
if(strcmp(argv[1],"-install")==0)
{
if(installServices(szName)==-1)
{
err = GetLastError();
}
else
{
if(ServiceStart(szName)==-1)
err = GetLastError();
}
}
else if(strcmp(argv[1],"-delete")==0)
{
if(ServiceStop(szName,true)==-1)
err = GetLastError();

}
else if(strcmp(argv[1],"-start")==0)
{
if(ServiceStart(szName)==-1)
err = GetLastError();
}
else if(strcmp(argv[1],"-stop")==0)
{
if(ServiceStop(szName)==-1)
err = GetLastError();
}
}

else
{
for (int i=0;i<100;i++)
{
Sleep(50);
}

SERVICE_TABLE_ENTRY sTable[2] = {0};
sTable[0].lpServiceName = (LPSTR)szName; ///该参数被忽略,可以为空字符
sTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain;
sTable[1].lpServiceName = NULL;
sTable[1].lpServiceProc = NULL;
if(!StartServiceCtrlDispatcher((SERVICE_TABLE_ENTRY*)sTable))
err = GetLastError();
}

return 0;
}

原文地址:http://blog.csdn.net/xxq123321/article/details/6212252

posted on 2013-12-17 17:10 天道酬勤 阅读(414) 评论(0)  编辑 收藏 引用 所属分类: windows编程


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理