【问题标题】:How to export Windows Event log for a specific source with WMI?如何使用 WMI 导出特定源的 Windows 事件日志?
【发布时间】:2018-02-25 11:27:34
【问题描述】:

我正在尝试使用Win32_NTEventLogFile WMI 类导出Windows 事件日志的Applications 分支,但仅针对特定源对其进行过滤。 (否则日志包含太多不必要的信息。)

所以,举个例子,比如说,我需要导出所有包含MSSQL$SQLSRVR2012的记录:

我这样做:

using System.Management;

    static void Main(string[] args)
    {
        BackupEventLogFilterBySource("Application", "MSSQL$SQLSRVR2012", @"C:\Users\User\Desktop\exp.evtx");
    }


    public static void BackupEventLogFilterBySource(String logName, String applicationName, String targetFile)
    {
        ManagementScope scope = new ManagementScope(@"\\.\root\cimv2");
        scope.Options.EnablePrivileges = true;
        scope.Options.Impersonation = ImpersonationLevel.Impersonate;

        ObjectQuery query = new ObjectQuery(
            String.Format("Select * from Win32_NTEventLogFile Where LogFileName='{0}' And Sources='{1}'",
                logName, applicationName)
        );

        using (ManagementObjectSearcher search = 
            new ManagementObjectSearcher(scope, query))
        {
            foreach (ManagementObject o in search.Get())
            {
                ManagementBaseObject inParams = o.GetMethodParameters("BackupEventlog");
                inParams["ArchiveFileName"] = targetFile;
                ManagementBaseObject outParams = o.InvokeMethod("BackupEventLog", inParams, null);
                var res = outParams.Properties["ReturnValue"].Value;

                Console.Write("result=" + res + "\n");
            }
        }
    }

但该查询失败并出现以下异常:

“System.Management.ManagementException”类型的未处理异常 发生在 System.Management.dll 中

附加信息:无效查询

那么我在这里做错了什么?

【问题讨论】:

  • 我不确定,但是,如果你通过了这个文档:msdn.microsoft.com/en-us/library/aa394225(v=vs.85).aspx 它说Sources 是一个字符串类型的数组
  • @FaizanRabbani:是的,我也看到了。虽然我不确定那是什么意思?将它作为一个数组真的没有意义。
  • 它适用于多个来源,但我不知道如何将其放入查询中。

标签: c# .net wmi event-log


【解决方案1】:

用于标识Source 的内部名称可能与计算机管理 UI 中显示的名称不同。
例如,源 Winlogon,内部引用为 Microsoft-Windows-Winlogon

另外,Sources 参数也有问题,因为它是一个数组。

此修改后的方法使用Win32_NTLogEvent 而不是Win32_NTEventLogFile
我认为它更直接地指向目标。
由于我提到的原因,该查询使用LIKE '%parameter%' 过滤源。但是,可以使用带有 LogFileName 过滤器的原始方法提取所有源名称,并分析 Sources { } 数组的内容。


从日志源文件中提取的值存储在列表中。
您可以使用它的属性来创建与您在事件查看器中看到的报告相似的报告。

注意: TimeGeneratedTimeLogged 属性可以使用 ManagementDateTimeConverter 转换为 DateTime .ToDateTime Method

public class WinLogEvent
{
    public string ComputerName { get; set; }
    public string LogName { get; set; }
    public string Message { get; set; }
    public string Source { get; set; }
    public UInt16 EventCode { get; set; }
    public uint EventIdentifier { get; set; }
    public string EventType { get; set; }
    public uint RecordNumber { get; set; }
    public DateTime? TimeGenerated { get; set; }
    public DateTime? TimeLogged { get; set; }
    public byte[] Data { get; set; }
    public string[] InsertionStrings { get; set; }
}

private static EnumerationOptions GetEnumerationOptions(bool deepScan)
{
    var mOptions = new EnumerationOptions()
    {
        Rewindable = false,        //Forward only query => no caching
        ReturnImmediately = true,  //Pseudo-async result
        DirectRead = true,
        EnumerateDeep = deepScan
    };
    return mOptions;
}

private static ConnectionOptions GetConnectionOptions(string UserName, string Password, string Domain)
{
    var connOptions = new ConnectionOptions()
    {
        EnablePrivileges = true,
        Timeout = ManagementOptions.InfiniteTimeout,
        Authentication = AuthenticationLevel.PacketPrivacy,
        Impersonation = ImpersonationLevel.Default,
        Username = UserName,
        Password = Password,
        //Authority = "NTLMDOMAIN:[domain]"
        Authority = !string.IsNullOrEmpty(Domain) ? $"NTLMDOMAIN:{Domain}" : string.Empty
    };
    return connOptions;
}

public static List<WinLogEvent> BackupEventLogFilterBySource(string logName, string sourceName)
{
    List<WinLogEvent> logEvents = new List<WinLogEvent>();

    var connOptions = GetConnectionOptions(null, null, null);
    var options = GetEnumerationOptions(false);
    var scope = new ManagementScope(@"\\" + Environment.MachineName + @"\root\CIMV2", connOptions);
    scope.Connect();

    var query = new SelectQuery("SELECT * FROM Win32_NTLogEvent");
    query.Condition = $"Logfile='{logName}' AND SourceName LIKE '%{sourceName}%'";

    using (ManagementObjectSearcher moSearch = new ManagementObjectSearcher(scope, query, options))
    {
        foreach (ManagementObject eventLog in moSearch.Get())
        {
            ManagementBaseObject inParams = eventLog.GetMethodParameters("BackupEventlog");
            inParams["ArchiveFileName"] = @"D:\exp.evtx";
            ManagementBaseObject outParams = eventLog.InvokeMethod("BackupEventLog", inParams, null);
            var res = outParams.Properties["ReturnValue"].Value;

            logEvents.Add(new WinLogEvent
            {
                ComputerName = eventLog.GetPropertyValue("ComputerName")?.ToString(),
                LogName = eventLog.GetPropertyValue("Logfile")?.ToString(),
                Source = eventLog.GetPropertyValue("SourceName")?.ToString(),
                EventCode = (UInt16?)eventLog.GetPropertyValue("EventCode") ?? 0,
                EventIdentifier = (uint?)eventLog.GetPropertyValue("EventIdentifier") ?? 0,
                EventType = eventLog.GetPropertyValue("Type")?.ToString(),
                RecordNumber = (uint?)eventLog.GetPropertyValue("RecordNumber") ?? 0,
                TimeGenerated = ManagementDateTimeConverter.ToDateTime(eventLog.GetPropertyValue("TimeGenerated")?.ToString()),
                TimeLogged = ManagementDateTimeConverter.ToDateTime(eventLog.GetPropertyValue("TimeWritten")?.ToString()),
                Message = eventLog.GetPropertyValue("Message")?.ToString(),
                InsertionStrings = (string[])eventLog.GetPropertyValue("InsertionStrings") ?? null,
                Data = (byte[])eventLog.GetPropertyValue("Data") ?? null,
            });
            inParams.Dispose();
            outParams.Dispose();
        }
    }
    return logEvents;
}   //BackupEventLogFilterBySource

【讨论】:

  • 哦,非常好。谢谢!虽然我看到您的方法只是从事件日志中提取记录。没有办法让它保存在.evtx 文件中,是吗?在进行研究时,我还发现 ReadEventLog 函数可以通过 PInvoke 执行类似的操作。但似乎没有办法将其过滤到 .evtx 文件中(除了我手动操作。)
  • 也只是您的代码的旁注。在将它们转换为字符串之前,我会检查GetPropertyValue 返回的对象是否为null。在我的情况下,它会在某些条目上抛出一个空断言。
  • 事实上我不确定你想要什么样的报告。如果您需要本机格式,我会看看它。总之很有趣。尽管您收到空值错误是很奇怪的。这些是预期的,但是在调试时它们总是被正确处理,将属性设置为 null(当然,您必须知道它们的值可能为 null 才能访问这些属性)。我会进一步研究它。
  • 谢谢。是的,在我的情况下,某些条目的 Message 属性返回为 null,因此您的代码会引发异常。但这没什么大不了的。只是需要注意的事情。尽管如此,即使无法将其直接过滤到.evtx 文件中,您的代码也可以让我提取相关数据。如果迫不得已,我总是可以将其格式化为 html 文件并以这种方式呈现。它可能比内置的事件查看器更加用户友好:)
  • DateTime 格式现在可以了,Message 的来源为 null 时为 Empty。 -- 据我所知,只能备份整个(完整的)日志文件。创建它的副本,即(这就是您通常所做的)。日志文件的标记方式如果保存不正确,则会使它们无法使用。似乎没有导出部分(有效)日志的内置机制。但我会看看我能不能想出点什么来。顺便说一句,您的原始代码没有 Sources 过滤器,会创建日志文件的有效备份。
【解决方案2】:

在您的查询中,您必须将sources 替换为SourceName。为了获得正确的 WMI 查询,请使用 wbemtest,它会准确地让您知道要查询的字段。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-08
    • 2014-12-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多