fix 7 correctness and cleanup issues
Blocking fixes: 1. Read<T> now returns default(T) on failed/partial read instead of crash (applies to single Read<T>, array Read<T>, and ReadBytes) 2. InProcessReader uses ReadProcessMemory via handle instead of unsafe Buffer.MemoryCopy — fails soft on bad address instead of AV'ing 3. GetRelative now returns absolute - ImageBase (inverse of GetAbsolute). Fix round-trip test to validate at arbitrary offsets, not just ImageBase 4. ReadString reads in 64-byte chunks with encoding-aware null-terminator pattern matching (handles UTF-16's 2-byte null, UTF-32's 4-byte null) Cleanup: 5. InProcessReader validates handle on open and uses RPM through it (handle is no longer unused) 6. Array marshal read: pin raw buffer once, PtrToStructure at offset 7. StructureToByteArray(Span) delegates to byte[] overload, no duplicate New tests: 4 invalid-address grace tests (returns default/empty/false). All 57 passing.
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
using WhiteMagic.Native;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using WhiteMagic;
|
||||
using WhiteMagic.Native;
|
||||
|
||||
namespace WhiteMagicTest;
|
||||
|
||||
@@ -31,7 +31,6 @@ public class MemoryBaseTests
|
||||
{
|
||||
using var reader = OpenSelf();
|
||||
|
||||
// Pin a local int to use as our "remote" address
|
||||
int slot = 0;
|
||||
GCHandle pin = GCHandle.Alloc(slot, GCHandleType.Pinned);
|
||||
try
|
||||
@@ -91,10 +90,7 @@ public class MemoryBaseTests
|
||||
try
|
||||
{
|
||||
IntPtr addr = pin.AddrOfPinnedObject();
|
||||
|
||||
// Write a new value
|
||||
Assert.True(reader.Write(addr, new TestStruct { X = 100, Y = 200 }));
|
||||
|
||||
var result = reader.Read<TestStruct>(addr);
|
||||
Assert.Equal(100, result.X);
|
||||
Assert.Equal(200, result.Y);
|
||||
@@ -140,7 +136,6 @@ public class MemoryBaseTests
|
||||
int[] expected = [10, 20, 30, 40];
|
||||
|
||||
Assert.True(reader.Write(addr, expected));
|
||||
|
||||
int[] actual = reader.Read<int>(addr, 4);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
@@ -168,7 +163,6 @@ public class MemoryBaseTests
|
||||
};
|
||||
|
||||
Assert.True(reader.Write(addr, expected));
|
||||
|
||||
var actual = reader.Read<TestStruct>(addr, 4);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
@@ -199,7 +193,39 @@ public class MemoryBaseTests
|
||||
{
|
||||
var reader = OpenSelf();
|
||||
reader.Dispose();
|
||||
reader.Dispose(); // Should not throw
|
||||
reader.Dispose();
|
||||
}
|
||||
|
||||
// ── Graceful failure on invalid addresses ───────────────────────────────
|
||||
|
||||
[Fact]
|
||||
public void Read_int_on_invalid_address_returns_default()
|
||||
{
|
||||
using var reader = OpenSelf();
|
||||
Assert.Equal(0, reader.Read<int>(IntPtr.Zero));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Read_struct_on_invalid_address_returns_default()
|
||||
{
|
||||
using var reader = OpenSelf();
|
||||
var result = reader.Read<TestStruct>(IntPtr.Zero);
|
||||
Assert.Equal(0, result.X);
|
||||
Assert.Equal(0, result.Y);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Read_int_array_on_invalid_address_returns_empty()
|
||||
{
|
||||
using var reader = OpenSelf();
|
||||
Assert.Empty(reader.Read<int>(IntPtr.Zero, 10));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Read_bytes_on_invalid_address_returns_empty()
|
||||
{
|
||||
using var reader = OpenSelf();
|
||||
Assert.Empty(reader.ReadBytes(IntPtr.Zero, 10));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user