【问题标题】:Windows C service not writing to file in PreShutdown eventWindows C 服务未在 PreShutdown 事件中写入文件
【发布时间】:2021-01-22 16:26:02
【问题描述】:

我正在开发一个用 C 代码编写的 Windows 服务。

在服务初始化代码中,我注册了一个 SERVICE_ACCEPT_PRESHUTDOWN 事件,如下所示:

gSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PRESHUTDOWN;

在服务控制处理程序中,我在 SERVICE_CONTROL_PRESHUTDOWN 事件中编写了写入文件的逻辑,如下所示:

DWORD WINAPI SvcCtrlHandler(DWORD dwCtrl, DWORD eventType, void *eventData, void *context)
{
    if (dwCtrl == SERVICE_CONTROL_STOP)
    {
        gSvcStatus.dwWin32ExitCode = 0;
        gSvcStatus.dwCurrentState = SERVICE_STOPPED;
    }
    else if (dwCtrl == SERVICE_CONTROL_PRESHUTDOWN)
    {
        char sentence[] = "Hello World";
        FILE *fptr;
        fptr = fopen("C:\\test\\program.txt", "w");
        if (fptr == NULL) {
            printf("Error!");
        }
        else
        {
            fprintf(fptr, "%s", sentence);
            fclose(fptr);
        }
        gSvcStatus.dwWin32ExitCode = 0;
        gSvcStatus.dwCurrentState = SERVICE_STOPPED;
    }
    SetServiceStatus(g_ServiceStatusHandle, &gSvcStatus);
    return NO_ERROR;
}

但重启后,我无法在 test 文件夹中看到文件 program.txt。需要帮助解决此问题。

我还尝试了什么:

我还尝试使用 SvcReportEvent 函数写入事件查看器:

//
// 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);
    }
}

SvcCtrlHandler函数修改完成:

DWORD WINAPI SvcCtrlHandler(DWORD dwCtrl, DWORD eventType, void *eventData, void *context)
{
    if (dwCtrl == SERVICE_CONTROL_STOP)
    {
        SvcReportEvent((LPTSTR)TEXT("In function SvcCtrlHandler in condition SERVICE_CONTROL_STOP"));
        gSvcStatus.dwWin32ExitCode = 0;
        gSvcStatus.dwCurrentState = SERVICE_STOPPED;
    }
    else if (dwCtrl == SERVICE_CONTROL_PRESHUTDOWN)
    {
        SvcReportEvent((LPTSTR)TEXT("In function SvcCtrlHandler in condition SERVICE_CONTROL_PRESHUTDOWN"));
        gSvcStatus.dwWin32ExitCode = 0;
        gSvcStatus.dwCurrentState = SERVICE_STOPPED;
    }
    SetServiceStatus(g_ServiceStatusHandle, &gSvcStatus);
    return NO_ERROR;
}

观察:

  1. 当我重新启动计算机时,没有写入 Eventviewer 日志
  2. 当我手动停止服务时,会在事件查看器中写入一条日志:

在 SERVICE_CONTROL_STOP 条件下的函数 SvcCtrlHandler 中

注意:我在编写服务时引用的链接:https://docs.microsoft.com/en-us/windows/win32/services/the-complete-service-sample?redirectedfrom=MSDN

【问题讨论】:

  • 您确定在每次调用 SetServiceStatus 时都设置了 SERVICE_ACCEPT_PRESHUTDOWN 标志吗?另外,我建议添加 SERVICE_ACCEPT_SHUTDOWN 和相应的处理程序,看看是否被调用。
  • 嗨。是的,我在每次调用 SetSerivceStatus 时都设置了 PRESHUTDOWN 标志。我还尝试添加推荐的 SHUTDOWN 处理程序。由于某种原因,即使是这个处理程序也没有被调用。我也尝试过只使用 SHUTDOWN 处理程序,因为我读过的一些帖子表明只有一个 SHUTDOWN/PRESHUTDOWN 被调用。但对我来说,他们都没有被叫到。
  • 奇怪。确保您对 SetServiceStatus() 的调用成功并且没有返回错误。这是什么版本的 Windows?
  • 嗨@CoreTech。我想知道您是否可以与我分享一个基于 C 的最低工作服务,以供参考如何处理 SHUTDOWN / PRESHUTDOWN 事件。我目前正在寻找一个示例来执行此操作,但我没有在网上找到任何满足我要求的东西。
  • @CoreTech "另外,我建议添加SERVICE_ACCEPT_SHUTDOWN" - SERVICE_ACCEPT_PRESHUTDOWN 和 SERVICE_ACCEPT_SHUTDOWN 不能一起使用,见Correct way to register for pre-shutdown notification from C++

标签: c windows windows-services


【解决方案1】:

发布解决我问题的答案

#include <windows.h>
#include <WinBase.h>
#include <tchar.h>
#include <strsafe.h>
#define SVC_ERROR                              ((DWORD)0xC0020001L)

SERVICE_STATUS        gSvcStatus = { 0 };
SERVICE_STATUS_HANDLE g_ServiceStatusHandle = NULL;
HANDLE                g_ServiceStopEvent = INVALID_HANDLE_VALUE;

VOID WINAPI ServiceMain(DWORD argc, LPTSTR *argv);
DWORD WINAPI SvcCtrlHandler(DWORD dwCtrl, DWORD eventType, void *eventData, void *context);
DWORD WINAPI ServiceWorkerThread(LPVOID lpParam);

#define SVCNAME  _T("SampleService")

//
// 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_INFORMATION_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);
    }
}

int _tmain(int argc, TCHAR *argv[])
{
    SvcReportEvent(_T("My Sample Service: Main: Entry"));

    SERVICE_TABLE_ENTRY ServiceTable[] =
    {
        {SVCNAME, (LPSERVICE_MAIN_FUNCTION)ServiceMain},
        {NULL, NULL}
    };

    if (StartServiceCtrlDispatcher(ServiceTable) == FALSE)
    {
        SvcReportEvent(_T("My Sample Service: Main: StartServiceCtrlDispatcher returned error"));
        return GetLastError();
    }

    SvcReportEvent(_T("My Sample Service: Main: Exit"));
    return 0;
}


VOID WINAPI ServiceMain(DWORD argc, LPTSTR *argv)
{
    DWORD Status = E_FAIL;

    SvcReportEvent(_T("My Sample Service: ServiceMain: Entry"));

    g_ServiceStatusHandle = RegisterServiceCtrlHandlerEx(
        SVCNAME, 
        SvcCtrlHandler, 
        NULL);

    if (g_ServiceStatusHandle == NULL)
    {
        SvcReportEvent(_T("My Sample Service: ServiceMain: RegisterServiceCtrlHandler returned error"));
        goto EXIT;
    }

    // Tell the service controller we are starting
    ZeroMemory(&gSvcStatus, sizeof(gSvcStatus));
    gSvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
    gSvcStatus.dwControlsAccepted = 0;
    gSvcStatus.dwCurrentState = SERVICE_START_PENDING;
    gSvcStatus.dwWin32ExitCode = 0;
    gSvcStatus.dwServiceSpecificExitCode = 0;
    gSvcStatus.dwCheckPoint = 0;

    if (SetServiceStatus(g_ServiceStatusHandle, &gSvcStatus) == FALSE)
    {
        SvcReportEvent(_T("My Sample Service: ServiceMain: SetServiceStatus returned error"));
    }

    /*
     * Perform tasks neccesary to start the service here
     */
    SvcReportEvent(_T("My Sample Service: ServiceMain: Performing Service Start Operations"));

    // Create stop event to wait on later.
    g_ServiceStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (g_ServiceStopEvent == NULL)
    {
        SvcReportEvent(_T("My Sample Service: ServiceMain: CreateEvent(g_ServiceStopEvent) returned error"));

        gSvcStatus.dwControlsAccepted = 0;
        gSvcStatus.dwCurrentState = SERVICE_STOPPED;
        gSvcStatus.dwWin32ExitCode = GetLastError();
        gSvcStatus.dwCheckPoint = 1;

        if (SetServiceStatus(g_ServiceStatusHandle, &gSvcStatus) == FALSE)
        {
            SvcReportEvent(_T("My Sample Service: ServiceMain: SetServiceStatus returned error"));
        }
        goto EXIT;
    }

    // Tell the service controller we are started
    gSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PRESHUTDOWN;
    gSvcStatus.dwCurrentState = SERVICE_RUNNING;
    gSvcStatus.dwWin32ExitCode = 0;
    gSvcStatus.dwCheckPoint = 0;

    if (SetServiceStatus(g_ServiceStatusHandle, &gSvcStatus) == FALSE)
    {
        SvcReportEvent(_T("My Sample Service: ServiceMain: SetServiceStatus returned error"));
    }

    // Start the thread that will perform the main task of the service
    HANDLE hThread = CreateThread(NULL, 0, ServiceWorkerThread, NULL, 0, NULL);

    SvcReportEvent(_T("My Sample Service: ServiceMain: Waiting for Worker Thread to complete"));

    // Wait until our worker thread exits effectively signaling that the service needs to stop
    WaitForSingleObject(hThread, INFINITE);

    SvcReportEvent(_T("My Sample Service: ServiceMain: Worker Thread Stop Event signaled"));


    /*
     * Perform any cleanup tasks
     */
    SvcReportEvent(_T("My Sample Service: ServiceMain: Performing Cleanup Operations"));

    CloseHandle(g_ServiceStopEvent);

    gSvcStatus.dwControlsAccepted = 0;
    gSvcStatus.dwCurrentState = SERVICE_STOPPED;
    gSvcStatus.dwWin32ExitCode = 0;
    gSvcStatus.dwCheckPoint = 3;

    if (SetServiceStatus(g_ServiceStatusHandle, &gSvcStatus) == FALSE)
    {
        SvcReportEvent(_T("My Sample Service: ServiceMain: SetServiceStatus returned error"));
    }

EXIT:
    SvcReportEvent(_T("My Sample Service: ServiceMain: Exit"));

    return;
}


DWORD WINAPI SvcCtrlHandler(DWORD dwCtrl, DWORD eventType, void *eventData, void *context)
{
    if (dwCtrl == SERVICE_CONTROL_STOP)
    {
        SvcReportEvent((LPTSTR)TEXT("In function SvcCtrlHandler in condition SERVICE_CONTROL_STOP"));
        gSvcStatus.dwWin32ExitCode = 0;
        gSvcStatus.dwCurrentState = SERVICE_STOPPED;
    }
    else if (dwCtrl == SERVICE_CONTROL_PRESHUTDOWN)
    {
        SvcReportEvent((LPTSTR)TEXT("In function SvcCtrlHandler in condition SERVICE_CONTROL_PRESHUTDOWN"));
        gSvcStatus.dwWin32ExitCode = 0;
        gSvcStatus.dwCurrentState = SERVICE_STOPPED;
    }
    SetServiceStatus(g_ServiceStatusHandle, &gSvcStatus);
    return NO_ERROR;
}


DWORD WINAPI ServiceWorkerThread(LPVOID lpParam)
{
    SvcReportEvent(_T("My Sample Service: ServiceWorkerThread: Entry"));

    //  Periodically check if the service has been requested to stop
    while (WaitForSingleObject(g_ServiceStopEvent, 0) != WAIT_OBJECT_0)
    {
        /*
         * Perform main service function here
         */

         //  Simulate some work by sleeping
        for (size_t i = 0; i < 1000000; i++)
        {
            char sentence[] = "Hello World";
            FILE *fptr;
            fptr = fopen("C:\\test\\program.txt", "a");
            if (fptr == NULL)
            {
                printf("Error!");
            }
            else
            {
                fprintf(fptr, "%s", sentence);
                fclose(fptr);
            }
            Sleep(3000);
        }
    }

    SvcReportEvent(_T("My Sample Service: ServiceWorkerThread: Exit"));

    return ERROR_SUCCESS;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-10
    • 2014-05-25
    • 1970-01-01
    相关资源
    最近更新 更多