Skip to content
This repository was archived by the owner on Feb 24, 2026. It is now read-only.

Commit 4d136d1

Browse files
committed
Enable nullable reference types and update nullability; fix several null checks
Fully enable nullable reference types and update method signatures, fields, and events to use nullable annotations throughout the codebase. Methods now return null instead of empty arrays/strings where appropriate. Private parameterless constructors now throw NotImplementedException to prevent misuse. Improves null-safety, code clarity, and robustness against null reference exceptions.
1 parent 890d64a commit 4d136d1

9 files changed

Lines changed: 98 additions & 91 deletions

File tree

src/VmmSharpEx/Extensions/Input/VmmInputManager.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public sealed class VmmInputManager
4848
private readonly ulong _gafAsyncKeyStateExport;
4949
private readonly uint _winLogonPid;
5050

51-
private VmmInputManager() { }
51+
private VmmInputManager() { throw new NotImplementedException(); }
5252

5353
/// <summary>
5454
/// Extension class that queries user input state via Win32 Kernel Interop (Read-Only).
@@ -64,6 +64,8 @@ public VmmInputManager(Vmm vmm)
6464
if (!_vmm.PidGetFromName("winlogon.exe", out _winLogonPid))
6565
throw new VmmException("Failed to get winlogon.exe PID");
6666
var pids = _vmm.PidGetAllFromName("csrss.exe");
67+
if (pids is null || pids.Length == 0)
68+
throw new VmmException("Failed to get csrss.exe PIDs");
6769
ulong gafAsyncKeyStateExport = 0;
6870

6971
var exceptions = new List<Exception>();

src/VmmSharpEx/Extensions/VmmExtensions.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ public static bool IsValidKernelVA(this ulong va) =>
7676
/// <param name="paramName">Parameter name for the exception message.</param>
7777
/// <exception cref="VmmException"></exception>
7878
[MethodImpl(MethodImplOptions.AggressiveInlining)]
79-
public static void ThrowIfInvalidVA(this ulong va, string paramName = null)
79+
public static void ThrowIfInvalidVA(this ulong va, string? paramName = null)
8080
{
8181
if (!VmmUtilities.IsValidVA(va))
8282
throw new VmmException(paramName is null ?
@@ -91,7 +91,7 @@ public static void ThrowIfInvalidVA(this ulong va, string paramName = null)
9191
/// <param name="paramName">Parameter name for the exception message.</param>
9292
/// <exception cref="VmmException"></exception>
9393
[MethodImpl(MethodImplOptions.AggressiveInlining)]
94-
public static void ThrowIfInvalidUserVA(this ulong va, string paramName = null)
94+
public static void ThrowIfInvalidUserVA(this ulong va, string? paramName = null)
9595
{
9696
if (!VmmUtilities.IsValidUserVA(va))
9797
throw new VmmException(paramName is null ?
@@ -106,7 +106,7 @@ public static void ThrowIfInvalidUserVA(this ulong va, string paramName = null)
106106
/// <param name="paramName">Parameter name for the exception message.</param>
107107
/// <exception cref="VmmException"></exception>
108108
[MethodImpl(MethodImplOptions.AggressiveInlining)]
109-
public static void ThrowIfInvalidKernelVA(this ulong va, string paramName = null)
109+
public static void ThrowIfInvalidKernelVA(this ulong va, string? paramName = null)
110110
{
111111
if (!VmmUtilities.IsValidKernelVA(va))
112112
throw new VmmException(paramName is null ?
@@ -145,7 +145,7 @@ public static bool FixCr3(this Vmm vmm, string processName, uint pid)
145145
while (true)
146146
{
147147
var percentBytes = vmm.VfsRead(@"\misc\procinfo\progress_percent.txt", 4);
148-
if (percentBytes.Length > 0 &&
148+
if (percentBytes is not null &&
149149
int.TryParse(Encoding.ASCII.GetString(percentBytes).Trim(), out int percent) &&
150150
percent == 100)
151151
break;
@@ -154,9 +154,9 @@ public static bool FixCr3(this Vmm vmm, string processName, uint pid)
154154
}
155155

156156
// VFS list and read DTBs
157-
var vfsEntries = vmm.VfsList(@"\misc\procinfo\");
157+
//var vfsEntries = vmm.VfsList(@"\misc\procinfo\");
158158
var dtbRaw = vmm.VfsRead(@"\misc\procinfo\dtb.txt", 0x1000);
159-
if (dtbRaw.Length == 0)
159+
if (dtbRaw is null)
160160
return false;
161161

162162
var possibleDtbs = new List<ulong>();

src/VmmSharpEx/LeechCore.cs

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,12 @@ public sealed class LeechCore : IDisposable
3838
private readonly Vmm _parent;
3939
private IntPtr _handle;
4040

41-
private LeechCore() { }
41+
private LeechCore() { throw new NotImplementedException(); }
4242

4343
private LeechCore(IntPtr hLC)
4444
{
4545
_handle = hLC;
46+
_parent = null!; // Required for nullable, but will be set in internal constructor
4647
}
4748

4849
/// <summary>
@@ -120,7 +121,7 @@ public override string ToString()
120121
/// <param name="pLcCreateConfig">The LC configuration to use.</param>
121122
/// <param name="configErrorInfo">Receives extended create-time error information, if available.</param>
122123
/// <returns>An initialized <see cref="LeechCore"/> instance on success; otherwise <see langword="null"/>.</returns>
123-
public static unsafe LeechCore Create(ref LCConfig pLcCreateConfig, out LCConfigErrorInfo configErrorInfo)
124+
public static unsafe LeechCore? Create(ref LCConfig pLcCreateConfig, out LCConfigErrorInfo configErrorInfo)
124125
{
125126
var cbERROR_INFO = Marshal.SizeOf<Lci.LC_CONFIG_ERRORINFO>();
126127
var pLcCreateConfigNative = Marshal.AllocHGlobal(Marshal.SizeOf<LCConfig>());
@@ -151,7 +152,7 @@ public static unsafe LeechCore Create(ref LCConfig pLcCreateConfig, out LCConfig
151152
configErrorInfo.fUserInputRequest = e.fUserInputRequest;
152153
if (e.cwszUserText > 0)
153154
{
154-
configErrorInfo.strUserText = Marshal.PtrToStringUni((IntPtr)(pLcErrorInfo.ToInt64() + cbERROR_INFO));
155+
configErrorInfo.strUserText = Marshal.PtrToStringUni((IntPtr)(pLcErrorInfo.ToInt64() + cbERROR_INFO)) ?? "";
155156
}
156157
}
157158

@@ -193,7 +194,7 @@ private void Dispose(bool disposing)
193194
/// <param name="pa">Physical address to read from.</param>
194195
/// <param name="cb">Count of bytes to read.</param>
195196
/// <returns>A byte array with the read memory, otherwise <see langword="null"/>.</returns>
196-
public unsafe byte[] Read(ulong pa, uint cb)
197+
public unsafe byte[]? Read(ulong pa, uint cb)
197198
{
198199
var arr = new byte[cb];
199200
fixed (byte* pb = arr)
@@ -235,7 +236,7 @@ public unsafe bool ReadValue<T>(ulong pa, out T result)
235236
/// <param name="pa">Physical address to read.</param>
236237
/// <param name="count">Number of elements to read.</param>
237238
/// <returns>An array on success; otherwise <see langword="null"/>.</returns>
238-
public unsafe T[] ReadArray<T>(ulong pa, int count)
239+
public unsafe T[]? ReadArray<T>(ulong pa, int count)
239240
where T : unmanaged
240241
{
241242
var arr = new T[count];
@@ -257,7 +258,7 @@ public unsafe T[] ReadArray<T>(ulong pa, int count)
257258
/// <param name="pa">Physical address to read.</param>
258259
/// <param name="count">Number of elements to read.</param>
259260
/// <returns>A <see cref="IMemoryOwner{T}"/> lease on success; otherwise <see langword="null"/>. Be sure to call <see cref="IDisposable.Dispose()"/> when done.</returns>
260-
public unsafe IMemoryOwner<T> ReadPooled<T>(ulong pa, int count)
261+
public unsafe IMemoryOwner<T>? ReadPooled<T>(ulong pa, int count)
261262
where T : unmanaged
262263
{
263264
var arr = new PooledMemory<T>(count);
@@ -474,7 +475,7 @@ public bool SetOption(LcOption fOption, ulong qwValue)
474475
/// <param name="dataIn">Optional input data.</param>
475476
/// <param name="dataOut">Receives any output data returned by the command.</param>
476477
/// <returns><see langword="true"/> on success; otherwise <see langword="false"/>.</returns>
477-
public unsafe bool ExecuteCommand(LcCmd fOption, ReadOnlySpan<byte> dataIn, out byte[] dataOut)
478+
public unsafe bool ExecuteCommand(LcCmd fOption, ReadOnlySpan<byte> dataIn, out byte[]? dataOut)
478479
{
479480
uint cbDataOut;
480481
IntPtr pbDataOut;
@@ -512,15 +513,18 @@ public unsafe bool ExecuteCommand(LcCmd fOption, ReadOnlySpan<byte> dataIn, out
512513
/// Wraps native memory returned from a scatter read invocation.
513514
/// </summary>
514515
/// <remarks>
515-
/// The underlying native scatter page buffers are released via <see cref="Lci.LcMemFree(IntPtr)"/> when this handle
516+
/// The underlying native scatter page buffers are released via <see cref="Lci.LcMemFree(void*)"/> when this handle
516517
/// is disposed.
517518
/// </remarks>
518519
public sealed class LcScatterHandle : IDisposable
519520
{
520521
private readonly PooledDictionary<ulong, ScatterData> _results;
521522
private IntPtr _mems;
522523

523-
private LcScatterHandle() { }
524+
private LcScatterHandle()
525+
{
526+
_results = new PooledDictionary<ulong, ScatterData>();
527+
}
524528

525529
internal LcScatterHandle(PooledDictionary<ulong, ScatterData> results, IntPtr mems)
526530
{
@@ -648,7 +652,7 @@ public struct LcMemScatter
648652
/// A read-only view over the page contents pointed at by <see cref="pb"/>.
649653
/// </summary>
650654
/// <remarks>
651-
/// DANGER: Do not access this memory after the memory is freed via <see cref="Lci.LcMemFree(IntPtr)"/>.
655+
/// DANGER: Do not access this memory after the memory is freed via <see cref="Lci.LcMemFree(void*)"/>.
652656
/// </remarks>
653657
public readonly unsafe ReadOnlySpan<byte> Data =>
654658
new ReadOnlySpan<byte>(pb.ToPointer(), checked((int)cb));
@@ -730,7 +734,7 @@ public struct LCConfigErrorInfo
730734
/// <summary>
731735
/// Optional user text provided by the native layer.
732736
/// </summary>
733-
public string strUserText;
737+
public string? strUserText;
734738
}
735739

736740
#endregion

src/VmmSharpEx/Scatter/VmmScatter.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ private set
6060
/// <summary>
6161
/// Event is fired upon completion of <see cref="Execute"/>. Exceptions are handled/ignored.
6262
/// </summary>
63-
public event EventHandler<VmmScatter> Completed;
63+
public event EventHandler<VmmScatter>? Completed;
6464
private void OnCompleted()
6565
{
6666
foreach (var callback in Completed?.GetInvocationList() ?? Enumerable.Empty<Delegate>())
@@ -73,7 +73,7 @@ private void OnCompleted()
7373
}
7474
}
7575

76-
private VmmScatter() { }
76+
private VmmScatter() { throw new NotImplementedException(); }
7777

7878
internal VmmScatter(Vmm vmm, uint pid, VmmFlags flags = VmmFlags.NONE)
7979
{
@@ -280,7 +280,7 @@ public void Execute()
280280
/// <param name="cb">Count of bytes to be read.</param>
281281
/// <param name="cbRead">Count of bytes actually read.</param>
282282
/// <returns>A byte array with the read memory, otherwise <see langword="null"/>. Be sure to also check <paramref name="cbRead"/>.</returns>
283-
public unsafe byte[] Read(ulong address, uint cb, out uint cbRead)
283+
public unsafe byte[]? Read(ulong address, uint cb, out uint cbRead)
284284
{
285285
var arr = new byte[cb];
286286
fixed (byte* pb = arr)
@@ -374,7 +374,7 @@ public bool ReadPtr(ulong address, out VmmPointer result)
374374
/// <param name="address">Address to read from.</param>
375375
/// <param name="count">The number of array elements to read.</param>
376376
/// <returns>An array on success; otherwise <see langword="null"/>.</returns>
377-
public unsafe T[] ReadArray<T>(ulong address, int count)
377+
public unsafe T[]? ReadArray<T>(ulong address, int count)
378378
where T : unmanaged
379379
{
380380
uint cb = checked((uint)sizeof(T) * (uint)count);
@@ -399,7 +399,7 @@ public unsafe T[] ReadArray<T>(ulong address, int count)
399399
/// <param name="address">Address to read from.</param>
400400
/// <param name="count">The number of array elements to read.</param>
401401
/// <returns><see cref="IMemoryOwner{T}"/> lease, or <see langword="null"/> if failed. Be sure to call <see cref="IDisposable.Dispose()"/> when done.</returns>
402-
public unsafe IMemoryOwner<T> ReadPooled<T>(ulong address, int count)
402+
public unsafe IMemoryOwner<T>? ReadPooled<T>(ulong address, int count)
403403
where T : unmanaged
404404
{
405405
uint cb = checked((uint)sizeof(T) * (uint)count);
@@ -445,10 +445,10 @@ public unsafe bool ReadSpan<T>(ulong address, Span<T> span)
445445
/// <param name="cb">Count of bytes to read. Keep in mind some string encodings are 2-4 bytes per character.</param>
446446
/// <param name="encoding">String Encoding for this read.</param>
447447
/// <returns>C# Managed <see cref="System.String"/>. Otherwise, <see langword="null"/> if failed.</returns>
448-
public string ReadString(ulong address, int cb, Encoding encoding)
448+
public string? ReadString(ulong address, int cb, Encoding encoding)
449449
{
450-
byte[] rentedBytes = null;
451-
char[] rentedChars = null;
450+
byte[]? rentedBytes = null;
451+
char[]? rentedChars = null;
452452
try
453453
{
454454
Span<byte> bytesSource = cb <= 256 ?
@@ -495,7 +495,7 @@ public void Clear(VmmFlags? flags = null, uint? pid = null)
495495
if (pid is uint p)
496496
_pid = p;
497497
_isPrepared = default;
498-
Completed = default;
498+
Completed = null;
499499
if (!Vmmi.VMMDLL_Scatter_Clear(_handle, _pid, _flags))
500500
throw new VmmException("Failed to clear VmmScatter Handle.");
501501
}

src/VmmSharpEx/Scatter/VmmScatterMap.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ public sealed class VmmScatterMap : IDisposable
2424
/// <summary>
2525
/// Event is fired upon completion of <see cref="Execute"/>.
2626
/// </summary>
27-
public event EventHandler Completed;
27+
public event EventHandler? Completed;
2828
private void OnCompleted() => Completed?.Invoke(this, EventArgs.Empty);
2929

30-
private VmmScatterMap() { }
30+
private VmmScatterMap() { throw new NotImplementedException(); }
3131

3232
internal VmmScatterMap(Vmm vmm, uint pid)
3333
{

0 commit comments

Comments
 (0)