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








![[C#] エクスプローラ風のツリービュー表示](https://www.osadasoft.com/wp-content/uploads/2018/11/00-120x120.png)