diff --git a/.github/workflows/PullRequest.yml b/.github/workflows/PullRequest.yml
index 9da775a57..bc2f18fcb 100644
--- a/.github/workflows/PullRequest.yml
+++ b/.github/workflows/PullRequest.yml
@@ -26,7 +26,7 @@ jobs:
- name: Restore dependencies
run: dotnet restore EventLogExpert.slnx
- name: Build
- run: dotnet build EventLogExpert.slnx --no-restore -c Release -p:PublishReadyToRun=false -m:1
+ run: dotnet build EventLogExpert.slnx --no-restore -c Release -p:PublishReadyToRun=false -p:WindowsPackageType=None
- name: Test (unit)
shell: pwsh
run: |
diff --git a/Directory.Build.props b/Directory.Build.props
index 64a91cc37..37a2d8332 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -3,6 +3,7 @@
enableenabletrue
+ embedded$(NoWarn);CsWinRT1030
diff --git a/Directory.Build.targets b/Directory.Build.targets
new file mode 100644
index 000000000..b7fb0e0b0
--- /dev/null
+++ b/Directory.Build.targets
@@ -0,0 +1,7 @@
+
+
+
+ $(GlobalPropertiesToRemoveFromProjectReferences);RuntimeIdentifier;SelfContained
+
+
+
diff --git a/EventLogExpert.slnx b/EventLogExpert.slnx
index 6653bd557..7c20d77a0 100644
--- a/EventLogExpert.slnx
+++ b/EventLogExpert.slnx
@@ -48,14 +48,11 @@
-
-
-
diff --git a/README.md b/README.md
index a1fd7005a..8cab9bac7 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
A Windows Event Log viewer for tech support and IT professionals.
-
+
## Key features
@@ -21,20 +21,16 @@ For more information, check our [docs](docs/Home.md).
## Quick Start
-Download the `EventLogExpert_{version}_x64.appinstaller` (or the matching `EventLogExpert_{version}_x64.msix`) from the latest release and run it: .
+Download `EventLogExpert_{version}.msixbundle` from the latest release and double-click it to install: .
-The `.appinstaller` declares its dependency on the Windows App Runtime (currently `Microsoft.WindowsAppRuntime.1.7.msix`, also published in the same release) so App Installer fetches the runtime automatically on a clean machine. Updates are checked on launch.
+The bundle runs natively on both x64 and ARM64, and Windows installs the matching architecture automatically. It is self-contained (the Windows App Runtime ships inside the package), so there is no separate runtime to install. Updates are checked on launch.
-If you'd rather install the runtime manually first, grab `Microsoft.WindowsAppRuntime.1.7.msix` from the release and install it with:
+**Requirements:** Windows 11, Windows Server 2022, or Windows Server 2025 (x64 or ARM64).
-```
-Add-AppxPackage $home\Downloads\Microsoft.WindowsAppRuntime.1.7.msix
-```
-
-Then install the app:
+Prefer the command line? Install the same bundle with:
```
-Add-AppxPackage $home\Downloads\EventLogExpert_{version}_x64.msix
+Add-AppxPackage $home\Downloads\EventLogExpert_{version}.msixbundle
```
### First time setup
diff --git a/compose.yml b/compose.yml
index d99da095b..c22a02e18 100644
--- a/compose.yml
+++ b/compose.yml
@@ -39,13 +39,6 @@ services:
- --logger
- "trx;LogFileName=runtime.trx"
- eventdbtool:
- <<: *base
- command:
- - tests/Integration/EventLogExpert.EventDbTool.IntegrationTests/EventLogExpert.EventDbTool.IntegrationTests.csproj
- - --logger
- - "trx;LogFileName=eventdbtool.trx"
-
elevationhelper:
<<: *base
command:
diff --git a/docs/.images/EventLogExpert-Dashboard.png b/docs/.images/EventLogExpert-Dashboard.png
new file mode 100644
index 000000000..4ea6a2d8f
Binary files /dev/null and b/docs/.images/EventLogExpert-Dashboard.png differ
diff --git a/docs/Explorer-Context-Menu.md b/docs/Explorer-Context-Menu.md
index d87ae9059..1c12975ee 100644
--- a/docs/Explorer-Context-Menu.md
+++ b/docs/Explorer-Context-Menu.md
@@ -29,7 +29,7 @@ The MAUI receiving side:
## Build prerequisites
-The native shell extension requires the **MSVC C++ workload** to build. The **Windows 10/11 SDK (10.0.26100+)** it compiles against is delivered as NuGet packages (`Microsoft.Windows.SDK.CPP` + `Microsoft.Windows.SDK.CPP.x64`, restored to `src/packages`), so a machine-installed Windows SDK is **not** required on build agents — the OneBranch release container ships the C++ toolset but no SDK, and the packages supply the headers, import libs, winmd, and SDK tools (`rc.exe`, `mt.exe`, `midlrt.exe`). Specifically:
+The native shell extension requires the **MSVC C++ workload** to build. The **Windows 10/11 SDK (10.0.26100+)** it compiles against is delivered as NuGet packages (`Microsoft.Windows.SDK.CPP` + the per-architecture `Microsoft.Windows.SDK.CPP.x64` / `Microsoft.Windows.SDK.CPP.arm64`, restored to `src/packages`), so a machine-installed Windows SDK is **not** required on build agents - the OneBranch release container ships the C++ toolset but no SDK, and the packages supply the headers, import libs, winmd, and SDK tools (`rc.exe`, `mt.exe`, `midlrt.exe`). Specifically:
- Visual Studio 2026 (or 2022) with the **Desktop development with C++** workload
- The Windows SDK NuGet packages above (restored automatically by the `BuildExplorerExtensionNative` target; see below). A locally-installed Windows SDK 10.0.26100+ is still honored as a fallback when those packages are not restored — the vcxproj's `WindowsTargetPlatformVersion` then stays a bare `10.0` (latest installed SDK).
@@ -46,7 +46,7 @@ cd src/EventLogExpert.ExplorerExtensionNative
nuget restore packages.config -PackagesDirectory ..\packages
```
-This populates `src/packages/` with `Microsoft.Windows.CppWinRT.*`, `Microsoft.Windows.ImplementationLibrary.*` (WIL), and the Windows SDK packages `Microsoft.Windows.SDK.CPP.*` + `Microsoft.Windows.SDK.CPP.x64.*` (the SDK packages are large — several hundred MB extracted). All are gitignored.
+This populates `src/packages/` with `Microsoft.Windows.CppWinRT.*`, `Microsoft.Windows.ImplementationLibrary.*` (WIL), and the Windows SDK packages `Microsoft.Windows.SDK.CPP.*` + the per-architecture `Microsoft.Windows.SDK.CPP.x64.*` / `Microsoft.Windows.SDK.CPP.arm64.*` (the SDK packages are large - several hundred MB extracted). All are gitignored.
## Local dev install (signed MSIX)
diff --git a/docs/Filtering.md b/docs/Filtering.md
index 69ece4dce..801070076 100644
--- a/docs/Filtering.md
+++ b/docs/Filtering.md
@@ -4,7 +4,8 @@
The Filter pane sits above the event table. Every event in the active log set is evaluated against the applied filters; non-matches are hidden.
- 
+
+
The pane header carries the row-adding actions on the left and the persistence / management actions on the right:
diff --git a/docs/Saved-Filters.md b/docs/Saved-Filters.md
index 3b7921a92..41ef2a4ed 100644
--- a/docs/Saved-Filters.md
+++ b/docs/Saved-Filters.md
@@ -6,7 +6,8 @@ The Filter Library is the single persisted surface for filter reuse. It replaces
Open it from the [Filter pane](Filtering.md) header — the bookmarks icon labeled `Open Filter Library`. When the library fails to load or is still loading, the filter pane's `Recent` menu item and the `Apply Filter Set` picker surface guidance pointing back to this same button (the library does not auto-open from those flows).
- 
+
+
### Modal layout
diff --git a/docs/Settings.md b/docs/Settings.md
index 4b7f407b3..b7f3fd91a 100644
--- a/docs/Settings.md
+++ b/docs/Settings.md
@@ -6,7 +6,8 @@
`Save` writes the form fields and any pending enable/disable toggles you've made on database rows; `Exit` discards them. `Remove` and `Upgrade` are immediate side effects that persist regardless of `Save` / `Exit`. `Import Database` is also immediate, but a successful import additionally applies any pending form fields and toggles and closes the modal — make any other changes you want to keep before clicking it.
- 
+
+
### Time Zone
diff --git a/docs/Updates-And-Diagnostics.md b/docs/Updates-And-Diagnostics.md
index 3e92af55a..31fd2f1c0 100644
--- a/docs/Updates-And-Diagnostics.md
+++ b/docs/Updates-And-Diagnostics.md
@@ -29,7 +29,7 @@ The check ends with one of four user-facing alerts:
Background checks (the automatic check that runs on app start) surface `Update Available` so a fresh release is offered as soon as the app launches. The other three alerts are suppressed on background checks so a missing network at startup does not produce a popup. `Update Failure` does fire on a background check, but only after you've accepted the `Update Available` prompt and the resulting installer attempt fails; pre-prompt failures (e.g., feed unreachable, or you click `No` on the prompt and the queued-for-next-restart scheduling fails) stay silent and only land in the debug log.
-The `.appinstaller` distributed with each release also wires up app-installer-driven background update checks on every launch, so this entry is mostly for "what's the latest right now" — the app finds new releases on its own.
+Because the app already runs that background check on launch, the `Check for Updates` entry is mostly a manual "what's the latest right now" lookup; the app finds new releases on its own.
### Release Notes
@@ -39,7 +39,8 @@ Opens the Release Notes modal, which renders the markdown body of the published
Opens the Debug Log modal — the in-app view of the rolling diagnostic log written by the running session. The same log is also accessible as a file under the per-user app data directory; `View Logs` is the in-app surface.
- 
+
+
Filter bar:
diff --git a/docs/Viewing-Events.md b/docs/Viewing-Events.md
index 3ae1b300b..b9a03aa12 100644
--- a/docs/Viewing-Events.md
+++ b/docs/Viewing-Events.md
@@ -4,7 +4,8 @@
The main view has three regions: the **tab strip** (one tab per open log, plus a `Combined` tab when more than one log is open), the **event table**, and the **Details pane** (collapsible, bottom). The status bar runs along the very bottom — see [Opening Logs](Opening-Logs.md#live-log-behavior) for what it shows for live logs.
- 
+
+
### Tab strip
diff --git a/scripts/run-tests.ps1 b/scripts/run-tests.ps1
index f950eb364..9aeebc3e5 100644
--- a/scripts/run-tests.ps1
+++ b/scripts/run-tests.ps1
@@ -19,7 +19,7 @@
.EXAMPLE
./scripts/run-tests.ps1
./scripts/run-tests.ps1 -Suite eventing
- ./scripts/run-tests.ps1 -Suite runtime,eventdbtool
+ ./scripts/run-tests.ps1 -Suite runtime,elevationhelper
#>
[CmdletBinding()]
param(
diff --git a/src/EventLogExpert.EventDbTool/Properties/AssemblyInfo.cs b/src/EventLogExpert.EventDbTool/Properties/AssemblyInfo.cs
index fa5fabc58..6e18265ff 100644
--- a/src/EventLogExpert.EventDbTool/Properties/AssemblyInfo.cs
+++ b/src/EventLogExpert.EventDbTool/Properties/AssemblyInfo.cs
@@ -3,5 +3,4 @@
using System.Runtime.CompilerServices;
-[assembly: InternalsVisibleTo("EventLogExpert.EventDbTool.IntegrationTests")]
[assembly: InternalsVisibleTo("EventLogExpert.EventDbTool.Tests")]
diff --git a/src/EventLogExpert.Eventing/Interop/EvtVariant.cs b/src/EventLogExpert.Eventing/Interop/EvtVariant.cs
index 813b08196..5402f7475 100644
--- a/src/EventLogExpert.Eventing/Interop/EvtVariant.cs
+++ b/src/EventLogExpert.Eventing/Interop/EvtVariant.cs
@@ -8,28 +8,46 @@ namespace EventLogExpert.Eventing.Interop;
[StructLayout(LayoutKind.Explicit)]
internal readonly record struct EvtVariant
{
- [FieldOffset(0)] internal readonly uint UInteger;
- [FieldOffset(0)] internal readonly int Integer;
- [FieldOffset(0)] internal readonly byte UInt8;
- [FieldOffset(0)] internal readonly short Short;
- [FieldOffset(0)] internal readonly ushort UShort;
- [FieldOffset(0)] internal readonly uint Bool;
+ [FieldOffset(0)] internal readonly int BooleanVal;
+ [FieldOffset(0)] internal readonly sbyte SByteVal;
+ [FieldOffset(0)] internal readonly short Int16Val;
+ [FieldOffset(0)] internal readonly int Int32Val;
+ [FieldOffset(0)] internal readonly long Int64Val;
[FieldOffset(0)] internal readonly byte ByteVal;
- [FieldOffset(0)] internal readonly byte SByte;
- [FieldOffset(0)] internal readonly ulong ULong;
- [FieldOffset(0)] internal readonly long Long;
- [FieldOffset(0)] internal readonly float Single;
- [FieldOffset(0)] internal readonly double Double;
+ [FieldOffset(0)] internal readonly ushort UInt16Val;
+ [FieldOffset(0)] internal readonly uint UInt32Val;
+ [FieldOffset(0)] internal readonly ulong UInt64Val;
+ [FieldOffset(0)] internal readonly float SingleVal;
+ [FieldOffset(0)] internal readonly double DoubleVal;
+ [FieldOffset(0)] internal readonly ulong FileTimeVal;
+ [FieldOffset(0)] internal readonly nint SysTimeVal;
+ [FieldOffset(0)] internal readonly nint GuidVal;
[FieldOffset(0)] internal readonly nint StringVal;
- [FieldOffset(0)] internal readonly nint AnsiString;
+ [FieldOffset(0)] internal readonly nint AnsiStringVal;
+ [FieldOffset(0)] internal readonly nint BinaryVal;
[FieldOffset(0)] internal readonly nint SidVal;
- [FieldOffset(0)] internal readonly nint Binary;
- [FieldOffset(0)] internal readonly nint Reference;
- [FieldOffset(0)] internal readonly nint Handle;
- [FieldOffset(0)] internal readonly nint GuidReference;
- [FieldOffset(0)] internal readonly ulong FileTime;
- [FieldOffset(0)] internal readonly nint SystemTime;
- [FieldOffset(0)] internal readonly nint SizeT;
+ [FieldOffset(0)] internal readonly nuint SizeTVal;
+ [FieldOffset(0)] internal readonly nint BooleanArr;
+ [FieldOffset(0)] internal readonly nint SByteArr;
+ [FieldOffset(0)] internal readonly nint Int16Arr;
+ [FieldOffset(0)] internal readonly nint Int32Arr;
+ [FieldOffset(0)] internal readonly nint Int64Arr;
+ [FieldOffset(0)] internal readonly nint ByteArr;
+ [FieldOffset(0)] internal readonly nint UInt16Arr;
+ [FieldOffset(0)] internal readonly nint UInt32Arr;
+ [FieldOffset(0)] internal readonly nint UInt64Arr;
+ [FieldOffset(0)] internal readonly nint SingleArr;
+ [FieldOffset(0)] internal readonly nint DoubleArr;
+ [FieldOffset(0)] internal readonly nint FileTimeArr;
+ [FieldOffset(0)] internal readonly nint SysTimeArr;
+ [FieldOffset(0)] internal readonly nint GuidArr;
+ [FieldOffset(0)] internal readonly nint StringArr;
+ [FieldOffset(0)] internal readonly nint AnsiStringArr;
+ [FieldOffset(0)] internal readonly nint SidArr;
+ [FieldOffset(0)] internal readonly nint SizeTArr;
+ [FieldOffset(0)] internal readonly nint EvtHandleVal;
+ [FieldOffset(0)] internal readonly nint XmlVal;
+ [FieldOffset(0)] internal readonly nint XmlValArr;
[FieldOffset(8)] internal readonly uint Count;
[FieldOffset(12)] internal readonly uint Type;
}
diff --git a/src/EventLogExpert.Eventing/Interop/NativeMethods.Evt.cs b/src/EventLogExpert.Eventing/Interop/NativeMethods.Evt.cs
index 7a121ad6d..f0d7d3a3e 100644
--- a/src/EventLogExpert.Eventing/Interop/NativeMethods.Evt.cs
+++ b/src/EventLogExpert.Eventing/Interop/NativeMethods.Evt.cs
@@ -24,29 +24,29 @@ internal static partial class NativeMethods
case (int)EvtVariantType.String:
return Marshal.PtrToStringUni(variant.StringVal);
case (int)EvtVariantType.AnsiString:
- return Marshal.PtrToStringAnsi(variant.AnsiString);
+ return Marshal.PtrToStringAnsi(variant.AnsiStringVal);
case (int)EvtVariantType.SByte:
- return variant.SByte;
+ return variant.SByteVal;
case (int)EvtVariantType.Byte:
- return variant.UInt8;
+ return variant.ByteVal;
case (int)EvtVariantType.Int16:
- return variant.Short;
+ return variant.Int16Val;
case (int)EvtVariantType.UInt16:
- return variant.UShort;
+ return variant.UInt16Val;
case (int)EvtVariantType.Int32:
- return variant.Integer;
+ return variant.Int32Val;
case (int)EvtVariantType.UInt32:
- return variant.UInteger;
+ return variant.UInt32Val;
case (int)EvtVariantType.Int64:
- return variant.Long;
+ return variant.Int64Val;
case (int)EvtVariantType.UInt64:
- return variant.ULong;
+ return variant.UInt64Val;
case (int)EvtVariantType.Single:
- return variant.Single;
+ return variant.SingleVal;
case (int)EvtVariantType.Double:
- return variant.Double;
+ return variant.DoubleVal;
case (int)EvtVariantType.Boolean:
- return variant.Bool != 0;
+ return variant.BooleanVal != 0;
case (int)EvtVariantType.Binary:
if (variant.Count == 0)
{
@@ -55,23 +55,23 @@ internal static partial class NativeMethods
int byteCount = CheckedCount(variant.Count, EvtVariantType.Binary);
- if (variant.Binary == IntPtr.Zero)
+ if (variant.BinaryVal == IntPtr.Zero)
{
throw new InvalidDataException(
$"Null reference with non-zero count {variant.Count} for {nameof(EvtVariantType)}.{EvtVariantType.Binary}");
}
byte[] bytes = new byte[byteCount];
- Marshal.Copy(variant.Binary, bytes, 0, byteCount);
+ Marshal.Copy(variant.BinaryVal, bytes, 0, byteCount);
return bytes;
case (int)EvtVariantType.Guid:
- return Marshal.PtrToStructure(variant.GuidReference);
+ return Marshal.PtrToStructure(variant.GuidVal);
case (int)EvtVariantType.SizeT:
- return variant.SizeT;
+ return variant.SizeTVal;
case (int)EvtVariantType.FileTime:
- return DateTime.FromFileTimeUtc((long)variant.FileTime);
+ return DateTime.FromFileTimeUtc((long)variant.FileTimeVal);
case (int)EvtVariantType.SysTime:
- var sysTime = Marshal.PtrToStructure(variant.SystemTime);
+ var sysTime = Marshal.PtrToStructure(variant.SysTimeVal);
return new DateTime(
sysTime.Year,
@@ -85,13 +85,13 @@ internal static partial class NativeMethods
case (int)EvtVariantType.Sid:
return variant.SidVal == IntPtr.Zero ? null : new SecurityIdentifier(variant.SidVal);
case (int)EvtVariantType.HexInt32:
- return variant.Integer;
+ return variant.Int32Val;
case (int)EvtVariantType.HexInt64:
- return variant.ULong;
+ return variant.UInt64Val;
case (int)EvtVariantType.Handle:
- return new EvtHandle(variant.Handle);
+ return new EvtHandle(variant.EvtHandleVal);
case (int)EvtVariantType.Xml:
- return Marshal.PtrToStringUni(variant.StringVal);
+ return Marshal.PtrToStringUni(variant.XmlVal);
case (int)EvtVariantType.StringArray:
if (variant.Count == 0)
{
@@ -100,7 +100,7 @@ internal static partial class NativeMethods
int stringCount = CheckedCount(variant.Count, EvtVariantType.StringArray);
- if (variant.Reference == IntPtr.Zero)
+ if (variant.StringArr == IntPtr.Zero)
{
throw new InvalidDataException(
$"Null reference with non-zero count {variant.Count} for {nameof(EvtVariantType)}.{EvtVariantType.StringArray}");
@@ -110,20 +110,20 @@ internal static partial class NativeMethods
for (int i = 0; i < stringCount; i++)
{
- IntPtr stringRef = Marshal.ReadIntPtr(variant.Reference, i * IntPtr.Size);
+ IntPtr stringRef = Marshal.ReadIntPtr(variant.StringArr, i * IntPtr.Size);
stringArray[i] = Marshal.PtrToStringAuto(stringRef) ?? string.Empty;
}
return stringArray;
case (int)EvtVariantType.ByteArray:
- return ReadBlittableArray(variant.Reference, variant.Count, EvtVariantType.ByteArray);
+ return ReadBlittableArray(variant.ByteArr, variant.Count, EvtVariantType.ByteArray);
case (int)EvtVariantType.UInt16Array:
- return ReadBlittableArray(variant.Reference, variant.Count, EvtVariantType.UInt16Array);
+ return ReadBlittableArray(variant.UInt16Arr, variant.Count, EvtVariantType.UInt16Array);
case (int)EvtVariantType.UInt32Array:
- return ReadBlittableArray(variant.Reference, variant.Count, EvtVariantType.UInt32Array);
+ return ReadBlittableArray(variant.UInt32Arr, variant.Count, EvtVariantType.UInt32Array);
case (int)EvtVariantType.HexInt32Array:
- return ReadBlittableArray(variant.Reference, variant.Count, EvtVariantType.HexInt32Array);
+ return ReadBlittableArray(variant.Int32Arr, variant.Count, EvtVariantType.HexInt32Array);
default:
throw new InvalidDataException($"Invalid {nameof(EvtVariantType)}: {variant.Type}");
}
@@ -295,6 +295,19 @@ internal static partial EvtHandle EvtQuery(
[MarshalAs(UnmanagedType.LPWStr)] string? query,
LogPathType flags);
+ ///
+ /// EvtQuery overload that takes the raw combined query-flags (the path-type bits
+ /// ORed with direction flags such as EvtQueryReverseDirection 0x200), so a caller can opt into newest-first
+ /// reads. The typed binding remains the path for the default oldest-first reads and its other
+ /// callers (the watcher and the XML resolver).
+ ///
+ [LibraryImport(EventLogApi, EntryPoint = "EvtQuery", SetLastError = true)]
+ internal static partial EvtHandle EvtQueryWithFlags(
+ EvtHandle session,
+ [MarshalAs(UnmanagedType.LPWStr)] string path,
+ [MarshalAs(UnmanagedType.LPWStr)] string? query,
+ int flags);
+
/// Renders an XML fragment base on the render context that you specify
[LibraryImport(EventLogApi, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
@@ -395,6 +408,85 @@ internal static string FormatMessage(EvtHandle publisherMetadataHandle, uint mes
}
}
+ /// Converts a buffer that was returned from to an
+ ///
+ /// Pointer to a buffer returned from . Must stay pinned and valid for
+ /// the duration of the call; the variants are read directly through this pointer.
+ ///
+ /// Number of properties returned from
+ ///
+ internal static unsafe EventRecord GetEventRecord(IntPtr eventBuffer, int propertyCount)
+ {
+ EventRecord properties = new();
+
+ var variants = (EvtVariant*)eventBuffer;
+
+ for (int i = 0; i < propertyCount; i++)
+ {
+ ref readonly var variant = ref variants[i];
+
+ // Properties are returned in the order defined in EVT_SYSTEM_PROPERTY_ID enum. Value-type
+ // properties are read straight from the typed union field (no boxing); the string, SID and
+ // time properties stay on ConvertVariant (no boxing benefit, and it handles the
+ // FileTime/SysTime split for TimeCreated). Unmapped indices are intentionally skipped.
+ switch (i)
+ {
+ case (int)EvtSystemPropertyId.ProviderName:
+ properties.ProviderName = (string)ConvertVariant(variant)!;
+ break;
+ case (int)EvtSystemPropertyId.EventId:
+ if (variant.Type != (uint)EvtVariantType.UInt16)
+ {
+ throw new InvalidDataException($"Expected EVT_VARIANT type UInt16 for EventId, got {variant.Type}.");
+ }
+
+ properties.Id = variant.UInt16Val;
+ break;
+ case (int)EvtSystemPropertyId.Qualifiers:
+ properties.Qualifiers = ReadOptionalUInt16(variant);
+ break;
+ case (int)EvtSystemPropertyId.Level:
+ properties.Level = ReadOptionalByte(variant);
+ break;
+ case (int)EvtSystemPropertyId.Task:
+ properties.Task = ReadOptionalUInt16(variant);
+ break;
+ case (int)EvtSystemPropertyId.Keywords:
+ properties.Keywords = ReadOptionalInt64(variant);
+ break;
+ case (int)EvtSystemPropertyId.TimeCreated:
+ properties.TimeCreated = (DateTime)ConvertVariant(variant)!;
+ break;
+ case (int)EvtSystemPropertyId.EventRecordId:
+ properties.RecordId = ReadOptionalInt64(variant);
+ break;
+ case (int)EvtSystemPropertyId.ActivityId:
+ properties.ActivityId = ReadGuidOrNull(variant);
+ break;
+ case (int)EvtSystemPropertyId.ProcessID:
+ properties.ProcessId = ReadOptionalInt32(variant);
+ break;
+ case (int)EvtSystemPropertyId.ThreadID:
+ properties.ThreadId = ReadOptionalInt32(variant);
+ break;
+ case (int)EvtSystemPropertyId.Channel:
+ properties.LogName = (string)ConvertVariant(variant)!;
+ break;
+ case (int)EvtSystemPropertyId.Computer:
+ properties.ComputerName = (string)ConvertVariant(variant)!;
+ break;
+ case (int)EvtSystemPropertyId.UserID:
+ properties.UserId = (SecurityIdentifier?)ConvertVariant(variant);
+ break;
+ case (int)EvtSystemPropertyId.Version:
+ properties.Version = ReadOptionalByte(variant);
+ break;
+ }
+ }
+
+ return properties;
+ }
+
internal static object GetObjectArrayProperty(
EvtHandle array,
int index,
@@ -441,7 +533,7 @@ internal static object GetObjectArrayProperty(
{
fixed (char* bufferPtr = buffer)
{
- var variant = Marshal.PtrToStructure((IntPtr)bufferPtr);
+ var variant = *(EvtVariant*)bufferPtr;
return ConvertVariant(variant) ??
throw new InvalidDataException($"Invalid Object Array for Property: {propertyId}");
@@ -581,15 +673,15 @@ internal static IReadOnlyList