专注于互联网--专注于架构

最新标签
网站地图
文章索引
Rss订阅

首页 »C 教程 » vc编写windows服务:Windows服务编写原理及探讨(2) »正文

vc编写windows服务:Windows服务编写原理及探讨(2)

来源: 发布时间:星期四, 2009年2月12日 浏览:104次 评论:0



( 2)对服务深入讨论的上

  上章其实只是概括性介绍下面开始才是真正细节所在在进入点里面要完成ServiceMain准确点说是个SERVICE_TABLE_ENTRY结构这个结构记录了这个服务里面所包含所有服务名称和服务进入点下面是个SERVICE_TABLE_ENTRY例子:

SERVICE_TABLE_ENTRY service_table_entry =
{
{ \"MyFTPd\" , FtpdMain },
{ \"MyHttpd\", Httpserv},
{ NULL, NULL },
};

  第个成员代表服务名字第 2个成员是ServiceMain回调地址上面服务拥有两个服务所以有 3个SERVICE_TABLE_ENTRY元素前两个用于服务最后NULL指明结束

  接下来这个地址被传递到StartServiceCtrlDispatcher:

BOOL StartServiceCtrlDispatcher(
LPSERVICE_TABLE_ENTRY lpServiceStartTable
)

  这个Win32表明可执行文件进程怎样通知SCM包含在这个进程中服务就像上章中讲那样StartServiceCtrlDispatcher为每个传递到它非空元素产生个新线程个进程开始执行由元素中lpServiceStartTable指明ServiceMain

  SCM启动个服务的后它会等待该主线程去调StartServiceCtrlDispatcher如果那个在两分钟内没有被SCM将会认为这个服务有问题TerminateProcess去杀死这个进程这就要求你主线程要尽可能快StartServiceCtrlDispatcher

  StartServiceCtrlDispatcher则并不立即返回相反它会驻留在个循环内当在该循环内时StartServiceCtrlDispatcher悬挂起自己等待下面两个事件中个发生如果SCM要去送个控制通知给运行在这个进程内个服务时候这个线程就会激活当控制通知到达后线程激活并相应服务CtrlHandlerCtrlHandler处理这个服务控制通知并返回到StartServiceCtrlDispatcherStartServiceCtrlDispatcher循环回去后再次悬挂自己

  第 2如果服务线程中个服务中止这个线程也将激活在这种情况下该进程将运行在它里面服务数减如果服务数为零StartServiceCtrlDispatcher就会返回到入口点以便能够执行任何和进程有关清除工作并结束进程如果还有服务在运行哪怕只是个服务StartServiceCtrlDispatcher也会继续循环下去继续等待其它控制通知或者剩下服务线程中止

  上面内容是有关入口点下面内容则是有关ServiceMain还记得以前讲过ServiceMain原型吗?但实际上个ServiceMain通常忽略传递给它两个参数服务般不如何传递参数设置个服务最好思路方法就是设置注册表般服务在
HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Service\\ServiceName\\Parameters
子键下存放自己设置这里ServiceName是服务名字事实上可能要写个客户应用去进行服务背景设置这个客户应用将这些信息存在注册表中以便服务读取个外部应用已经改变了某个正在运行中服务设置数据时候这个服务能够用RegNotyChangeKeyValue去接受个通知这样就允许服务快速重新设置自己

  前面讲到StartServiceCtrlDispatcher为每个传递到它非空元素产生个新线程接下来个ServiceMain要做些什么呢?MSDN里面原文是这样说:The ServiceMain function should immediately call the RegisterServiceCtrlHandler function to specy a Handler function to handle control requests. Next, it should call the SetServiceStatus function to send status information to the service control manager. 为什么呢?发出启动服务请求的后如果在定时间的内无法完成服务SCM会认为服务启动已经失败了这个时间长度在Win NT 4.0中是80秒Win2000中不详...

  基于上面理由ServiceMain要迅速完成自身工作首先是必不可少两项工作项是RegisterServiceCtrlHandler去通知SCM它CtrlHandler回调地址:

SERVICE_STATUS_HANDLE RegisterServiceCtrlHandler(
LPCTSTR lpServiceName, //服务名字
LPHANDLER_FUNCTION lpHandlerProc //CtrlHandler地址
)


  第个参数指明你正在建立CtrlHandler是为哪个服务所用第 2个参数是CtrlHandler地址lpServiceName必须和在SERVICE_TABLE_ENTRY里面被服务名字相匹配RegisterServiceCtrlHandler返回个SERVICE_STATUS_HANDLE这是个32位句柄SCM用它来唯确定这个服务当这个服务需要把它当时状态报告给SCM时候就必须把这个句柄传给需要它Win32注意:这个句柄和其他大多数句柄区别你无需关闭它

  SCM要求ServiceMain线程在秒钟内RegisterServiceCtrlHandler否则SCM会认为服务已经失败但在这种情况下SCM不会终止服务不过在NT 4中将无法启动这个服务同时会返回个不正确信息点在Windows 2000中得到了修正

  在RegisterServiceCtrlHandler返回后ServiceMain线程要立即告诉SCM服务正在继续具体思路方法是通过SetServiceStatus传递SERVICE_STATUS数据结构

BOOL SetServiceStatus(
SERVICE_STATUS_HANDLE hService, //服务句柄
SERVICE_STATUS lpServiceStatus //SERVICE_STATUS结构地址
)

  这个要求传递给它指明服务句柄(刚刚通过RegisterServiceCtrlHandler得到)SERVICE_STATUS结构地址:

typedef struct _SERVICE_STATUS
{
DWORD dwServiceType;
DWORD dwCurrentState;
DWORD dwControlsAccepted;
DWORD dwWin32ExitCode;
DWORD dwServiceSpecicExitCode;
DWORD dwCheckPo;
DWORD dwWaitH;
} SERVICE_STATUS, *LPSERVICE_STATUS;

  SERVICE_STATUS结构含有 7个成员它们反映服务现行状态所有这些成员必须在这个结构被传递到SetServiceStatus的前正确设置



  成员dwServiceType指明服务可执行文件类型如果你可执行文件中只有个单独服务就把这个成员设置成SERVICE_WIN32_OWN_PROCESS;如果拥有多个服务就设置成SERVICE_WIN32_SHARE_PROCESS除了这两个标志的外如果你服务需要和桌面发生交互(当然不推荐这样做)就要用“OR”运算符附加上SERVICE_INTERACTIVE_PROCESS这个成员值在你服务生存期内绝对不应该改变

  成员dwCurrentState是这个结构中最重要成员它将告诉SCM你服务现行状态为了报告服务仍在应该把这个成员设置成SERVICE_START_PENDING在以后具体讲述CtrlHandler时候具体解释其它可能

  成员dwControlsAccepted指明服务愿意接受什么样控制通知如果你允许个SCP去暂停/继续服务就把它设成SERVICE_ACCEPT_PAUSE_CONTINUE很多服务不支持暂停或继续就必须自己决定在服务中它是否可用如果你允许个SCP去停止服务就要设置它为SERVICE_ACCEPT_STOP如果服务要在操作系统关闭时候得到通知设置它为SERVICE_ACCEPT_SHUTDOWN可以收到预期结果这些标志可以用“OR”运算符组合

  成员dwWin32ExitCode和dwServiceSpecicExitCode是允许服务报告关键如果希望服务去报告个Win32代码(预定义在WinError.h中)它就设置dwWin32ExitCode为需要代码个服务也可以报告它本身特有、没有映射到个预定义Win32代码中为了这要把dwWin32ExitCode设置为ERROR_SERVICE_SPECIFIC_ERROR然后还要设置成员dwServiceSpecicExitCode为服务特有代码当服务运行正常没有可以报告时候就设置成员dwWin32ExitCode为NO_ERROR

  最后两个成员dwCheckPo和dwWaitH个服务用来报告它当前事件进展情况当成员dwCurrentState被设置成SERVICE_START_PENDING时候应该把dwCheckPo设成0dwWaitH设成个经过多次尝试后确定比较合适这样服务才能高效运行旦服务被完全就应该重新化SERVICE_STATUS结构成员更改dwCurrentState为SERVICE_RUNNING然后把dwCheckPo和dwWaitH都改为0

  dwCheckPo成员存在对用户是有益它允许个服务报告它处于进程SetServiceStatus时可以增加它到个能指明服务已经执行到哪数字它可以帮助用户决定多长时间报告次服务进展情况如果决定要报告服务化进程就应该设置dwWaitH为你认为到达下步所需毫秒数而不是服务完成它进程所需毫秒数

  在服务所有化都完成的后服务SetServiceStatus指明SERVICE_RUNNING在那刻服务已经开始运行通常个服务是把自己放在个循环的中来运行在循环内部这个服务进程悬挂自己等待指明它下步是应该暂停、继续或停止的类网络请求或通知个请求到达时候服务线程激活并处理这个请求然后再循环回去等待下个请求/通知

  如果个服务由于个通知而激活它会先处理这个通知除非这个服务得到是停止或关闭通知如果真是停止或关闭通知服务线程将退出循环执行必要清除操作然后从这个线程返回当ServiceMain线程返回并中止时引起在StartServiceCtrlDispatcher内睡眠线程激活并像在前面解释过那样减少它运行服务计数
0

相关文章

读者评论

发表评论

  • 昵称:
  • 内容: