Timer i usługa w Windowsie

0

Witam

W jaki sposób mogę w skorzystać z Timera w usłudze w Windowsie ? Usługę napisałem w Windows API i C++.

0

Dzięki ale chyba nie o to mi chodziło. W usłudze nie mam standardowego uchwytu do okna a także nie mam standardowej pętli komunikatów.

Załóżmy mam taką usługę, przykład ze strony MSDN http://msdn.microsoft.com/en-us/library/bb540475%28v=vs.85%29.aspx

#include <windows.h>
#include <tchar.h>
#include <strsafe.h>
#include "sample.h"

#pragma comment(lib, "advapi32.lib")

#define SVCNAME TEXT("SvcName")

SERVICE_STATUS          gSvcStatus; 
SERVICE_STATUS_HANDLE   gSvcStatusHandle; 
HANDLE                  ghSvcStopEvent = NULL;

VOID SvcInstall(void);
VOID WINAPI SvcCtrlHandler( DWORD ); 
VOID WINAPI SvcMain( DWORD, LPTSTR * ); 

VOID ReportSvcStatus( DWORD, DWORD, DWORD );
VOID SvcInit( DWORD, LPTSTR * ); 
VOID SvcReportEvent( LPTSTR );


//
// Purpose: 
//   Entry point for the process
//
// Parameters:
//   None
// 
// Return value:
//   None
//
void __cdecl _tmain(int argc, TCHAR *argv[]) 
{ 
    // If command-line parameter is "install", install the service. 
    // Otherwise, the service is probably being started by the SCM.

    if( lstrcmpi( argv[1], TEXT("install")) == 0 )
    {
        SvcInstall();
        return;
    }

    // TO_DO: Add any additional services for the process to this table.
    SERVICE_TABLE_ENTRY DispatchTable[] = 
    { 
        { SVCNAME, (LPSERVICE_MAIN_FUNCTION) SvcMain }, 
        { NULL, NULL } 
    }; 
 
    // This call returns when the service has stopped. 
    // The process should simply terminate when the call returns.

    if (!StartServiceCtrlDispatcher( DispatchTable )) 
    { 
        SvcReportEvent(TEXT("StartServiceCtrlDispatcher")); 
    } 
} 

//
// Purpose: 
//   Installs a service in the SCM database
//
// Parameters:
//   None
// 
// Return value:
//   None
//
VOID SvcInstall()
{
    SC_HANDLE schSCManager;
    SC_HANDLE schService;
    TCHAR szPath[MAX_PATH];

    if( !GetModuleFileName( NULL, szPath, MAX_PATH ) )
    {
        printf("Cannot install service (%d)\n", GetLastError());
        return;
    }

    // Get a handle to the SCM database. 
 
    schSCManager = OpenSCManager( 
        NULL,                    // local computer
        NULL,                    // ServicesActive database 
        SC_MANAGER_ALL_ACCESS);  // full access rights 
 
    if (NULL == schSCManager) 
    {
        printf("OpenSCManager failed (%d)\n", GetLastError());
        return;
    }

    // Create the service

    schService = CreateService( 
        schSCManager,              // SCM database 
        SVCNAME,                   // name of service 
        SVCNAME,                   // service name to display 
        SERVICE_ALL_ACCESS,        // desired access 
        SERVICE_WIN32_OWN_PROCESS, // service type 
        SERVICE_DEMAND_START,      // start type 
        SERVICE_ERROR_NORMAL,      // error control type 
        szPath,                    // path to service's binary 
        NULL,                      // no load ordering group 
        NULL,                      // no tag identifier 
        NULL,                      // no dependencies 
        NULL,                      // LocalSystem account 
        NULL);                     // no password 
 
    if (schService == NULL) 
    {
        printf("CreateService failed (%d)\n", GetLastError()); 
        CloseServiceHandle(schSCManager);
        return;
    }
    else printf("Service installed successfully\n"); 

    CloseServiceHandle(schService); 
    CloseServiceHandle(schSCManager);
}

//
// Purpose: 
//   Entry point for the service
//
// Parameters:
//   dwArgc - Number of arguments in the lpszArgv array
//   lpszArgv - Array of strings. The first string is the name of
//     the service and subsequent strings are passed by the process
//     that called the StartService function to start the service.
// 
// Return value:
//   None.
//
VOID WINAPI SvcMain( DWORD dwArgc, LPTSTR *lpszArgv )
{
    // Register the handler function for the service

    gSvcStatusHandle = RegisterServiceCtrlHandler( 
        SVCNAME, 
        SvcCtrlHandler);

    if( !gSvcStatusHandle )
    { 
        SvcReportEvent(TEXT("RegisterServiceCtrlHandler")); 
        return; 
    } 

    // These SERVICE_STATUS members remain as set here

    gSvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; 
    gSvcStatus.dwServiceSpecificExitCode = 0;    

    // Report initial status to the SCM

    ReportSvcStatus( SERVICE_START_PENDING, NO_ERROR, 3000 );

    // Perform service-specific initialization and work.

    SvcInit( dwArgc, lpszArgv );
}

//
// Purpose: 
//   The service code
//
// Parameters:
//   dwArgc - Number of arguments in the lpszArgv array
//   lpszArgv - Array of strings. The first string is the name of
//     the service and subsequent strings are passed by the process
//     that called the StartService function to start the service.
// 
// Return value:
//   None
//
VOID SvcInit( DWORD dwArgc, LPTSTR *lpszArgv)
{
    // TO_DO: Declare and set any required variables.
    //   Be sure to periodically call ReportSvcStatus() with 
    //   SERVICE_START_PENDING. If initialization fails, call
    //   ReportSvcStatus with SERVICE_STOPPED.

    // Create an event. The control handler function, SvcCtrlHandler,
    // signals this event when it receives the stop control code.

    ghSvcStopEvent = CreateEvent(
                         NULL,    // default security attributes
                         TRUE,    // manual reset event
                         FALSE,   // not signaled
                         NULL);   // no name

    if ( ghSvcStopEvent == NULL)
    {
        ReportSvcStatus( SERVICE_STOPPED, NO_ERROR, 0 );
        return;
    }

    // Report running status when initialization is complete.

    ReportSvcStatus( SERVICE_RUNNING, NO_ERROR, 0 );

    // TO_DO: Perform work until service stops.

    while(1)
    {
        // Check whether to stop the service.

        WaitForSingleObject(ghSvcStopEvent, INFINITE);

        ReportSvcStatus( SERVICE_STOPPED, NO_ERROR, 0 );
        return;
    }
}

//
// Purpose: 
//   Sets the current service status and reports it to the SCM.
//
// Parameters:
//   dwCurrentState - The current state (see SERVICE_STATUS)
//   dwWin32ExitCode - The system error code
//   dwWaitHint - Estimated time for pending operation, 
//     in milliseconds
// 
// Return value:
//   None
//
VOID ReportSvcStatus( DWORD dwCurrentState,
                      DWORD dwWin32ExitCode,
                      DWORD dwWaitHint)
{
    static DWORD dwCheckPoint = 1;

    // Fill in the SERVICE_STATUS structure.

    gSvcStatus.dwCurrentState = dwCurrentState;
    gSvcStatus.dwWin32ExitCode = dwWin32ExitCode;
    gSvcStatus.dwWaitHint = dwWaitHint;

    if (dwCurrentState == SERVICE_START_PENDING)
        gSvcStatus.dwControlsAccepted = 0;
    else gSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;

    if ( (dwCurrentState == SERVICE_RUNNING) ||
           (dwCurrentState == SERVICE_STOPPED) )
        gSvcStatus.dwCheckPoint = 0;
    else gSvcStatus.dwCheckPoint = dwCheckPoint++;

    // Report the status of the service to the SCM.
    SetServiceStatus( gSvcStatusHandle, &gSvcStatus );
}

//
// Purpose: 
//   Called by SCM whenever a control code is sent to the service
//   using the ControlService function.
//
// Parameters:
//   dwCtrl - control code
// 
// Return value:
//   None
//
VOID WINAPI SvcCtrlHandler( DWORD dwCtrl )
{
   // Handle the requested control code. 

   switch(dwCtrl) 
   {  
      case SERVICE_CONTROL_STOP: 
         ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0);

         // Signal the service to stop.

         SetEvent(ghSvcStopEvent);
         ReportSvcStatus(gSvcStatus.dwCurrentState, NO_ERROR, 0);
         
         return;
 
      case SERVICE_CONTROL_INTERROGATE: 
         break; 
 
      default: 
         break;
   } 
   
}

//
// Purpose: 
//   Logs messages to the event log
//
// Parameters:
//   szFunction - name of function that failed
// 
// Return value:
//   None
//
// Remarks:
//   The service must have an entry in the Application event log.
//
VOID SvcReportEvent(LPTSTR szFunction) 
{ 
    HANDLE hEventSource;
    LPCTSTR lpszStrings[2];
    TCHAR Buffer[80];

    hEventSource = RegisterEventSource(NULL, SVCNAME);

    if( NULL != hEventSource )
    {
        StringCchPrintf(Buffer, 80, TEXT("%s failed with %d"), szFunction, GetLastError());

        lpszStrings[0] = SVCNAME;
        lpszStrings[1] = Buffer;

        ReportEvent(hEventSource,        // event log handle
                    EVENTLOG_ERROR_TYPE, // event type
                    0,                   // event category
                    SVC_ERROR,           // event identifier
                    NULL,                // no security identifier
                    2,                   // size of lpszStrings array
                    0,                   // no binary data
                    lpszStrings,         // array of strings
                    NULL);               // no binary data

        DeregisterEventSource(hEventSource);
    }
}

To moje pytanie brzmi gdzie i w jaki sposób użyć tego SetTimer czy innego rodzaju timera ?

0

Przeczytać uważnie link:

lpTimerFunc [in, optional]
Type: TIMERPROC

A pointer to the function to be notified when the time-out value elapses. For more information about the function, see TimerProc. If lpTimerFunc is NULL, the system posts a WM_TIMER message to the application queue. The hwnd member of the message's MSG structure contains the value of the hWnd parameter.
2
> posts a WM_TIMER message to the application queue

A z tego co na szybko widzę, kolejki komunikatów tu nie obsługujesz / nie ma.


Źle to u góry napisałem.
Cytat z linka @_13th_Dragon:</p>

An application can process WM_TIMER messages by including a WM_TIMER case statement in the window procedure or by specifying a TimerProc callback function when creating the timer. When you specify a TimerProc callback function, the default window procedure calls the callback function when it processes WM_TIMER. Therefore, you need to dispatch messages in the calling thread, even when you use TimerProc instead of processing WM_TIMER.

Więc SetTimer chyba odpada.

Może to Ci pomoże:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms687012(v=vs.85).aspx

0

Dziękuję obu kolegom za szybką odpowiedź.

Z tego co zdążyłem doczytać to jeśli parametr lpTimerFunc jest wskaźnikiem do funkcji, to Timer to nie przesyła komunikatu WM_TIMER do kolejki komunikatów aplikacji, tylko wywołuje tą funkcję.

Waitable Timer kompletnie nie znam, czy może ktoś mi wyjaśnić główną różnicę pomiędzy stosowaniem Timer a WaitableTimer

2

Jeśli nie masz to pętli komunikatów to timer nie będzie działał - bo nie masz możliwości wykonywania zdarzeń (w tym wypadku WM_TIMER czyli zdarzenie wywoływane co określony czas).
Zostaje Ci tylko wołanie funkcji zwracającej czas (więc wyliczanie interwałów) np. timeGetTime lub QueryPerformanceCounter (własny timer)
ewentualnie możesz zrobić nowy wątek tam zrobić pętlę zdarzeń i wykonywać tam zdarzenia

0

@_13th_Dragon: my english mastah :P - przeczytaj też ze zrozumieniem, zrób sobie apke bez pętli komunikatów i zobacz czy działa.

0

Jeśli twierdzicie że czytacie ze zrozumieniem, to czytajcie ze zrozumieniem DO KOŃCA.

When you specify a TimerProc callback function, the default window procedure calls the callback function when it processes WM_TIMER. Therefore, you need to dispatch messages in the calling thread, even when you use TimerProc instead of processing WM_TIMER.

Pogrubienie moje. Kto chce, niech sobie minusuje w ramach zaklinania rzeczywistości.

0

No to właśnież Panie tak pisałem, że z tego co widzę, w w/w kodzie nie ma kolejki i SetTimer nie zadziała. Jakby była, to by zadziałało.

0

Zgadzam się z darkbit, Azarien i __nobody

To nie działa

#include <windows.h>
#include <stdio.h>
VOID CALLBACK funkcja();

void main()
{

SetTimer(NULL, 1, 1000, (TIMERPROC) funkcja); 
char str [80];
printf ("Wcisnij 'q' aby wyjsc");
do 
{
    scanf ("%s",str);
    printf ("Wpisales %s\n",str);
} while (strcmp(str,"q") != 0);

	KillTimer(NULL, 1);
}


VOID CALLBACK funkcja()
{
printf ("Wywołana funkcja dziala");
} 

Pętlę dodałem gdyż program bez niej od razu kończył działanie.

To działa

#include <windows.h>
#include <stdio.h>

VOID CALLBACK funkcja();


void main()
{
	MSG msg;
	SetTimer(NULL, 1, 1000, (TIMERPROC) funkcja); 

	while(GetMessage(&msg, NULL, 0, 0) > 0)
	{
		if  (msg.message == WM_DESTROY)
		{
			KillTimer(NULL, 1); 
			break;
		}
		else
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}
}

VOID CALLBACK funkcja()
{
printf ("Wywołana funkcja dziala");
}

Jak widać, tu nie ma jawnej obsługi komunikatu WM_TIMER.

Nie mniej jednak SetTimer kompletnie odpada w usłudze bo musiałbym tworzyć dodatkowy wątek aby obsługiwać pętle komunikatów.

Z tego co udało mi się znaleźć, to aby użyć Timera bez pętli, można użyć Timer Queues i funkcji CreateTimerQueueTimer na MSDN jest przykład użycia http://msdn.microsoft.com/en-us/library/ms687003%28v=vs.85%29.aspx .

Jeszcze jakby mi ktoś wyjaśnił, czy różni się Timer Queues od asychronicznego użycia funkcji CreateWaitableTimer, to byłoby super.

1

Napisałem kod z użyciem funkcji CreateTimerQueueTimer, nie ma tu obsługi pętli komunikatów a działa

#include <windows.h>
#include <stdio.h>

VOID CALLBACK funkcja()
{
	printf ("\nWywolana funkcja dziala");
}

void main()
{
	HANDLE hTimer = NULL;
	HANDLE hTimerQueue = NULL;
	hTimerQueue = CreateTimerQueue();
	CreateTimerQueueTimer( &hTimer, hTimerQueue, (WAITORTIMERCALLBACK)funkcja,NULL, 3000, 2000, 0);
	char str [80];
	printf ("Wcisnij 'q' aby wyjsc");
	do {
		scanf ("%s",str);
		printf ("Wpisales %s\n",str);
	} while (strcmp(str,"q") != 0);
	DeleteTimerQueue(hTimerQueue);

}
0

Napisałeś poprzednio, że nie chcesz tworzyc wątku dla pętli komunikatów. Z tego co wyczytałem:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms687003(v=vs.85).aspx
callback będzie odpalony z osobnego wątku. Taka uwaga z mojej strony ;)

1 użytkowników online, w tym zalogowanych: 0, gości: 1