Здравствуйте, algol, Вы писали:
A>Здравствуйте, San3k, Вы писали:
S>>Visual Studio 2005, создаётся служба (ЕХЕ), используя мастер ATL.
S>>Вопрос: Куда помещать рабочий код программы, функции?
A>Создание простого сервиса с использованием библиотеки ATLАвтор: Владислав
Дата: 06.04.05
Спасибо, я это читал. Но не нашёл.
Там описываются события PreMessageLoop, RunMessageLoop и т.п., как-то без них можно? Потому что RunMessageLoop, как я понял, обрабатывает сообщения, остальные события — при остановке, при запуске, и т.д.
Вот служба запустилась, работает, что дальше?
Здравствуйте, San3k, Вы писали:
Года 4 назад писал подобную лабуду:
файл Module.h
#pragma once
class CServiceModule
{
public:
CServiceModule();
~CServiceModule();
protected:
static VOID WINAPI _ServiceMain(DWORD, LPTSTR*);
static void WINAPI _Handler(DWORD);
public:
void ShowInfo(UINT);
DWORD WinMain(void);
DWORD Start(void);
private:
BOOL IsInstalled(void);
BOOL Install(void);
BOOL Uninstall(void);
void ServiceMain(DWORD, LPTSTR*);
void Handler(DWORD);
void SetServiceStatus(DWORD, DWORD, DWORD, DWORD);
DWORD RegisterEventSource(void);
DWORD UnregisterEventSource(void);
DWORD SetProcessDebugPrivilege(void);
private:
void ReportError(HRESULT);
void LogEvent(UINT);
void LogError(HRESULT, LPCTSTR);
public:
CAtlString m_strServiceName;
CAtlString m_strServiceDisplayName;
CAtlString m_strServiceDescription;
SERVICE_STATUS_HANDLE m_hServiceStatus;
HANDLE m_hShutdownEvent;
};
extern CServiceModule _Module;
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
файл Module.cpp
[skipped]
#include "Module.h"
CServiceModule::CServiceModule()
{
m_hServiceStatus = NULL;
HINSTANCE hModule = ::GetModuleHandle(NULL);
m_strServiceName.LoadString(hModule, IDS_SERVICENAME);
m_strServiceDisplayName.LoadString(hModule, IDS_SERVICEDISPLAY);
m_strServiceDescription.LoadString(hModule, IDS_SERVICEDESCRIPTION);
m_hShutdownEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
}
CServiceModule::~CServiceModule()
{
if (m_hShutdownEvent != NULL)
::CloseHandle(m_hShutdownEvent);
#ifdef _DEBUG
::_CrtDumpMemoryLeaks();
#endif
}
VOID WINAPI CServiceModule::_ServiceMain(DWORD dwArgc, LPTSTR* lpszArgv)
{
_Module.ServiceMain(dwArgc, lpszArgv);
}
void WINAPI CServiceModule::_Handler(DWORD dwOpcode)
{
_Module.Handler(dwOpcode);
}
void CServiceModule::ShowInfo(UINT uID)
{
CAtlString strInfo;
strInfo.LoadString(::GetModuleHandle(NULL), uID);
::_tprintf(strInfo);
}
DWORD CServiceModule::WinMain(void)
{
DWORD dwRet = 0;
BOOL bMade = FALSE;
CAtlString strCommandLine(::GetCommandLine());
TCHAR szTokens[] = _T("-/");
int nPosition = 0;
CAtlString strToken = strCommandLine.Tokenize(szTokens, nPosition);
while (strToken != _T("")) {
strToken.Trim();
if (strToken.CompareNoCase(_T("Install")) == 0) {
if (Uninstall() && Install() && (RegisterEventSource() == ERROR_SUCCESS))
bMade = TRUE;
else
dwRet = -1;
break;
}
else if (strToken.CompareNoCase(_T("Uninstall")) == 0) {
if (Uninstall() && (UnregisterEventSource() == ERROR_SUCCESS))
bMade = TRUE;
else
dwRet = -1;
break;
}
strToken = strCommandLine.Tokenize(szTokens, nPosition);
}
if (!bMade && (dwRet == 0))
ShowInfo(IDS_SERVICE_USAGE);
return (dwRet);
}
DWORD CServiceModule::Start(void)
{
DWORD dwRet = ERROR_SUCCESS;
SERVICE_TABLE_ENTRY st[] = {{(LPTSTR) (LPCTSTR) m_strServiceName, _ServiceMain}, {NULL, NULL}};
if (::StartServiceCtrlDispatcher(st) == 0) {
dwRet = ::GetLastError();
if (dwRet == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT)
ShowInfo(IDS_SERVICE_USAGE);
}
return (dwRet);
}
BOOL CServiceModule::IsInstalled(void)
{
BOOL bResult = FALSE;
SC_HANDLE hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hSCM != NULL) {
SC_HANDLE hService = ::OpenService(hSCM, m_strServiceName, SERVICE_QUERY_CONFIG);
if (hService != NULL) {
bResult = TRUE;
::CloseServiceHandle(hService);
}
::CloseServiceHandle(hSCM);
}
return (bResult);
}
BOOL CServiceModule::Install(void)
{
if (IsInstalled())
return (TRUE);
TCHAR szFilePath[MAX_PATH + _ATL_QUOTES_SPACE];
DWORD dwFLen = ::GetModuleFileName(NULL, szFilePath + 1, MAX_PATH);
if((dwFLen == 0) || (dwFLen == MAX_PATH))
return (FALSE);
szFilePath[0] = _T('\"');
szFilePath[dwFLen + 1] = _T('\"');
szFilePath[dwFLen + 2] = 0;
SC_HANDLE hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hSCM == NULL) {
CAtlString strError;
strError.LoadString(::GetModuleHandle(NULL), IDS_SERVICE_MANAGER_OPEN_ERROR);
::MessageBox(NULL, strError, m_strServiceName, MB_OK);
return (FALSE);
}
SC_HANDLE hService = ::CreateService(
hSCM, m_strServiceName, m_strServiceDisplayName,
SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
szFilePath, NULL, NULL, _T("RPCSS\0LmHosts\0\0"), NULL, NULL);
if (hService == NULL) {
CAtlString strError;
strError.LoadString(::GetModuleHandle(NULL), IDS_SERVICE_CREATE_ERROR);
::MessageBox(NULL, strError, m_strServiceName, MB_OK);
return (FALSE);
}
SC_LOCK sclLock = ::LockServiceDatabase(hSCM);
if (sclLock != NULL) {
SERVICE_DESCRIPTION sdBuf;
sdBuf.lpDescription = (LPTSTR) (LPCTSTR) m_strServiceDescription;
::ChangeServiceConfig2(hService, SERVICE_CONFIG_DESCRIPTION, (LPVOID) &sdBuf);
::UnlockServiceDatabase(sclLock);
}
::CloseServiceHandle(hService);
::CloseServiceHandle(hSCM);
return (TRUE);
}
BOOL CServiceModule::Uninstall(void)
{
if (!IsInstalled())
return (TRUE);
SC_HANDLE hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hSCM == NULL) {
CAtlString strError;
strError.LoadString(::GetModuleHandle(NULL), IDS_SERVICE_MANAGER_OPEN_ERROR);
::MessageBox(NULL, strError, m_strServiceName, MB_OK);
return (FALSE);
}
SC_HANDLE hService = ::OpenService(hSCM, m_strServiceName, SERVICE_STOP | DELETE);
if (hService == NULL) {
::CloseServiceHandle(hSCM);
CAtlString strError;
strError.LoadString(::GetModuleHandle(NULL), IDS_SERVICE_OPEN_ERROR);
::MessageBox(NULL, strError, m_strServiceName, MB_OK);
return (FALSE);
}
SERVICE_STATUS status;
BOOL bRet = ::ControlService(hService, SERVICE_CONTROL_STOP, &status);
::Sleep(200);
if (!bRet) {
DWORD dwError = GetLastError();
if (!((dwError == ERROR_SERVICE_NOT_ACTIVE) || (dwError == ERROR_SERVICE_CANNOT_ACCEPT_CTRL && status.dwCurrentState == SERVICE_STOP_PENDING))) {
CAtlString strError;
strError.LoadString(::GetModuleHandle(NULL), IDS_SERVICE_STOP_ERROR);
::MessageBox(NULL, strError, m_strServiceName, MB_OK);
}
}
BOOL bDelete = ::DeleteService(hService);
::CloseServiceHandle(hService);
::CloseServiceHandle(hSCM);
if (bDelete)
return (TRUE);
CAtlString strError;
strError.LoadString(::GetModuleHandle(NULL), IDS_SERVICE_DELETE_ERROR);
::MessageBox(NULL, strError, m_strServiceName, MB_OK);
return (FALSE);
}
DWORD CServiceModule::SetProcessDebugPrivilege(void)
{
DWORD dwRet = ERROR_SUCCESS;
HANDLE hToken = NULL;
try {
if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hToken))
::AtlThrow(::GetLastError());
PRIVILEGE_SET psCurrent = {0};
psCurrent.PrivilegeCount = 1;
if (!::LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &psCurrent.Privilege[0].Luid))
::AtlThrow(::GetLastError());
BOOL bResult;
if (!::PrivilegeCheck(hToken, &psCurrent, &bResult))
::AtlThrow(::GetLastError());
if (psCurrent.Privilege[0].Attributes != SE_PRIVILEGE_USED_FOR_ACCESS) {
TOKEN_PRIVILEGES tpNew;
tpNew.PrivilegeCount = 1;
tpNew.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!::LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tpNew.Privileges[0].Luid))
::AtlThrow(::GetLastError());
if (!::AdjustTokenPrivileges(hToken, FALSE, &tpNew, sizeof(tpNew), NULL, NULL))
::AtlThrow(::GetLastError());
}
}
catch (CAtlException e) {
dwRet = e.m_hr;
}
if (hToken != NULL)
::CloseHandle(hToken);
return (dwRet);
}
void CServiceModule::ServiceMain(DWORD /*dwArgc*/, LPTSTR* /*lpszArgv*/)
{
m_hServiceStatus = ::RegisterServiceCtrlHandler(m_strServiceName, _Handler);
if (m_hServiceStatus == NULL) {
ReportError(::GetLastError());
return;
}
DWORD dwLastError = SetProcessDebugPrivilege();
SetServiceStatus(SERVICE_STOP_PENDING, dwLastError, 0, 0);
if (dwLastError != ERROR_SUCCESS) {
SetServiceStatus(SERVICE_STOPPED, dwLastError, 0, 0);
ReportError(dwLastError);
return;
}
WSADATA wsaData;
if (::WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) {
dwLastError = ::GetLastError();
ReportError(dwLastError);
SetServiceStatus(SERVICE_STOPPED, dwLastError, 0, 0);
return;
}
HRESULT hRes = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
if (FAILED(hRes)) {
::WSACleanup();
dwLastError = ::GetLastError();
ReportError(dwLastError);
SetServiceStatus(SERVICE_STOPPED, dwLastError, 0, 0);
return;
}
try {
LogEvent(IDS_SERVICE_STARTED);
hRes = ::CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, 0);
if (FAILED(hRes))
::AtlThrow(hRes);
{
здесь выполнение кода сервиса
}
}
catch(CAtlException e) {
ReportError(e.m_hr);
dwLastError = e.m_hr;
SetServiceStatus(SERVICE_STOP_PENDING, dwLastError, 0, 0);
}
::CoUninitialize();
::WSACleanup();
LogEvent(IDS_SERVICE_STOPPED);
SetServiceStatus(SERVICE_STOPPED, dwLastError, 0, 0);
}
void CServiceModule::Handler(DWORD dwOpcode)
{
DWORD dwState = SERVICE_RUNNING;
switch (dwOpcode) {
case SERVICE_CONTROL_SHUTDOWN:
case SERVICE_CONTROL_STOP:
dwState = SERVICE_STOP_PENDING;
break;
case SERVICE_CONTROL_INTERROGATE:
break;
default:
break;
}
SetServiceStatus(dwState, ERROR_SUCCESS, 0, 0);
if ((dwOpcode == SERVICE_CONTROL_SHUTDOWN) || (dwOpcode == SERVICE_CONTROL_STOP)) {
::SetEvent(m_hShutdownEvent);
::Sleep(100);
}
}
void CServiceModule::SetServiceStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwCheckPoint, DWORD dwWaitHint)
{
SERVICE_STATUS status;
if (dwCurrentState == SERVICE_START_PENDING)
status.dwControlsAccepted = 0;
else
status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
status.dwServiceSpecificExitCode = 0;
status.dwCurrentState = dwCurrentState;
status.dwWin32ExitCode = dwWin32ExitCode;
status.dwCheckPoint = dwCheckPoint;
status.dwWaitHint = dwWaitHint;
::SetServiceStatus(m_hServiceStatus, &status);
}
const LPCTSTR szEventAppID = _T("SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\");
DWORD CServiceModule::RegisterEventSource(void)
{
DWORD dwRet = ERROR_SUCCESS;
try {
TCHAR szFilePath[MAX_PATH + 1];
DWORD dwFLen = ::GetModuleFileName(NULL, szFilePath, MAX_PATH);
if((dwFLen == 0) || (dwFLen == MAX_PATH))
::AtlThrow(::GetLastError());
CRegKey keyEventAppID;
LONG lRes = keyEventAppID.Open(HKEY_LOCAL_MACHINE, szEventAppID, KEY_WRITE);
if (lRes != ERROR_SUCCESS)
::AtlThrow(::AtlHresultFromWin32((DWORD)lRes));
lRes = keyEventAppID.SetKeyValue(m_strServiceName, szFilePath, _T("EventMessageFile"));
if (lRes != ERROR_SUCCESS)
::AtlThrow(::AtlHresultFromWin32((DWORD)lRes));
CRegKey keyAppID;
lRes = keyAppID.Open(keyEventAppID, m_strServiceName, KEY_WRITE);
if (lRes != ERROR_SUCCESS)
::AtlThrow(::AtlHresultFromWin32((DWORD)lRes));
lRes = keyAppID.SetDWORDValue(_T("TypesSupported"), EVENTLOG_ERROR_TYPE | EVENTLOG_INFORMATION_TYPE);
if (lRes != ERROR_SUCCESS)
::AtlThrow(::AtlHresultFromWin32((DWORD)lRes));
lRes = keyAppID.Close();
if (lRes != ERROR_SUCCESS)
::AtlThrow(::AtlHresultFromWin32((DWORD)lRes));
lRes = keyEventAppID.Close();
if (lRes != ERROR_SUCCESS)
::AtlThrow(::AtlHresultFromWin32((DWORD)lRes));
}
catch (CAtlException e) {
dwRet = e.m_hr;
}
return (dwRet);
}
DWORD CServiceModule::UnregisterEventSource(void)
{
DWORD dwRet = ERROR_SUCCESS;
try {
CRegKey keyEventAppID;
LONG lRes = keyEventAppID.Open(HKEY_LOCAL_MACHINE, szEventAppID, KEY_WRITE);
if (lRes != ERROR_SUCCESS)
::AtlThrow(::AtlHresultFromWin32((DWORD)lRes));
lRes = keyEventAppID.RecurseDeleteKey(m_strServiceName);
if (lRes != ERROR_SUCCESS)
::AtlThrow(::AtlHresultFromWin32((DWORD)lRes));
lRes = keyEventAppID.Close();
if (lRes != ERROR_SUCCESS)
::AtlThrow(::AtlHresultFromWin32((DWORD)lRes));
}
catch (CAtlException e) {
dwRet = e.m_hr;
}
return (dwRet);
}
void CServiceModule::LogEvent(UINT uID)
{
HANDLE hEventSource;
hEventSource = ::RegisterEventSource(NULL, m_strServiceName);
if (hEventSource != NULL) {
CAtlString strMessage;
strMessage.LoadString(::GetModuleHandle(NULL), uID);
LPCTSTR ppStrings[1];
ppStrings[0]= strMessage;
::ReportEvent(hEventSource, EVENTLOG_INFORMATION_TYPE, 0, MSG_SERVICE_INFO, NULL, sizeof(ppStrings) / sizeof(LPCTSTR), 0, ppStrings, NULL);
::DeregisterEventSource(hEventSource);
}
}
void CServiceModule::ReportError(HRESULT hResError)
{
LPVOID lpMsgBuf;
if (::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
hResError,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0,
NULL )) {
LogError(hResError, (LPCTSTR) lpMsgBuf);
::LocalFree(lpMsgBuf);
}
else
LogError(0x00, _T("Unknown"));
}
void CServiceModule::LogError(HRESULT hResError, LPCTSTR lpszError)
{
HANDLE hEventSource;
hEventSource = ::RegisterEventSource(NULL, m_strServiceName);
if (hEventSource != NULL) {
CAtlString strError;
strError.Format(_T("%#x"), hResError);
LPCTSTR ppStrings[2];
ppStrings[0] = lpszError;
ppStrings[1] = strError;
::ReportEvent(hEventSource, EVENTLOG_ERROR_TYPE, 0, MSG_SERVICE_ERROR, NULL, sizeof(ppStrings) / sizeof(LPCTSTR), 0, ppStrings, NULL);
::DeregisterEventSource(hEventSource);
}
}
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Здравствуйте, San3k, Вы писали:
S>В указанном выше примере описаны события. Но ведь нет события "после старта", я правильно понимаю?
S>То, что в них, выполняется при запуске службы, и, пока не выполниться, служба не стартует...
PreMessageLoop — выполняется до старта. Можно использовать для чтения конфигов, инициализации и т.п.
RunMessageLoop — собственно старт сервиса. Отсюда вы запускаете отдельный поток для выполнения вашей работы или взводите таймер, если сервис выполняет работу периодически.
OnStop — остановка сервиса. Здесь вы завершаете рабочий поток или убиваете таймер.
OnPause — приостановка работы сервиса. Отличается от OnStop тем, что вы останавливаете рабочий поток на время и можете потом продолжить его выполнение.
OnContinue — продолжение работы после OnPause.
OnShutdown — завершение работы сервиса.
Вот простой пример сервиса с таймером:
HRESULT CMyServiceModule::PreMessageLoop (int nShowCmd) throw()
{
CoInitialize(NULL);
// Инициализация
m_uPeriod = 60;
configPtr = ConfigPtr(Config::Load(_T("MyService.xml")));
return S_OK;
}
void CMyServiceModule::RunMessageLoop () throw()
{
m_uTimer = ::SetTimer(NULL, NULL, m_uPeriod * 1000, &CMyServiceModule::TimerProc);
CAtlServiceModuleT< CMyServiceModule, IDS_SERVICENAME >::RunMessageLoop ();
}
void CMyServiceModule::OnContinue () throw()
{
CAtlServiceModuleT< CMyServiceModule, IDS_SERVICENAME >::OnContinue ();
m_uTimer = ::SetTimer(NULL, NULL, m_uPeriod * 1000, &CMyServiceModule::TimerProc);
SetServiceStatus(SERVICE_RUNNING);
}
void CMyServiceModule::OnPause () throw()
{
CAtlServiceModuleT< CMyServiceModule, IDS_SERVICENAME >::OnPause ();
if (m_uTimer)
{
::KillTimer(NULL, m_uTimer);
m_uTimer = 0;
}
SetServiceStatus(SERVICE_PAUSED);
}
void CMyServiceModule::OnShutdown () throw()
{
OnStop ();
}
void CMyServiceModule::OnStop () throw()
{
CAtlServiceModuleT< CMyServiceModule, IDS_SERVICENAME >::OnStop ();
if (m_uTimer)
{
::KillTimer(NULL, m_uTimer);
m_uTimer = 0;
}
SetServiceStatus(SERVICE_STOPPED);
CoUninitialize();
}
void CMyServiceModule::TimerProc( HWND hWnd, // window handle
UINT nMsg, // WM_TIMER
UINT nIDEvent, // timer identifier
DWORD dwTime // system time
)
{
// Здесь вы что-то делаете
}