开发Windows NT的后台服务

摘 要:利用一组WIN32 API函数将自主开发的服务器程序扩展为NT的一项后台服务,让NT把其当作系统服务自动加载,从而扩充NT服务器的后台功能,并结合一 个实例说明开发中应做的工作。
   关键词:Windows NT 后台服务 服务控制管理器

  在WINDOWS NT服务器中,NT的一些后台系统服务随着NT的启动而自动加载, 不需用户人工 干预,这种方式不仅简化了服务器启动过程,同时也使一些重要服务的启动更加安全可靠。 而且,在服务器启动以后,用户也可以利用控制面板中服务控制面板应用程序灵活地设置这 些后台服务的启动属性,方便了用户对NT后台服务的管理。笔者在开发基于NT服务器的应用 程 序时,发现利用一组WIN32 API 函数完全可以将自己开发的服务器程序扩展为NT的一项后台 服务,让NT把其当作系统服务自动加载,从而扩充NT服务器的后台服务功能。下面结合一个 实例,概述开发NT后台服务应用程序的方法和步骤。

1 自定义后台服务注册
  在NT操作系统中,所有的后台服务全都由服务控制管理器进行统一管理,这些后台服务的状 态数据都保存在服务控制管理器数据库中。所以要想创建一个新的后台服务,在应用程序的 主模块里应首先调用函数OpenSCManager打开该数据库,再调用函数CreateService在此数据 库中创建1个新的服务线程对象,并将该线程对象启动属性设置为随系统启动自动加载,这 样NT在重新启动时该线程就会由NT自动启动。完成这一步,仅仅实现了后台服务线程对象的 注册,还没有建立与服务控制管理器的联结。

//在服务控制管理器数据库中注册后台服务线程

void CmdRegisterService() {   SCHANDLE  schService;   SCHANDLE  schSCManager;   TCHAR szPath[512];   //获得后台服务进程执行路径   if(GetModuleFileName(NULL,szPath,512)==0)   {tprintf(TEXT(“Unable to install %s - %s\n"),   TEXT(“Service Example"), GetLastErrorText(szErr, 256));    return;   }   //打开服务控制管理器数据库   schSCManager=OpenSCManager(          NULL,   //本地机器          NULL,   //默认数据库          SCMANAGERALLACCESS //访问权限          );   //在此数据库中创建后台服务线程对象   if(schSCManager)   { schService=CreateService(     schSCManager, //服务控制管理器数据库     TEXT(“ServiceExample"), //后台服务名称     TEXT(“Service Example"),//在服务控制面板中显示的//服务名称     SERVICE_ALL_ACCESS, //访问权限     SERVICE_WIN32_OWN_PROCESS, //服务类型     SERVICE_AUTO_START, //启动类型,随系统自动//加载     SERVICE_ERROR_NORMAL,     szPath,     NULL,     NULL, TEXT(“"), NULL,  //本地系统帐号 NULL);// 没有口令   //在这里可以创建多个后台服务线程对象,完成不同的 //后台任务   if(schService)    {tprintf(TEXT(“%s installed.\n"),TEXT(“Service Example"));     CloseServiceHandle(schService);    }    else    {tprintf(TEXT(“CreateService failed-%s\n"),    GetLastErrorText(szErr, 256));    }    CloseServiceHandle(schSCManager);   }   else    tprintf(TEXT(“OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256)); }


2 定义后台服务线程入口点ServiceMain
  在此之前应该首先定义服务线程入口表(SERVICETABLEENTRY),它记录 了服务器应用程序 要执行的所有后台服务的入口点(在1个服务器应用程序中可同时加载多个后台服务线程)。 每一个后台服务线程都有自己的ServiceMain执行入口点。当服务控制管理器接收到启动后 台服务的命令后,它会首先将启动命令发送到服务控制分配器中,然后,服务控制分配器就 会按照服务线程入口表定义的对应关系启动1个新线程去执行指定的后台服务。在Servic eMain中应当调用RegisterServiceCtrlHandler先注册1个服务控制函数Handler,它负责接 收和处理服务控制管理器发出的命令,并通过调用SetServiceStatus重新设置后台服务状态 ,将被启动的后台服务状态信息回送到服务控制管理器中。这些操作主要是在后台服务线程 启动之前完成一些必要的初始化工作。ServiceMain及Handler是占位符,在程序中可以用不 同的名称定义自己的后台服务线程入口函数和服务控制处理函数。

//定义后台服务线程入口表 SERVICETABLEENTRY MyServiceTable[]= {    {    TEXT(“ServiceExample"),//后台服务线程的名称    (LPSERVICEMAINFUNCTION)MyServiceMain//后台服//务线程入口点    },    {NULL, NULL }   //标志表的结束 }; //服务线程入口函数 void WINAPI MyServiceMain(DWORD dwArgc, LPTSTR *lpszArgv) {   //注册服务控制处理函数 sshStatusHandle=RegisterServiceCtrlHandler(TEXT(“ServiceExample"), Servi ceControlHandler);   if(!sshStatusHandle) goto cleanup;   //服务类型   ssStatus.dwServiceType=SERVICE_WIN32_OWN_PROCESS;   //指定的出错代码   ssStatus.dwServiceSpecificExitCode=0;   //启动自己定义的后台服务   ServiceStart(); cleanup:   if(sshStatusHandle) (VOID)ReportStatusToSCMgr( SERVICESTOPPED,dwErr,0);   return; } //服务控制处理函数:负责接收和处理服务控制管理器发出的 //命令 VOID WINAPI ServiceControlHandler(DWORD dwCtrlCode) {   switch(dwCtrlCode)   {    //停止服务之前,应首先向服务控制管理器回送状态信息    case SERVICE_CONTROLSTOP:     ReportStatusToSCMgr(SERVICE_STOP_PENDING,NOE RROR, 0);     ServiceStop();     return;    //更新服务状态    case SERVICE_CONTROL_INTERROGATE:     break;    default:     break;   }   //向服务控制面板管理器回送状态信息   ReportStatusToSCMgr(ssStatus.dwCurrentState, NOERROR, 0); } //设置当前服务状态并将状态信息回送到服务控制管理器 BOOL ReportStatusToSCMgr(DWORD dwCurrentState,DWORD dwWin32ExitCode,WORD dwWaitHint) {   static DWORD dwCheckPoint=1;   BOOL fResult=TRUE;   if(dwCurrentState==SERVICESTARTPENDING)    ssStatus.dwControlsAccepted=0;   else    ssStatus.dwControlsAccepted=SERVICE_ACCEPT_STOP;   //设置状态信息   ssStatus.dwCurrentState=dwCurrentState;   ssStatus.dwWin32ExitCode=dwWin32ExitCode;   ssStatus.dwWaitHint=dwWaitHint;   if((dwCurrentState==SERVICE_RUNNING )‖    (dwCurrentState==SERVICE_STOPPED))    ssStatus.dwCheckPoint=0;   else    ssStatus.dwCheckPoint=dwCheckPoint++;   //将状态信息回送到服务控制管理器   if(!(fResult=SetServiceStatus(sshStatusHandle,&ssStatus))){    AddToMessageLog(TEXT(“SetServiceStatus"));//向NT事//件管理器报告出错消息    }    return fResult; }


3 建立应用程序主线程与服务控制管理器的连接
  在创建了服务器应用程序的所有服务线程对象后,当NT重新启动加载该服务器进程或者从服 务控制面板中手工启动该服务进程时, 服务器应用程序主线程就会调用函数StartServiceCt rlDispatcher建立服务进程主线程与服务控制管理器的连接,使得主线程扮演起服务控制分 配器的角色,只有当所有的服务线程终止后,该主线程才会完成任务并返回。利用上述过程 建立起来的连接,服务控制管理器会将服务控制面板中用户对服务进程的控制命令(Start、 Terminate等)发送到主线程中,交给服务控制分配器处理。

//服务进程入口 voidCRTAPI1 main(int argc, char **argv) {   if((argc>1)&&((*argv[1]==‘-')‖(*argv[1]==‘/')) )   {    if(stricmp(“register", argv[1]+1)==0)     CmdRegisterService(); //注册后台服务线程对象    else     goto dispatch;    exit(0);   }   dispatch:    //启动服务控制分配器,建立主线程与服务控制面板的 //连接    if(StartServiceCtrlDispatcher(MyServiceTable))     AddToMessageLog(TEXT (“StartServiceCtrlDispatcher success.")); } 4 定义自己的后台服务实现主模块   该模块放在服务线程入口点ServiceMain中,在服务线程初始化完毕以后,就可以启动自己 定义的后台服务。   void ServiceStart() {   //向服务控制管理器报告服务正在启动   if(!ReportStatusToSCMgr( SERVICE_START_PENDING, //service state NOERROR, dwWaitHint)) return;   //下面是线程的初始化代码(注意:初始化代码执行时间 //不应超过设定的nWaitHi nt,否则服务控制管理器会认 //为服务线程已经没有响应   //向服务控制管理器报告服务已经启动   if(!ReportStatusToSCMgr( SERVICE_RUNNING,  //service state NOERROR,   //exit code 0))    //wait hint    return; //开始执行服务 } //停止后台服务线程 void ServiceStop() {   //添加结束服务线程代码 }


  在该示例中,后台服务线程在服务器上创建1个网络套接字,并在6002端口监听TCP/IP客 户 的连接请求。如果与新的客户建立连接后,首先向其发送欢迎消息,接下来,只要每收到客 户发送过来的消息,就会向其回送1个确认信息,直到连接断开以后重新进入监听状态。感 兴趣的读者可以将本程序在VC5.0中编译成可执行文件(在AppWizard中选择Win32 Console A pplication,工程名为ntservice),然后在WINDOWS NT SERVER4.0的控制台中输入“ntservi ce -register”,再重新启动服务器,打开控制面板中的服务管理器,就可以看到Service Example已启动。如果要测试服务例程的响应功能,可以自己编写1个TCP/IP客户程序,并连 接 到该服务器的端口6002。本程序在VC5.0中编译连接,在中文WINDOWS NT SERVER4.0中调试 通过。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值