贝贝花花包包店,精品555皮具,钱夹,皮夹

字体: | 推荐给好友 上一篇 | 下一篇

Windows2000 服务器端应用程序开发设计指南-服务控制程序

发布: 2008-5-06 20:08 | 作者: Jeffrey Richter Jaso | 来源: 本站原创 | 查看: 405次


您应该注意到StopService呼叫了WaitForServiceState函数。WaitForServiceState函数并不是一个Windows函数,但也不是我所编写的函数,而它说明了如何适当地去处理一个服务的状态轮询。考虑以下的情形:使用服务嵌入式管理单元,您初始化了一个服务的停止要求,使得SCM去通知被选择的服务应该停止执行。该服务应该经由呼叫SetServiceStatus与在SERVICE_STATUS结构中,被设定为SERVICE_STOP_PENDING的dwCurrentState成员而回应。然而,服务还是没有被停止,所以服务嵌入式管理单元没有更新它的使用者介面,以反映服务已被停止的情形。不幸地,系统没有提供一个当服务状态改变时通知应用程序的方法,所以一个SCP必须周期性地轮询服务以决定何时改变它的状态。WaitForServiceState函数处理了这个轮询的动作。

BOOL WaitForServiceState(SC_HANDLE hService, DWORD dwDesiredState,  

SERVICE_STATUS* pss, DWORD dwMilliseconds) {
BOOL fServiceOk = TRUE;
BOOL fFirstTime = TRUE; // 第一次不要比较状态/检查点
DWORD dwLastState = 0, dwLastCheckPoint = 0;
DWORD dwTimeout = GetTickCount() + dwMilliseconds;
// 递回至服务到达了被要求的状态、错误发生或逾时为止
for (;;) {
// 取得目前服务的状态
fServiceOk = ::QueryServiceStatus(hService, pss);
// 如果我们不能询问服务,我们会做
if (!fServiceOk)break;
// 如果服务到达了被要求的状态,我们会做
if (pss->dwCurrentState == dwDesiredState) break;
// 如果逾时,我们会做
if ((dwMilliseconds != INFINITE) && (dwTimeout < GetTickCount())) {
fServiceOk = FALSE;
SetLastError(ERROR_TIMEOUT);
break;
}
// 如果第一次储存服务的状态/检查点
if (fFirstTime) {
dwLastState = pss->dwCurrentState;
dwLastCheckPoint = pss->dwCheckPoint;
fFirstTime = FALSE;
} else {
// 如果不是第一次且状态被改变,则储存状态/检查点
if (dwLastState ! = pss->dwCurrentState) {
dwLastState = pss->dwCurrentState;
dwLastCheckPoint = pss->dwCheckPoint;
} else {
// 状态无法被改变;确定检查点没有被减少
if (pss->dwCheckPoint >= dwLastCheckPoint) {
// 好的检查点;储存它
dwLastCheckPoint = pss->dwCheckPoint;
} else {
// 坏的检查点、服务执行失败,我们会做
fServiceOk = FALSE;
break;
}
}
}
// 我们没有完成;等待被指定的一段时间
// 等待提示的1/10轮询
DWORD dwWaitHint = pss->dwWaitHint / 10;
// 最多一次一秒
if (dwWaitHint < 1000) dwWaitHint = 1000;
// 至少每个10秒
if (dwWaitHint > 10000)dwWaitHint = 10000;
Sleep(dwWaitHint);
}
// 注意:最后一个SERVICE_STATUS被回传至呼叫者处,所以呼叫者可以检
// 查服务状态与错误码
return(fServiceOk);
}

我们知道去做轮询的动作是一件麻烦的事,因为它浪费了宝贵的CPU循环时间,但是在这个情形下,我们真的没有其他选择。幸运地,情况并不像您所想的严重,因为SERVICE_STATUS结构包含了dwWaitHint成员。当一个服务呼叫SetServiceStatus时,dwWaitHint成员必须指示在执行下次轮询服务状态的动作前程序在传送控制码时应该等待多少个毫秒。

服务控制程序也应该检查在确认它不会被减少的轮询处理程序期间,从服务被回传的检查点。如果一个服务回传了一个小于检查点的值,那么该服务控制程序应该假设服务已经执行失败。

您将会通知WaitForServiceState去呼叫QueryServiceStatus:

BOOL QueryServiceStatus( 

SC_HANDLE hService,
SERVICE_STATUS* pss);

QueryServiceStatus询问SCM以回传最后被快取的服务状态资讯(当服务最后被SetServiceStatus呼叫时设定)。呼叫QueryServiceStatus的方法就像透过传递SERVICE_CONTROL_INTERROGATE控制码呼叫ControlService一样,但是使用SERVICE_CONTROL_INTERROGATE呼叫ControlService时,会传送一个动作要求给服务以更新目前之状态资讯。另一个在呼叫QueryServiceStatus与ControlService的不同处是QueryServiceStatus总是会在一定的时间内返回,反之,如果服务已经停止回应,则ControlService可能会回传失败。当您传送一个询问控制码给它时,若该服务的控制函数正处于忙碌的状态,则该服务可能会有一段会无法回应,这些会导致您的程序呼叫ControlService并开始等待(也许为30秒)。当然,减少使用QueryServiceStatus函数可能会使SCM被快取的资料不精确并且会影响服务之最新状态。现在您已经知道折衷点在哪里了,所以您可以依照您的实际情况来决定在询问服务使用状态时应该使用何种方式。

除了QueryServiceStatus之外,Microsoft最近加入了新的QueryServiceStatusEx函数:

BOOL QueryServiceStatusEx( 

SC_HANDLE hService,
SC_STATUS_TYPE InfoLevel,
PBYTE pbBuffer,
DWORD cbBufSize,
PDWORD pdwBytesNeeded);

这个函数要求一个服务的状态并将新的SERVICE_STATUS_PROCESS结构初始化:

typedef struct _SERVICE_STATUS_PROCESS { 

DWORD dwServiceType;
DWORD dwCurrentState;
DWORD dwControlsAccepted;
DWORD dwWin32ExitCode;
DWORD dwServiceSpecificExitCode;
DWORD dwCheckPoint;
DWORD dwWaitHint;
DWORD dwProcessId;
DWORD dwServiceFlags;
} SERVICE_STATUS_PROCESS, *LPSERVICE_STATUS_PROCESS;

此结构与SERVICE_STATUS结构完全相同,只是它多出了二个成员:dwProcessId与dwServiceFlags。dwProcessId成员指示了包含服务处理程序的ID,而dwServiceFlags则指出一些关于服务之额外资讯。如果dwServiceFlags包含了SERVICE_RUNS_IN_SYSTEM_PROCESS(目前唯一被定义的标记),则表示该服务正在系统处理程序中执行,例如Services.exe或LSASS.exe。因为处理程序为作业系统之必要元件,所以您不该试图删除一个正在系统处理程序中执行的服务。

重新设定一个服务
 

CreateService函数会将一个新服务的控制项加入至SCM资料库中。它通常不会如此做,然而偶尔您也许会想改变资料库中的资讯。例如,与控制项联系的使用者帐户也许需要已修改的密码,或者您可能想要服务的启动方式从手动改成自动启动。Windows提供您四个函数,以帮助您重新设定一个服务。第一个函数为QueryServiceConfig,它会从SCM资料库中取回服务的控制项:

BOOL QueryServiceConfig( 

SC_HANDLE hService,
QUERY_SERVICE_CONFIG* pqsc,
DWORD dwBufSize,
PDWORD pdwBytesNeeded);

当您呼叫这个函数时,hService参数会确认您想要询问的服务。而它的handle必须经由SERVICE_QUERY_CONFIG的存取而被开启。您也必须配置一个足够大的内存缓冲区以保存QUERY_SERVICE_CONFIG结构与服务的所有字串资料。一个QUERY_SERVICE_CONFIG结构看起来就像这样:

typedef struct _QUERY_SERVICE_CONFIG { 

DWORD dwServiceType;
DWORD dwStartType;
DWORD dwErrorControl;
PTSTR lpBinaryPathName;
PTSTR lpLoadOrderGroup;
DWORD dwTagId;
PTSTR lpDependencies;
PTSTR lpServiceStartName;
PTSTR lpDisplayName;
} QUERY_SERVICE_CONFIG, *LPQUERY_SERVICE_CONFIG;

QueryServiceConfig的dwBufSize参数会告诉函数您需要多大的缓冲区,而被pdwBytesNeeded参数所指的DWORD会被函数填满,并告诉您缓冲区需要多大。因为该函数在复制固定长度的资料结构后,会立即复制所有服务的字串资料到缓冲区中,所以您传递至QueryServiceConfig的缓冲区永远会大于QUERY_SERVICE_ CONFIG结构。PTSTR成员会指向缓冲区内部的内存位址。

 

评分:0

我来说两句

seccode