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で動作確認)
ビジネスインパクト大とかで、指摘されたのか分からないけど。
まぁ、でも古い使い方っぽいので、やっぱ新しい方に直しておいた方が無難ですね。。