Windows10 April 2018 Update適用でC#からイベントログ取得がうまくいかない

イベントログ取得できない

Windows10の大型アップデート「April 2018 Update」(バージョン1803)を適用すると、
C#から、うまくイベントログを拾えなくなってしまった。

イベントログ取得できない

例えば、以下のソースを実行すると、イベントログを途中まで読み込んで
勝手にループを抜けてしまう。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;

namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            EventLog evlog = new EventLog("System", ".");
            Debug.WriteLine("件数:" + evlog.Entries.Count);
            foreach (EventLogEntry et in evlog.Entries)
            {
                Debug.WriteLine(et.TimeGenerated);
            }
        }
    }
}

実行結果:

件数:2433
2018/05/11 20:10:55
2018/05/11 20:11:25
2018/05/11 20:11:25
2018/05/11 20:10:55
:
2018/05/11 20:13:16
'WindowsFormsApp1.exe' (CLR v4.0.30319: WindowsFormsApp1.exe): 'C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.resources\v4.0_4.0.0.0_ja_b77a5c561934e089\System.resources.dll' が読み込まれました。モジュールがシンボルなしでビルドされました。
例外がスローされました: 'System.InvalidOperationException' (System.dll の中)
'WindowsFormsApp1.exe' (CLR v4.0.30319: WindowsFormsApp1.exe): 'C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\mscorlib.resources\v4.0_4.0.0.0_ja_b77a5c561934e089\mscorlib.resources.dll' が読み込まれました。モジュールがシンボルなしでビルドされました。
プログラム '[3968] WindowsFormsApp1.exe' はコード 0 (0x0) で終了しました。

⇒イベントログ2433件あるのに、123件しか取得できない。

 
 

今度は、途中で抜けないようforeachでなく、for文で件数分しっかり回し、
しかも、例外が出ても強制的にループを回してみる。

EventLog evlog = new EventLog("System", ".");

for (int i = 0; i < evlog.Entries.Count; i++)
{
    try
    {
        Debug.WriteLine(i.ToString("[0] ") + evlog.Entries[i].TimeGenerated);
    }
    catch (System.ArgumentException ex)
    {
        Debug.WriteLine(ex.Message);
    }
}

 

実行結果:

[0] 2018/05/11 20:10:55
[1] 2018/05/11 20:11:25
[2] 2018/05/11 20:11:25
:
[122] 2018/05/11 20:13:16
'WindowsFormsApp1.exe' (CLR v4.0.30319: WindowsFormsApp1.exe): 'C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.resources\v4.0_4.0.0.0_ja_b77a5c561934e089\System.resources.dll' が読み込まれました。モジュールがシンボルなしでビルドされました。
例外がスローされました: 'System.InvalidOperationException' (System.dll の中)
例外がスローされました: 'System.ArgumentException' (System.dll の中)
[123] インデックス 123 が境界を越えています。
[124] 1970/03/28 20:20:03
[125] 1970/03/28 20:20:03
[126] 1970/03/28 20:20:03
:
[2431] 1970/03/28 20:20:03
[2432] 1970/03/28 20:20:03

⇒やっぱり123個目で例外発生。
 catchして、それ以降の配列に何が入っているか表示してみると、1970/3/28と
 絶対あり得ないイベントログの日付が入っている。
 EventLogのEntriesとして配列は用意されたが、まともなデータが格納されていない。

 
 

これ、Updateによるバグだよね?
それとも仕様変更(非互換)?

回避できるのかな。。

自作ソフトの勤務時間取得ツールで、イベントログ読み込んでいるので
この仕様変更は非常に困る。

バグってことで、またWindows Updateで直らないかなぁ。

 
<追記>

ダメモトでシステムのイベントログを全削除してみた。
イベントログの削除
イベントビューアーの削除確認

けど、やっぱり駄目。
今度は、削除後に追加されたイベントも取得できない。

なので、まともに取得する方法は諦め、Windows PowerShellのGet-WinEventで取得する方法で回避。

PowerShellを使うため、先頭で以下を宣言。

using System.Management.Automation;

更に、参照で以下のdllを追加。
格納先例:
C:\Program Files (x86)\Reference Assemblies\Microsoft\WindowsPowerShell\3.0\System.Management.Automation.dll

で、以下のような処理。

string machineName = "localhost";
string targeEvtLog = "system";

RunspaceInvoke rInv = new RunspaceInvoke();
System.Collections.ObjectModel.Collection<PSObject> events = rInv.Invoke(@"Get-WinEvent " + targeEvtLog + " -ComputerName " + machineName + " |select-object TimeCreated,ID|sort-object TimeCreated");

foreach (var ev in events)
{
    Debug.WriteLine(
        String.Format(
            "{0} - {1}",
            ev.Properties["TimeCreated"].Value,
            ev.Properties["ID"].Value
        )
    );
}

 

出力結果:

2018/06/01 20:01:45 - 104
2018/06/01 20:01:49 - 6105
2018/06/01 20:02:07 - 6105
2018/06/01 20:02:20 - 16
2018/06/01 20:02:21 - 43
:
2018/06/02 15:27:48 - 37
2018/06/02 15:27:54 - 1
2018/06/02 15:27:54 - 35
2018/06/02 15:27:54 - 1
2018/06/02 15:29:47 - 10016
2018/06/02 15:33:58 - 10016

これで解決!

と思ったけど、machineNameを別マシンに書き換えたら失敗。
そりゃそうだ。
IDもパスワードも指定していないや。

そこも調べないと。
でも、ローカルPCだけならこれでOK。

それでも、やっぱり、こんな修正方法は嫌だな。
dllも多分配布しなきゃならないだろうし。

もうちょっと待ってればWindows updateで修正されるかなぁ。

 
 
<そして更に追記>
どうも、修正されないっぽい。これはおかしい。
バグじゃなくて仕様(非互換)なのかも。
上記<追記>の方法でも動作はするんだけど、PowerShellを使わなきゃならないので、やっぱりこの方法は避けたい。

で、調べなおしてみたら、このSystem.Diagnostics.EventLogはWindows XPなど古いOS向けの形式のようで、
新しいOSでは、System.Diagnostics.Eventing.Reader.EventLogReaderを使った方が良いらしい。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Diagnostics.Eventing.Reader;

namespace ConsoleApp2
{
    class Program
    {
        static void Main(string[] args)
        {
            EventLogQuery eQuery = new EventLogQuery("System", PathType.LogName, "<QueryList><Query Id=\"0\" Path = \"System\"><Select Path=\"System\">*</Select > </Query></QueryList>");
            EventLogReader eLogReader = new EventLogReader(eQuery);
            EventRecord eRecord = eLogReader.ReadEvent();
            if (eRecord != null)
            {
                while ((eRecord = eLogReader.ReadEvent()) != null)
                {
                    Debug.WriteLine(eRecord.TimeCreated.Value);
                }
            }
        }
    }
}

そんな訳で、この方法で勤務時間取得ツールもVer3.10で直してみました。
 
 
 
 
 
 

<そして更に更に追記>
あれから1年後。
なんかやっぱりOSのバグだったようで、古いソースでも元通り動くようになっていた。
(Windows10 バージョン1903で動作確認)

ビジネスインパクト大とかで、指摘されたのか分からないけど。
まぁ、でも古い使い方っぽいので、やっぱ新しい方に直しておいた方が無難ですね。。

 
 

Tagged

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA


*