Windows C 服务未在 PreShutdown 事件中写入文件
Windows C service not writing to file in PreShutdown event
我正在开发一个用 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;
}
观察:
- 当我重新启动计算机时,没有写入 Eventviewer 日志
- 当我手动停止服务时,日志被写入事件查看器:
In function SvcCtrlHandler in condition SERVICE_CONTROL_STOP
注意:Link我在写服务的时候提到了:https://docs.microsoft.com/en-us/windows/win32/services/the-complete-service-sample?redirectedfrom=MSDN
发布解决我问题的答案
#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;
}
我正在开发一个用 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;
}
观察:
- 当我重新启动计算机时,没有写入 Eventviewer 日志
- 当我手动停止服务时,日志被写入事件查看器:
In function SvcCtrlHandler in condition SERVICE_CONTROL_STOP
注意:Link我在写服务的时候提到了:https://docs.microsoft.com/en-us/windows/win32/services/the-complete-service-sample?redirectedfrom=MSDN
发布解决我问题的答案
#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;
}