TimeService.cpp
/********************************************************************
/模组:TimeService.cpp
通告:Copyright (c)2000 Jeffrey Richter
********************************************************************/
#include "..\CmnHdr.h" /* 请参阅附录A */
#include "..\ClassLib\IOCP.h" /* 请参阅附录B */
#include "..\ClassLib\EnsureCleanup.h" /* 请参阅附录B */
#define SERVICESTATUS_IMPL
#include "ServiceStatus.h"
//////////////////////////////////////////////////////////////////////////////
TCHAR g_szServiceName[] = TEXT("Programming Server-Side Applications Time");
CServiceStatus g_ssTime;
//////////////////////////////////////////////////////////////////////////////
// 完成连接埠因为以下二个原因之一而醒来
enum COMPKEY {
CK_SERVICECONTROL, // 一个服务控制码
CK_PIPE // 一个客户端连接至我们的管道
};
//////////////////////////////////////////////////////////////////////////////
DWORD WINAPI TimeHandlerEx(DWORD dwControl, DWORD dwEventType,
PVOID pvEventData, PVOID pvContext) {
DWORD dwReturn = ERROR_CALL_NOT_IMPLEMENTED;
BOOL fPostControlToServiceThread = FALSE;
switch (dwControl) {
case SERVICE_CONTROL_STOP:
case SERVICE_CONTROL_SHUTDOWN:
g_ssTime.SetUltimateState(SERVICE_STOPPED, 2000);
fPostControlToServiceThread = TRUE;
break;
case SERVICE_CONTROL_PAUSE:
g_ssTime.SetUltimateState(SERVICE_PAUSED, 2000);
fPostControlToServiceThread = TRUE;
break;
case SERVICE_CONTROL_CONTINUE:
g_ssTime.SetUltimateState(SERVICE_RUNNING, 2000);
fPostControlToServiceThread = TRUE;
break;
case SERVICE_CONTROL_INTERROGATE:
g_ssTime.ReportStatus();
break;
case SERVICE_CONTROL_PARAMCHANGE:
break;
case SERVICE_CONTROL_DEVICEEVENT:
case SERVICE_CONTROL_HARDWAREPROFILECHANGE:
case SERVICE_CONTROL_POWEREVENT:
break;
case 128: // 一个测试用的使用者定义控制码
// 注意:正常的情况下,一个服务不该显示使用者介面
MessageBox(NULL, TEXT("In HandlerEx processing user-defined code."),
g_szServiceName, MB_OK |MB_SERVICE_NOTIFICATION);
break;
}
if (fPostControlToServiceThread) {
// Handler线程非常简单而且执行的非常快速
// 它只传递控制码以离开ServiceMain线程
CIOCP* piocp = (CIOCP*) pvContext;
piocp->PostStatus(CK_SERVICECONTROL, dwControl);
dwReturn = NO_ERROR;
}
return(dwReturn);
}
//////////////////////////////////////////////////////////////////////////////
void WINAPI TimeServiceMain(DWORD dwArgc, PTSTR* pszArgv) {
ULONG_PTR CompKey = CK_SERVICECONTROL;
DWORD dwControl = SERVICE_CONTROL_CONTINUE;
CEnsureCloseFile hpipe;
OVERLAPPED o, *po;
SYSTEMTIME st;
DWORD dwNumBytes;
// 建立完成端口并储存在整体变数中它的handle
//所以Handler函数可以存取它
CIOCP iocp(0);
g_ssTime.Initialize(g_szServiceName, TimeHandlerEx, (PVOID) &iocp, TRUE);
g_ssTime.AcceptControls(
SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE);
do {
switch (CompKey) {
case CK_SERVICECONTROL:
// 我们取得一个控制码
switch (dwControl) {
case SERVICE_CONTROL_CONTINUE:
// 当它正在执行时,建立一个管道以让客户端连结
hpipe = CreateNamedPipe(TEXT("\\\\.\\pipe\\TimeService"),
PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE, 1, sizeof(st), sizeof(st), 1000, NULL);
// 建立管道与完成连接埠之关联
iocp.AssociateDevice(hpipe, CK_PIPE);
// 相对于管道,此处悬置了一个非同步连接
ZeroMemory(&o, sizeof(o));
ConnectNamedPipe(hpipe, &o);
g_ssTime.ReportUltimateState();
break;
case SERVICE_CONTROL_PAUSE:
case SERVICE_CONTROL_STOP:
// 若没有在执行中,则关闭管道以使客户端无法与之连结
hpipe.Cleanup();
g_ssTime.ReportUltimateState();
break;
}
break;
case CK_PIPE:
if (hpipe.IsValid()) {
// 我们取得了一个客户端要求:传送我们的系统时间至客户端
GetSystemTime(&st);
WriteFile(hpipe, &st, sizeof(st), &dwNumBytes, NULL);
FlushFileBuffers(hpipe);
DisconnectNamedPipe(hpipe);
// 允许另一个客户端连结
ZeroMemory(&o, sizeof(o));
ConnectNamedPipe(hpipe, &o);
} else {
// 当管道被关闭时,我们会执行到此处
}
}
if (g_ssTime != SERVICE_STOPPED) {
// 除非一个控制码进来或是有一个客户端连接上来,否则即处于睡眠状态
iocp.GetStatus(&CompKey, &dwNumBytes, &po);
dwControl = dwNumBytes;
}
} while (g_ssTime != SERVICE_STOPPED);
}
/////////////////////////////////////////////////////////////////////////////////////////
void InstallService() {
// 在此机器上开启SCM
CEnsureCloseServiceHandle hSCM =
OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
// 取得我们的路径名称
TCHAR szModulePathname[_MAX_PATH * 2];
GetModuleFileName(NULL, szModulePathname, chDIMOF(szModulePathname));
// 因为处理程序去执行一个服务,所以增加转换switch
lstrcat(szModulePathname, TEXT(" /service"));
// 将此服务加入SCM的资料库中
CEnsureCloseServiceHandle hService =
CreateService(hSCM, g_szServiceName, g_szServiceName,
SERVICE_CHANGE_CONFIG, SERVICE_WIN32_OWN_PROCESS,
SERVICE_DEMAND_START, SERVICE_ERROR_IGNORE,
szModulePathname, NULL, NULL, NULL, NULL, NULL);
SERVICE_DESCRIPTION sd = {
TEXT("Sample Time Service from")
TEXT("Programming Server-Side Applications for Microsoft Windows Book")
};
ChangeServiceConfig2(hService, SERVICE_CONFIG_DESCRIPTION, &sd);
}
//////////////////////////////////////////////////////////////////////////////
void RemoveService() {
// 在此机器上开启SCM
CEnsureCloseServiceHandle hSCM =
OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
// 为了DELETE的存取而开启这个服务
CEnsureCloseServiceHandle hService =
OpenService(hSCM, g_szServiceName, DELETE);
// 从SCM资料库中移除此服务
DeleteService(hService);
}
//////////////////////////////////////////////////////////////////////////////
int WINAPI _tWinMain(HINSTANCE hinstExe, HINSTANCE, PTSTR pszCmdLine, int) {
int nArgc = __argc;
#ifdef UNICODE
PCTSTR *ppArgv = (PCTSTR*) CommandLineToArgvW(GetCommandLine(), &nArgc);
#else
PCTSTR *ppArgv = (PCTSTR*) __argv;
#endif
if (nArgc < 2) {
MessageBox(NULL,
TEXT("Programming Server-Side Applications for Microsoft Windows:")
TEXT("Time Service Sample\n\n")
TEXT("Usage:TimeService.exe [/install] [/remove] [/debug] ")
TEXT("[/service]\n")
TEXT(" //install\t\tInstalls the service in the SCM’s database.\n")
TEXT(" //remove\t\tRemoves the service from the SCM’s database.\n")
TEXT(" //debug \t \tRuns the service as a normal process for ")
TEXT("debugging.\n ")
TEXT(" //service \t\tRuns the process as a service")
TEXT("(should only be set in the SCM’s database)."),
g_szServiceName, MB_OK);
} else {
for (int i = 1; i < nArgc; i++) {
if ((ppArgv[i][0] == TEXT(’-’)) || (ppArgv[i][0] == TEXT(’/’))) {
// 命令列开关
if (lstrcmpi(&ppArgv[i][1], TEXT("install")) == 0)
InstallService();
if (lstrcmpi(&ppArgv[i][1], TEXT("remove")) == 0)
RemoveService();
if (lstrcmpi(&ppArgv[i][1], TEXT("debug")) == 0) {
// 执行服务控制码
TimeServiceMain(0, NULL);
}
if (lstrcmpi(&ppArgv[i][1], TEXT("service")) == 0) {
// 连接至服务控制分发器
SERVICE_TABLE_ENTRY ServiceTable [] == {
{ g_szServiceName, TimeServiceMain },
{ NULL, NULL } // End of list
};
chVERIFY(StartServiceCtrlDispatcher(ServiceTable));
}
}
}
}
#ifdef UNICODE
HeapFree(GetProcessHeap(), 0, (PVOID) ppArgv);
#endif
return(0);
}
///////////////////////////////// End of File ////////////////////////////////
ServiceStatus.h
/*******************************************************************
模组:ServiceStatus.h
注意:Copyright (c)2000 Jeffrey Richter
目的:这个类别封装了一个SERVICE_STATUS结构以适当的使用
********************************************************************/
#pragma once
///////////////////////////////////////////////////////////////////////////////
#include "..\CmnHdr.h" /*See Appendix A. */
#include "Gate.h "
///////////////////////////////////////////////////////////////////////////////
class CServiceStatus : public SERVICE_STATUS {
public:
CServiceStatus();
BOOL Initialize(PCTSTR szServiceName, LPHANDLER_FUNCTION_EX pfnHandler,
PVOID pvContext, BOOL fOwnProcess, BOOL fInteractWithDesktop = FALSE);
VOID AcceptControls(DWORD dwFlags, BOOL fAccept = TRUE);
BOOL ReportStatus();
BOOL SetUltimateState(DWORD dwUltimateState, DWORD dwWaitHint = 0);
BOOL AdvanceState(DWORD dwWaitHint, DWORD dwCheckPoint = 0);
BOOL ReportUltimateState();
BOOL ReportWin32Error(DWORD dwError);
BOOL ReportServiceSpecificError(DWORD dwError);
operator DWORD() const { return(dwCurrentState); }
private:
SERVICE_STATUS_HANDLE m_hss;
CGate m_gate;
};
///////////////////////////////////////////////////////////////////////////////
inline CServiceStatus::CServiceStatus() {
ZeroMemory(this, sizeof(SERVICE_STATUS));
m_hss = NULL;
}
///////////////////////////////////////////////////////////////////////////////
inline VOID CServiceStatus::AcceptControls(DWORD dwFlags, BOOL fAccept) {
if (fAccept) dwControlsAccepted |= dwFlags;
else dwControlsAccepted &= ~dwFlags;
}
///////////////////////////////////////////////////////////////////////////////
inline BOOL CServiceStatus::ReportStatus() {
BOOL fOk = ::SetServiceStatus(m_hss, this);
chASSERT(fOk);
return(fOk);
}
///////////////////////////////////////////////////////////////////////////////
inline BOOL CServiceStatus::ReportWin32Error(DWORD dwError) {
dwWin32ExitCode = dwError;
dwServiceSpecificExitCode = 0;
return(ReportStatus());
}
inline BOOL CServiceStatus::ReportServiceSpecificError(DWORD dwError) {
dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
dwServiceSpecificExitCode = dwError;
return(ReportStatus());
}
///////////////////////////////////////////////////////////////////////////////
#ifdef SERVICESTATUS_IMPL
///////////////////////////////////////////////////////////////////////////////
BOOL CServiceStatus::Initialize(PCTSTR szServiceName,
LPHANDLER_FUNCTION_EX pfnHandler, PVOID pvContext,
BOOL fOwnProcess, BOOL fInteractWithDesktop) {
m_hss = RegisterServiceCtrlHandlerEx(szServiceName, pfnHandler, pvContext);
chASSERT(m_hss != NULL);
dwServiceType = fOwnProcess
? SERVICE_WIN32_OWN_PROCESS : SERVICE_WIN32_SHARE_PROCESS;
if (fInteractWithDesktop)
dwServiceType |= SERVICE_INTERACTIVE_PROCESS;
dwCurrentState = SERVICE_START_PENDING;
dwControlsAccepted = 0;
dwWin32ExitCode = NO_ERROR;
dwServiceSpecificExitCode = 0;
dwCheckPoint = 0;
dwWaitHint = 2000;
return(m_hss != NULL);
}
///////////////////////////////////////////////////////////////////////////////
BOOL CServiceStatus::SetUltimateState(DWORD dwUltimateState,
DWORD dwWaitHint) {
DWORD dwPendingState = 0; // 一个无效的状态值
switch (dwUltimateState) {
case SERVICE_STOPPED:
dwPendingState = SERVICE_STOP_PENDING;
break;
case SERVICE_RUNNING:
dwPendingState = (dwCurrentState == SERVICE_PAUSED)
? SERVICE_CONTINUE_PENDING :SERVICE_START_PENDING;
break;
case SERVICE_PAUSED:
dwPendingState = SERVICE_PAUSE_PENDING;
break;
default:
chASSERT(dwPendingState != 0); // Invalid parameter
break;
}
// 当建立了一个新的ServiceMain线程,系统假设
// dwCurrentState=SERVICE_START_PENDING, dwCheckPoint=0, dwWaitHint=2000
// So,since we must always increment the checkpoint, let’s start at 1
dwCheckPoint = 1;
this->dwWaitHint = dwWaitHint;
// 没有错误被回报
dwWin32ExitCode = NO_ERROR;
dwServiceSpecificExitCode = 0;
BOOL fOk = FALSE; // 假设失败的情形
if (dwPendingState != 0) {
// 如果另一个悬置中的作业没有被完成,即等待它
m_gate.WaitToEnterGate();
dwCurrentState = dwPendingState; // Update the state in the structure
// 如果没有等待提示,则到达要求状态
fOk = (dwWaitHint != 0) ? ReportStatus() : ReportUltimateState();
}
return(fOk);
}
///////////////////////////////////////////////////////////////////////////////
BOOL CServiceStatus::AdvanceState(DWORD dwWaitHint, DWORD dwCheckPoint) {
// 一个为0的检查点为无效的,所以我们会增加检查点至1
this->dwCheckPoint =
(dwCheckPoint == 0) ? this->dwCheckPoint + 1 : dwCheckPoint;
this->dwWaitHint = dwWaitHint;
// 没有错误被回报
dwWin32ExitCode = NO_ERROR;
dwServiceSpecificExitCode = 0;
return(ReportStatus());
}
///////////////////////////////////////////////////////////////////////////////
BOOL CServiceStatus::ReportUltimateState() {
DWORD dwUltimateState = 0; // An invalid state value
switch (dwCurrentState) {
case SERVICE_START_PENDING:
case SERVICE_CONTINUE_PENDING:
dwUltimateState = SERVICE_RUNNING;
break;
case SERVICE_STOP_PENDING:
dwUltimateState = SERVICE_STOPPED;
break;
case SERVICE_PAUSE_PENDING:
dwUltimateState = SERVICE_PAUSED;
break;
}
dwCheckPoint = dwWaitHint = 0; // We reached the ultimate state
// 没有错误被回报
dwWin32ExitCode = NO_ERROR;
dwServiceSpecificExitCode = 0;
BOOL fOk = FALSE; // 假设为失败的情形
if (dwUltimateState != 0) [
dwCurrentState = dwUltimateState; // 更新结构中的状态
fOk = ReportStatus();
// 我们的状态改变完成,允许一个新的状态被改变
m_gate.LiftGate();
}
return(fOk);
}
///////////////////////////////////////////////////////////////////////////////
#endif // SERVICESTATUS_IMPL
//////////////////////////////// End of File /////////////////////////////////