JNAからWindows Event Log APIを呼び出してイベントログを取得してみる(ブックマーク利用)
Windows Event Log API にはブックマークなる機能があるらしいので、これを JNA 経由で使ってみました。
長ったらしいですが、以下のような感じになりました。
ブックマークは XML としてファイルに保存するのですが、初回処理などでブックマークがなかった場合は「ハンドルの先頭にシークして処理、その後、ブックマークを作成する」、ブックマークがある場合は「ブックマークへシークして処理、その後、ブックマークを更新する」という感じになっています。いろいろまずい箇所がある気がしますが、とりあえずこんなところとしておきます・・・
・WindowsEventTest.java
package com.example.jna.winevent; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import com.sun.jna.Library; import com.sun.jna.Memory; import com.sun.jna.Native; import com.sun.jna.Pointer; import com.sun.jna.WString; import com.sun.jna.platform.win32.WinDef; import com.sun.jna.platform.win32.WinNT.HANDLE; import com.sun.jna.ptr.IntByReference; public class WindowsEventTest { public interface Kernel32 extends Library { Kernel32 INSTANCE = (Kernel32) Native.loadLibrary("kernel32", Kernel32.class); int GetLastError(); Pointer LocalFree(Pointer hMem); } public interface Wevtapi extends Library { Wevtapi INSTANCE = (Wevtapi) Native.loadLibrary("wevtapi", Wevtapi.class); // EVT_QUERY_FLAGS public final int EvtQueryChannelPath = 0x1; public final int EvtQueryFilePath = 0x2; public final int EvtQueryForwardDirection = 0x100; public final int EvtQueryReverseDirection = 0x200; public final int EvtQueryTolerateQueryErrors = 0x1000; // EVT_SEEK_FLAGS public final int EvtSeekRelativeToFirst = 1; public final int EvtSeekRelativeToLast = 2; public final int EvtSeekRelativeToCurrent = 3; public final int EvtSeekRelativeToBookmark = 4; public final int EvtSeekOriginMask = 7; public final int EvtSeekStrict = 0x10000; // EVT_RENDER_FLAGS public final int EvtRenderEventValues = 0; public final int EvtRenderEventXml = 1; public final int EvtRenderBookmark = 2; HANDLE EvtQuery(HANDLE Session, WString Path, WString Query, int Flags); boolean EvtSeek(HANDLE ResultSet, WinDef.LONGLONG Position, HANDLE Bookmark, int Timeout, int Flags); boolean EvtNext(HANDLE ResultSet, int EventArraySize, HANDLE[] EventArray, int Timeout, int Flags, IntByReference Returned); boolean EvtRender(HANDLE Context, HANDLE Fragment, int Flags, int BufferSize, Pointer Buffer, IntByReference BufferUsed, IntByReference PropertyCount); HANDLE EvtCreateBookmark(WString BookmarkXml); boolean EvtUpdateBookmark(HANDLE Bookmark, HANDLE Event); boolean EvtClose(HANDLE h); } private String fileName; public WindowsEventTest(String fileName) { this.fileName = fileName; } public static void main(String[] args) { String fileName = "bookmark.xml"; String query = "<QueryList><Query><Select Path='Application'>*[System[(EventID=100)]]</Select><Select Path='System'>*[System[(EventID=100)]]</Select></Query></QueryList>"; WindowsEventTest wet = new WindowsEventTest(fileName); String bookmarkString = wet.getBookmartString(fileName); String eventLog = wet.getEventLog(query, bookmarkString); System.out.println(eventLog); } private String getEventLog(String query, String bookmarkString) { StringBuffer eventLog = new StringBuffer(); HANDLE hResults = null; HANDLE hBookmark = null; try { WString wQuery = new WString(query); hResults = Wevtapi.INSTANCE.EvtQuery(null, null, wQuery, Wevtapi.INSTANCE.EvtQueryChannelPath | Wevtapi.INSTANCE.EvtQueryTolerateQueryErrors); if (hResults == null) throw new Exception("EvtQuery Error errorCode=" + getLastErrorWrapper()); //ブックマークがあればブックマークへシーク if (!bookmarkString.isEmpty()) { hBookmark = Wevtapi.INSTANCE.EvtCreateBookmark(new WString(bookmarkString)); if (hBookmark == null) throw new Exception("EvtCreateBookmark Error errorCode=" + getLastErrorWrapper()); boolean ret = Wevtapi.INSTANCE.EvtSeek(hResults, new WinDef.LONGLONG(0), hBookmark, 0, Wevtapi.INSTANCE.EvtSeekRelativeToBookmark); if (!ret) throw new Exception("EvtSeek Error errorCode=" + getLastErrorWrapper()); //ブックマークがなければハンドルの先頭へシーク } else { boolean ret = Wevtapi.INSTANCE.EvtSeek(hResults, new WinDef.LONGLONG(0), null, 0, Wevtapi.INSTANCE.EvtSeekRelativeToFirst); if (!ret) throw new Exception("EvtSeek Error errorCode=" + getLastErrorWrapper()); } HANDLE[] handles = new HANDLE[100]; IntByReference intRef = new IntByReference(0); while (true) { if (!Wevtapi.INSTANCE.EvtNext(hResults, 100, handles, 0, 0, intRef)) { if (getLastErrorWrapper() == 259) break; else throw new Exception("EvtNext Error errorCode=" + getLastErrorWrapper()); } else { //ブックマークへシークした場合、前回取得した分は読み飛ばすため1始まり for (int i = hBookmark != null ? 1 : 0; i < intRef.getValue(); i++) { eventLog.append(renderEvent(handles[i], Wevtapi.INSTANCE.EvtRenderEventXml)); Wevtapi.INSTANCE.EvtClose(handles[i]); handles[i] = null; } } } //ブックマーク保存処理 if (!Wevtapi.INSTANCE.EvtSeek(hResults, new WinDef.LONGLONG(0), null, 0, Wevtapi.INSTANCE.EvtSeekRelativeToLast)) throw new Exception("EvtSeek Error errorCode=" + getLastErrorWrapper()); HANDLE[] saveHandles = new HANDLE[1]; if (!Wevtapi.INSTANCE.EvtNext(hResults, 1, saveHandles, 0, 0, intRef)) throw new Exception("EvtNext Error errorCode=" + getLastErrorWrapper()); if (hBookmark == null) { hBookmark = Wevtapi.INSTANCE.EvtCreateBookmark(null); if (hBookmark == null) throw new Exception("EvtCreateBookmark Error errorCode=" + getLastErrorWrapper()); } if (!Wevtapi.INSTANCE.EvtUpdateBookmark(hBookmark, saveHandles[0])) throw new Exception("EvtUpdateBookmark Error errorCode=" + getLastErrorWrapper()); String saveBookmarkString = renderEvent(hBookmark, Wevtapi.INSTANCE.EvtRenderBookmark); saveBookmarkFile(fileName, saveBookmarkString); } catch (Exception e) { e.printStackTrace(); } finally { if (hResults != null) Wevtapi.INSTANCE.EvtClose(hResults); if (hBookmark != null) Wevtapi.INSTANCE.EvtClose(hBookmark); } return eventLog.toString(); } private String renderEvent(HANDLE h, int EvtRenderFlg) throws Exception { int bufferSize = 0; Pointer buffer = null; IntByReference bufferUsed = new IntByReference(0); IntByReference propertyCount = new IntByReference(0); if (!Wevtapi.INSTANCE.EvtRender(null, h, EvtRenderFlg, bufferSize, buffer, bufferUsed, propertyCount)) { if (getLastErrorWrapper() == 122) { bufferSize = bufferUsed.getValue(); buffer = new Memory(bufferSize); if (buffer != null) Wevtapi.INSTANCE.EvtRender(null, h, EvtRenderFlg, bufferSize, buffer, bufferUsed, propertyCount); else throw new Exception("malloc Error errorCode=" + getLastErrorWrapper()); } if (0 != getLastErrorWrapper()) throw new Exception("EvtRender Error errorCode=" + getLastErrorWrapper()); } return buffer.getWideString(0); } private String getBookmartString(String fileName) { StringBuffer sb = new StringBuffer(); BufferedReader br = null; try { br = new BufferedReader(new FileReader(fileName)); String line; while ((line = br.readLine()) != null) { sb.append(line); } } catch (IOException e) { System.err.println("getBookmartString Error msg=" + e.getMessage()); } finally { try { if (br != null) br.close(); } catch (IOException e) { System.err.println("getBookmartString Error msg=" + e.getMessage()); } } return sb.toString(); } private void saveBookmarkFile(String fileName, String bookmarkString) { BufferedWriter bw = null; try { bw = new BufferedWriter(new FileWriter("bookmark.xml")); bw.write(bookmarkString); } catch (IOException e) { System.err.println("saveBookmark Error msg=" + e.getMessage()); } finally { try { if (bw != null) bw.close(); } catch (IOException e) { System.err.println("saveBookmark Error msg=" + e.getMessage()); } } } private int getLastErrorWrapper() { return Kernel32.INSTANCE.GetLastError(); } }
うまくいくと以下のような bookmark.xml が作成されるはずです。
<BookmarkList Direction='backward'> <Bookmark Channel='Application' RecordId='4885'/> <Bookmark Channel='System' RecordId='5784' IsCurrent='true'/> </BookmarkList>
各関数のリファレンスは以下にあります。
Windows Event Log Functions (Windows)
以上です。
[環境情報]
Windows 10
Java SE 8 Update 92
JNA 4.2.2