Написание сервиса с использованием ATL
От: San3k  
Дата: 23.07.09 07:01
Оценка:
Здравствуйте!

Вопрос по службам поднимался не раз, но ответа найти не удалось.

Visual Studio 2005, создаётся служба (ЕХЕ), используя мастер ATL.
Написание служб не занимался ни разу, да и вижл с знаю не очень.

Вопрос: Куда помещать рабочий код программы, функции?
Если помещать в Run, то выполняется при старте, а как сделать, чтобы уже после того, как служба запущена, она занималась своей работой?

Заранее спасибо!
atl сервисы
Re: Написание сервиса с использованием ATL
От: algol Россия about:blank
Дата: 23.07.09 09:39
Оценка:
Здравствуйте, San3k, Вы писали:

S>Visual Studio 2005, создаётся служба (ЕХЕ), используя мастер ATL.

S>Вопрос: Куда помещать рабочий код программы, функции?

Создание простого сервиса с использованием библиотеки ATL
Автор: Владислав
Дата: 06.04.05
Re[2]: Написание сервиса с использованием ATL
От: San3k  
Дата: 23.07.09 09:53
Оценка:
Здравствуйте, algol, Вы писали:

A>Здравствуйте, San3k, Вы писали:


S>>Visual Studio 2005, создаётся служба (ЕХЕ), используя мастер ATL.

S>>Вопрос: Куда помещать рабочий код программы, функции?

A>Создание простого сервиса с использованием библиотеки ATL
Автор: Владислав
Дата: 06.04.05



Спасибо, я это читал. Но не нашёл.
Там описываются события PreMessageLoop, RunMessageLoop и т.п., как-то без них можно? Потому что RunMessageLoop, как я понял, обрабатывает сообщения, остальные события — при остановке, при запуске, и т.д.

Вот служба запустилась, работает, что дальше?
Re: Написание сервиса с использованием ATL
От: San3k  
Дата: 27.07.09 07:18
Оценка:
Кто-нибудь подскажет?

В указанном выше примере описаны события. Но ведь нет события "после старта", я правильно понимаю?
То, что в них, выполняется при запуске службы, и, пока не выполниться, служба не стартует...
Re: Написание сервиса с использованием ATL
От: Figaro Россия  
Дата: 27.07.09 09:03
Оценка:
Здравствуйте, 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>>
Re[2]: Написание сервиса с использованием ATL
От: Figaro Россия  
Дата: 27.07.09 09:07
Оценка:
файл 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>>
Re[2]: Написание сервиса с использованием ATL
От: algol Россия about:blank
Дата: 27.07.09 14:16
Оценка:
Здравствуйте, 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
                                    )
{
    // Здесь вы что-то делаете
}
Re[3]: Написание сервиса с использованием ATL
От: San3k  
Дата: 28.07.09 05:01
Оценка:
Огромное всем спасибо, буду разбираться!
Re[4]: Написание сервиса с использованием ATL
От: San3k  
Дата: 28.07.09 05:58
Оценка:
Попробовал сделать с таймером, никак не могу избавиться от такой ошибки:

error C2664: 'SetTimer' : cannot convert parameter 4 from 'TIMERPROC (__thiscall CSrv_adcModule::* )(HWND,UINT,UINT,DWORD)' to 'TIMERPROC'
There is no context in which this conversion is possible

ругается на RunMessageLoop и OnContinue, где используется &CSrv_adcModule::TimerProc

m_uTimer = ::SetTimer(NULL, NULL, 500, &CSrv_adcModule::TimerProc);

Почему не может сконвертировать? )
Re[5]: Написание сервиса с использованием ATL
От: algol Россия about:blank
Дата: 28.07.09 09:30
Оценка:
Здравствуйте, San3k, Вы писали:

S>error C2664: 'SetTimer' : cannot convert parameter 4 from 'TIMERPROC (__thiscall CSrv_adcModule::* )(HWND,UINT,UINT,DWORD)' to 'TIMERPROC'

S>There is no context in which this conversion is possible
S>Почему не может сконвертировать? )

TimerProc должна быть объявлена в классе как static void CALLBACK.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.