Skip to content

Commit

Permalink
Updated live logging
Browse files Browse the repository at this point in the history
  • Loading branch information
jschick04 authored and bill-long committed Nov 11, 2024
1 parent 2f4802e commit 8e9e5c5
Show file tree
Hide file tree
Showing 10 changed files with 566 additions and 276 deletions.
145 changes: 144 additions & 1 deletion src/EventLogExpert.Eventing/Helpers/EventMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// // Licensed under the MIT License.

using EventLogExpert.Eventing.Models;
using EventLogExpert.Eventing.Reader;
using Microsoft.Win32.SafeHandles;
using System.Runtime.InteropServices;
using System.Security.Principal;

Expand Down Expand Up @@ -100,6 +102,17 @@ internal enum EvtRenderFlags
Bookmark
}

[Flags]
internal enum EvtSubscribeFlags
{
ToFutureEvents = 1,
StartAtOldestRecord = 2,
StartAfterBookmark = 3,
OriginMask = 3,
TolerateQueryErrors = 0x1000,
Strict = 0x10000
}

internal enum EvtSystemPropertyId
{
ProviderName,
Expand Down Expand Up @@ -255,6 +268,10 @@ internal static partial class EventMethods
[return: MarshalAs(UnmanagedType.Bool)]
internal static partial bool EvtClose(IntPtr handle);

/// <summary>Creates a bookmark that identifies an event in a channel</summary>
[LibraryImport(EventLogApi, SetLastError = true)]
internal static partial EventLogHandle EvtCreateBookmark([MarshalAs(UnmanagedType.LPWStr)] string? bookmarkXml);

/// <summary>Creates a context that specifies the information in the event that you want to render</summary>
[LibraryImport(EventLogApi, SetLastError = true)]
internal static partial EventLogHandle EvtCreateRenderContext(
Expand Down Expand Up @@ -392,7 +409,7 @@ internal static partial EventLogHandle EvtQuery(
EventLogHandle session,
[MarshalAs(UnmanagedType.LPWStr)] string path,
[MarshalAs(UnmanagedType.LPWStr)] string? query,
int flags);
PathType flags);

/// <summary>Renders an XML fragment base on the render context that you specify</summary>
[LibraryImport(EventLogApi, SetLastError = true)]
Expand All @@ -406,6 +423,25 @@ internal static partial bool EvtRender(
out int bufferUsed,
out int propertyCount);

/// <summary>
/// Creates a subscription that will receive current and future events from a channel or log file that match the
/// specified query criteria
/// </summary>
[LibraryImport(EventLogApi, SetLastError = true)]
internal static partial EventLogHandle EvtSubscribe(
EventLogHandle session,
SafeWaitHandle signalEvent,
[MarshalAs(UnmanagedType.LPWStr)] string channelPath,
[MarshalAs(UnmanagedType.LPWStr)] string? query,
EventLogHandle bookmark,
IntPtr context,
IntPtr callback,
EvtSubscribeFlags flags);

[LibraryImport(EventLogApi, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static partial bool EvtUpdateBookmark(EventLogHandle bookmark, EventLogHandle @event);

/// <summary>Formats a message string</summary>
/// <param name="publisherMetadataHandle">Handle returned from <see cref="EvtOpenPublisherMetadata" /></param>
/// <param name="messageId">The resource identifier of the message string that you want formated</param>
Expand Down Expand Up @@ -579,6 +615,113 @@ internal static int GetObjectArraySize(EventLogHandle array)
return size;
}

internal static EventRecord RenderEvent(EventLogHandle eventHandle, EvtRenderFlags flag)
{
IntPtr buffer = IntPtr.Zero;

try
{
bool success = EvtRender(
EventLogSession.GlobalSession.SystemRenderContext,
eventHandle,
flag,
0,
IntPtr.Zero,
out int bufferUsed,
out int propertyCount);

int error = Marshal.GetLastWin32Error();

if (!success && error != Interop.ERROR_INSUFFICIENT_BUFFER)
{
ThrowEventLogException(error);
}

buffer = Marshal.AllocHGlobal(bufferUsed);

success = EvtRender(
EventLogSession.GlobalSession.SystemRenderContext,
eventHandle,
flag,
bufferUsed,
buffer,
out bufferUsed,
out propertyCount);

error = Marshal.GetLastWin32Error();

if (!success)
{
ThrowEventLogException(error);
}

return GetEventRecord(buffer, propertyCount);
}
finally
{
Marshal.FreeHGlobal(buffer);
}
}

internal static IList<object> RenderEventProperties(EventLogHandle eventHandle)
{
IntPtr buffer = IntPtr.Zero;

try
{
bool success = EvtRender(
EventLogSession.GlobalSession.UserRenderContext,
eventHandle,
EvtRenderFlags.EventValues,
0,
IntPtr.Zero,
out int bufferUsed,
out int propertyCount);

int error = Marshal.GetLastWin32Error();

if (!success && error != Interop.ERROR_INSUFFICIENT_BUFFER)
{
ThrowEventLogException(error);
}

buffer = Marshal.AllocHGlobal(bufferUsed);

success = EvtRender(
EventLogSession.GlobalSession.UserRenderContext,
eventHandle,
EvtRenderFlags.EventValues,
bufferUsed,
buffer,
out bufferUsed,
out propertyCount);

error = Marshal.GetLastWin32Error();

if (!success)
{
ThrowEventLogException(error);
}

List<object> properties = [];

if (propertyCount <= 0) { return properties; }

for (int i = 0; i < propertyCount; i++)
{
var property = Marshal.PtrToStructure<EvtVariant>(buffer + (i * Marshal.SizeOf<EvtVariant>()));

properties.Add(ConvertVariant(property) ?? throw new InvalidDataException());
}

return properties;
}
finally
{
Marshal.FreeHGlobal(buffer);
}
}

internal static void ThrowEventLogException(int error)
{
var message = ResolverMethods.GetErrorMessage((uint)Converter.HResultFromWin32(error));
Expand Down
1 change: 1 addition & 0 deletions src/EventLogExpert.Eventing/Helpers/Interop.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ internal static class Interop
internal const int ERROR_ACCESS_DENIED = 5;
internal const int ERROR_INVALID_HANDLE = 6;
internal const int ERROR_INVALID_DATA = 13;
internal const int ERROR_INVALID_PARAMETER = 87;

internal const int ERROR_INSUFFICIENT_BUFFER = 122;
internal const int ERROR_NO_MORE_ITEMS = 259;
Expand Down
9 changes: 7 additions & 2 deletions src/EventLogExpert.Eventing/Models/EventLogHandle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,18 @@ namespace EventLogExpert.Eventing.Models;

internal sealed partial class EventLogHandle : SafeHandleZeroOrMinusOneIsInvalid
{
// Must be public for P/Invoke to work
public EventLogHandle() : base(true) { }

internal EventLogHandle(IntPtr handle) : base(true)
{
SetHandle(handle);
}

// Must be public for P/Invoke to work
public EventLogHandle() : base(true) { }
internal EventLogHandle(IntPtr handle, bool ownsHandle) : base(ownsHandle)
{
SetHandle(handle);
}

internal static EventLogHandle Zero => new();

Expand Down
4 changes: 4 additions & 0 deletions src/EventLogExpert.Eventing/Models/EventRecord.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,8 @@ public sealed record EventRecord
public byte? Version { get; set; }

public string? Xml { get; set; }

public string? Error { get; set; }

public bool IsSuccess => string.IsNullOrEmpty(Error);
}
5 changes: 5 additions & 0 deletions src/EventLogExpert.Eventing/Providers/ProviderMetadata.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ internal ProviderMetadata(string providerName)
}
}

~ProviderMetadata()
{
Dispose(disposing: false);
}

public IDictionary<uint, string> Channels
{
get
Expand Down
Loading

0 comments on commit 8e9e5c5

Please sign in to comment.