【问题标题】:Reading Task Scheduler events from Event Log从事件日志中读取任务计划程序事件
【发布时间】:2019-05-02 23:07:42
【问题描述】:

我想列出 C/C++ 中任务计划程序的自定义任务的所有已执行运行。因此,我访问事件日志并尝试提取 TaskScheduler 日志条目,如下所示(为简单起见,删除了所有错误处理):

HANDLE hEv = OpenEventLogA(NULL, "Microsoft-Windows-TaskScheduler/Operational");
DWORD nrRead = 0x10000, status = ERROR_SUCCESS, nrMin = 0, nrDone;
PBYTE buf = (PBYTE) malloc(nrRead);
while (status == ERROR_SUCCESS) {
  if (!ReadEventLog(hEv, EVENTLOG_SEQUENTIAL_READ | EVENTLOG_BACKWARDS_READ, 
    0, buf, nrRead, &nrDone, &nrMin)) status = GetLastError();

  for (PBYTE pRec = buf, pEnd = buf + nrRead; pRec < pEnd;) {
    (void) (pRec + sizeof(EVENTLOGRECORD));     // Store record
    pRec += ((PEVENTLOGRECORD) pRec)->Length;
    if (((PEVENTLOGRECORD) pRec)->Length == 0) break; // Avoid endless loop
  }
}

实际上,我能够从日志中读取事件(例如 WiFi 日志)。但我无法打开 TaskScheduler 日志。然后它会按照文档中的说明进行操作并返回到应用程序日志。

我为日志名称尝试了不同的字符串:

  • 事件日志中的协议名称
  • 协议路径以斜线分隔
  • 英文和本地化名称

似乎都不起作用。那么如何打开TaskScheduler 日志呢?日志名称是否本地化,是否需要根据当前操作系统语言进行调整?还有其他方法可以检索 TaskScheduler 执行吗?

【问题讨论】:

  • 您是否也尝试过 Vista 引入的较新的事件日志 API(Evt* 函数)?
  • 其实我不知道有更新的API。我会搜索并尝试一下。感谢您的信息!

标签: c winapi event-log taskscheduler


【解决方案1】:

我已经尝试过你的代码,似乎 OpenEventLog 只能打开一些经常使用的日志(不确定)。但是,还有另一种列出 TaskScheduler 事件的方法:

使用EvtSubscribe()添加回调函数,查询记录时,以XML格式打印出来。下面是代码示例:

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

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

const int SIZE_DATA = 4096;
TCHAR XMLDataCurrent[SIZE_DATA];
TCHAR XMLDataUser[SIZE_DATA];

#define ARRAY_SIZE 10
#define TIMEOUT 1000  // 1 second; Set and use in place of INFINITE in EvtNext call

DWORD PrintEvent(EVT_HANDLE hEvent); // Shown in the Rendering Events topic
DWORD WINAPI SubscriptionCallback(EVT_SUBSCRIBE_NOTIFY_ACTION action, PVOID pContext, EVT_HANDLE hEvent);

void main(void)
{
    DWORD status = ERROR_SUCCESS;
    EVT_HANDLE hResults = NULL;

    //hResults = EvtQuery(NULL, pwsPath, pwsQuery, EvtQueryChannelPath );// EvtQueryReverseDirection);
    hResults = EvtSubscribe(NULL, NULL, L"Microsoft-Windows-TaskScheduler/Operational", NULL, NULL, NULL, (EVT_SUBSCRIBE_CALLBACK)SubscriptionCallback, EvtSubscribeStartAtOldestRecord);
    if (NULL == hResults)
    {
        status = GetLastError();
        if (ERROR_EVT_CHANNEL_NOT_FOUND == status)
            wprintf(L"The channel was not found.\n");
        else if (ERROR_EVT_INVALID_QUERY == status)
            // You can call the EvtGetExtendedStatus function to try to get 
            // additional information as to what is wrong with the query.
            wprintf(L"The query is not valid.\n");
        else
            wprintf(L"EvtQuery failed with %lu.\n", status);
    }
    Sleep(1000);

cleanup:

    if (hResults)
        EvtClose(hResults);

}
// The callback that receives the events that match the query criteria. 
DWORD WINAPI SubscriptionCallback(EVT_SUBSCRIBE_NOTIFY_ACTION action, PVOID pContext, EVT_HANDLE hEvent)
{
    UNREFERENCED_PARAMETER(pContext);

    DWORD status = ERROR_SUCCESS;

    switch (action)
    {
        // You should only get the EvtSubscribeActionError action if your subscription flags 
        // includes EvtSubscribeStrict and the channel contains missing event records.
    case EvtSubscribeActionError:
        if (ERROR_EVT_QUERY_RESULT_STALE == (DWORD)hEvent)
        {
            wprintf(L"The subscription callback was notified that event records are missing.\n");
            // Handle if this is an issue for your application.
        }
        else
        {
            wprintf(L"The subscription callback received the following Win32 error: %lu\n", (DWORD)hEvent);
        }
        break;

    case EvtSubscribeActionDeliver:
        if (ERROR_SUCCESS != (status = PrintEvent(hEvent)))
        {
            goto cleanup;
        }
        break;

    default:
        wprintf(L"SubscriptionCallback: Unknown action.\n");
    }

cleanup:

    if (ERROR_SUCCESS != status)
    {
        // End subscription - Use some kind of IPC mechanism to signal
        // your application to close the subscription handle.
    }

    return status; // The service ignores the returned status.
}


DWORD PrintEvent(EVT_HANDLE hEvent)
{
    DWORD status = ERROR_SUCCESS;
    DWORD dwBufferSize = 0;
    DWORD dwBufferUsed = 0;
    DWORD dwPropertyCount = 0;
    LPWSTR pRenderedContent = NULL;

    if (!EvtRender(NULL, hEvent, EvtRenderEventXml, dwBufferSize, pRenderedContent, &dwBufferUsed, &dwPropertyCount))
    {
        if (ERROR_INSUFFICIENT_BUFFER == (status = GetLastError()))
        {
            dwBufferSize = dwBufferUsed;
            pRenderedContent = (LPWSTR)malloc(dwBufferSize);
            if (pRenderedContent)
            {
                EvtRender(NULL, hEvent, EvtRenderEventXml, dwBufferSize, pRenderedContent, &dwBufferUsed, &dwPropertyCount);
            }
            else
            {
                wprintf(L"malloc failed\n");
                status = ERROR_OUTOFMEMORY;
                goto cleanup;
            }
        }

        if (ERROR_SUCCESS != (status = GetLastError()))
        {
            wprintf(L"EvtRender failed with %d\n", status);
            goto cleanup;
        }
    }

    ZeroMemory(XMLDataCurrent, SIZE_DATA);
    lstrcpyW(XMLDataCurrent, pRenderedContent);

    wprintf(L"EvtRender data %s\n", XMLDataCurrent);

cleanup:

    if (pRenderedContent)
        free(pRenderedContent);

    return status;
}

希望对你有帮助!

【讨论】:

    【解决方案2】:

    感谢@zett42 为我指明正确的方向,感谢@Drake Wu 提供详细的代码示例。但由于我不需要任何未来事件或异步检索,我现在实现了一个简单的同步函数:

    #define EVT_SIZE 10
    
    int GetEvents(LPCWSTR query) {
        DWORD xmlLen = 0;
        LPCWSTR xml = NULL;
        EVT_HANDLE hQuery = EvtQuery(NULL, NULL, query, EvtQueryChannelPath | EvtQueryTolerateQueryErrors));
    
        while (true) {
            EVT_HANDLE hEv[EVT_SIZE];
            DWORD dwReturned = 0;
            if (!EvtNext(hQuery, EVT_SIZE, hEv, INFINITE, 0, &dwReturned)) return 0;
    
            // Loop over all events
            for (DWORD i = 0; i < dwReturned; i++) {
                DWORD nrRead = 0, nrProps = 0;
    
                if (!EvtRender(NULL, hEv[i], EvtRenderEventXml, xmlLen, xml, &nrRead, &nrProps)) {
                    if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
                        xmlLen = nrRead;
                        xml = (LPWSTR) realloc(xml, xmlLen);
                        if (xml) {
                            EvtRender(NULL, hEv[i], EvtRenderEventXml, xmlLen, xml, &nrRead, &nrProps);
                        } else {
                            return -1;
                        }
                    }
                    if (GetLastError() != ERROR_SUCCESS) return -1;
                }
    
                // Store event data
    
                EvtClose(hEv[i]);
                hEv[i] = NULL;
            }
        }
        return 0;
    }
    

    我再次删除了大多数错误处理以简化示例。 Evt* 函数确实适用于检索 TaskScheduler 数据(独立于语言)。

    【讨论】:

      猜你喜欢
      • 2023-03-15
      • 2021-02-04
      • 1970-01-01
      • 2015-08-28
      • 1970-01-01
      • 2017-09-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多