- Example5: Fix format string bugs (alignment specifier placement, SafeMemoryHandle formatting) - Example4: Fix DetourManager.Detour() → Create() in all doc strings - Example3: Fix MemoryBase.CreateFunction() → InProcessInvoker.CreateFunction() in doc strings - Examples 1-5: Correct all runtime errors and API mismatches vs actual WhiteMagic API - README: Fix DetourManager.Detour() examples to use Create(); add missing code fence markers - Add WhiteMagic.Examples project with 5 comprehensive example files (40+ sub-examples) - Add docfx.json and toc.md for DocFX API reference generation - Add 5 conceptual guides: architecture, memory-access, execution-models, hooking, troubleshooting - Ensure zero errors, zero warnings across all projects (net8.0-windows) Doc strings now teach correct APIs; runtime format bugs eliminated; build succeeds.
13 KiB
Troubleshooting Guide
This guide covers common issues, errors, and solutions when using WhiteMagic.
Table of Contents
- Process Access Issues
- Memory Operation Failures
- Execution Errors
- Hooking Problems
- Pattern Scanning Issues
- Performance Problems
- Crash and Stability Issues
- Build and Compilation Errors
Process Access Issues
"Process is not open for read/write"
Symptom:
System.InvalidOperationException: Process is not open for read/write.
Causes:
- Process has exited
- Process handle is closed
- Insufficient permissions
Solutions:
// Check process state
if (magic.Memory.Handle.IsInvalid || magic.Memory.Handle.IsClosed)
{
Console.WriteLine("Process handle is invalid or closed");
}
// Re-open the process
using var magic = Magic.Open(process);
Prevention:
- Keep
Processobject alive - Don't dispose
Magicwhile using it - Use
usingstatement for automatic cleanup
"OpenProcess failed: Access Denied"
Symptom:
Process.Open returns false, or Magic.Open throws Win32Exception
Causes:
- Insufficient privileges (not running as administrator)
- Target process is protected (anti-cheat, system process)
- 32-bit/64-bit mismatch
Solutions:
// Run as administrator
// Right-click → Run as Administrator
// Check process bitness matches host
if (Environment.Is64BitProcess != Is64BitProcess(targetProcess))
{
Console.WriteLine("Bitness mismatch between host and target");
}
Prevention:
- Run with admin privileges
- Check target process protection level
- Ensure bitness compatibility
Memory Operation Failures
"Read returns default value"
Symptom:
int value = magic.Memory.Read<int>(address);
// value is 0 (or default), but expected different value
Causes:
- Address is invalid
- Memory protection prevents reading
- Fewer bytes read than expected
- Process has exited
Solutions:
// Verify address with debugger
// Check if address is readable
byte[] test = magic.Memory.ReadBytes(address, 4);
if (test.Length < 4)
{
Console.WriteLine("Cannot read from address");
}
// Validate handle
if (magic.Memory.Handle.IsInvalid)
{
Console.WriteLine("Process handle is invalid");
}
"Write returns false"
Symptom:
bool success = magic.Memory.Write(address, value);
if (!success) { /* Write failed */ }
Causes:
- Memory is read-only (e.g.,
.textsection) - Address is invalid
- Process has exited
- Anti-cheat blocking writes
Solutions:
// Retry with delay
for (int i = 0; i < 3; i++)
{
if (magic.Memory.Write(address, value))
break;
Thread.Sleep(50);
}
// Check if memory is protected
// Use VirtualProtectEx if you have rights (advanced)
For code patches, use PatchManager instead:
var patch = magic.PatchManager.Create("Patch", address, bytes);
patch.Apply(); // Handles VirtualProtect automatically
Execution Errors
"CreateRemoteThread failed"
Symptom:
Win32Exception: CreateRemoteThread failed
Causes:
- Target process is protected
- Insufficient permissions
- Target process has exited
Solutions:
// Ensure process is still running
if (process.HasExited)
{
Console.WriteLine("Process has exited");
}
// Check permissions
// Run as administrator
"Crash when calling target function via RemoteThreadExecutor"
Symptom: Target application crashes after RemoteThreadExecutor.Execute<T>
Cause: Calling single-threaded function from remote thread (thread-affinity violation)
Solution: Use MainThreadPump instead:
// WRONG (crashes game):
int health = magic.RemoteThread.Execute<int>(
readHealthFn,
CallConvention.Cdecl
);
// RIGHT (crash-safe):
var pump = magic.CreateMainThreadPump(frameAddress);
int health = await pump.Enqueue(() =>
magic.Memory.Read<int>(healthAddress)
);
Explanation: Game state, scripting engines, and render contexts are often main-thread-only. Use MainThreadPump for these.
"MainThreadPump work item wedged"
Symptom: Enqueue<T>() hangs or times out
Cause: Work item threw exception or entered infinite loop
Solution:
try
{
int result = await pump.Enqueue(() =>
{
// Keep work items short!
return magic.Memory.Read<int>(address);
}, TimeSpan.FromSeconds(1)); // Add timeout
}
catch (TimeoutException)
{
Console.WriteLine("Work item wedged (stuck the frame)");
}
Prevention:
- Keep pump work items short (< 1ms)
- Avoid blocking calls in work items
- Use timeout on
Enqueue<T>
Hooking Problems
"Detour failed: prologue too short"
Symptom:
InvalidOperationException: Prologue is too short for detour (minimum 5 bytes required)
Cause: Function prologue is shorter than minimum required for jmp instruction
Solutions:
// Option 1: Hook a different function
var detour = magic.DetourManager.Detour(alternativeAddress, hook);
// Option 2: Patch deeper into function (after prologue)
// (Advanced, risky)
// Option 3: For WinAPI, use hot-patch area
if (IsHotPatchPadded(functionAddress))
{
// Can use 2-byte jmp at [address-2]
}
"Patch failed: access violation"
Symptom:
AccessViolationException when applying patch
Cause: Memory is protected or invalid
Solutions:
// Verify address is valid
byte[] test = magic.Memory.ReadBytes(address, 1);
// Use PatchManager (handles VirtualProtect automatically)
var patch = magic.PatchManager.Create("Patch", address, bytes);
patch.Apply();
"Hook not called"
Symptom: Detour applied successfully, but hook delegate never executes
Causes:
- Wrong function address
- Target already called function before hook installed
- Target is using a different implementation (e.g., forwarded export)
Solutions:
// Verify address with debugger
// Install hook early (before target uses function)
// Check for forwarded exports
var fn = magic["module"]["function"];
Console.WriteLine($"Function address: {fn.Address}");
Pattern Scanning Issues
"FindPattern returns IntPtr.Zero"
Symptom: Pattern scan finds no matches
Causes:
- Pattern is incorrect
- Module not loaded
- Memory layout changed (ASLR, update)
- Pattern wildcards too broad
Solutions:
// Verify pattern syntax
// Use IDA-style: "48 8B ? ? ? ? ?"
// ? = wildcard byte
// Narrow search range
// Scan only relevant module, not entire memory
// Check if module is loaded
var module = magic["moduleName"];
if (module.BaseAddress == IntPtr.Zero)
{
Console.WriteLine("Module not loaded");
}
// Test with known pattern
var test = magic.Memory.FindPattern("48 8B 05 ? ? ? ?", module.BaseAddress, 0x1000);
"Pattern scanner is slow"
Symptom: FindPattern takes several seconds
Cause: Scanning large memory regions without cache
Solution:
// Use pattern cache (if available in your version)
// Or cache results manually
private static readonly Dictionary<string, IntPtr> PatternCache = new();
IntPtr FindPatternCached(string pattern, IntPtr baseAddr, int size)
{
string key = $"{pattern}_{baseAddr}_{size}";
if (PatternCache.TryGetValue(key, out var cached))
return cached;
IntPtr result = magic.Memory.FindPattern(pattern, baseAddr, size);
PatternCache[key] = result;
return result;
}
Performance Problems
"Memory reads are slow"
Symptom: Read<T> takes several milliseconds
Causes:
- Reading individual elements in a loop
- Using marshalled types extensively
- Target process is heavily loaded
Solutions:
// WRONG: Slow loop
for (int i = 0; i < 1000; i++)
{
values[i] = magic.Memory.Read<int>(baseAddr + i * 4);
}
// RIGHT: Batch read
values = magic.Memory.Read<int>(baseAddr, 1000);
// RIGHT: Use blittable types
// Use int instead of bool where possible
"High CPU usage"
Symptom: WhiteMagic causes high CPU in target process
Cause: Polling loops, tight read loops in pump
Solutions:
// WRONG: Tight loop in pump
while (true)
{
int health = magic.Memory.Read<int>(healthAddr);
if (health == 0) break;
}
// RIGHT: Event-driven or throttled
// Use MainThreadPump with delays
// Or poll at reasonable interval (e.g., 60 Hz)
Crash and Stability Issues
"Target crashes when attached"
Symptom: Target application crashes shortly after Magic.Open
Cause: Thread-affinity violation (calling function from wrong thread)
Solution: Use MainThreadPump for state-sensitive calls:
// Identify the crash-safe execution model
var pump = magic.CreateMainThreadPump(frameAddress);
// All state-sensitive calls go through pump
await pump.Enqueue(() => { /* safe code */ });
"Random crashes during operation"
Symptom: Intermittent crashes, hard to reproduce
Causes:
- Race conditions (concurrent memory access)
- Anti-cheat interference
- Memory protection changes mid-operation
Solutions:
// Add synchronization
lock (syncLock)
{
magic.Memory.Write(address, value);
}
// Handle access violations gracefully
try
{
magic.Memory.Read<int>(address);
}
catch (AccessViolationException)
{
// Retry or handle gracefully
}
// Check for anti-cheat
// Some anti-cheat tools detect and block memory manipulation
Build and Compilation Errors
"XML documentation errors"
Symptom: Build fails with CS1591 or CS0419 errors
Cause: Missing XML comments or ambiguous cref references
Solution:
<!-- In WhiteMagic.csproj -->
<PropertyGroup>
<NoWarn>$(NoWarn);CS1591</NoWarn> <!-- Suppress missing warnings -->
</PropertyGroup>
Or add missing XML comments (see Documentation Best Practices).
"Type or namespace not found"
Symptom: WhiteMagic namespace not found after adding reference
Cause: Project not referencing WhiteMagic.dll
Solution:
dotnet add reference ../WhiteMagic/WhiteMagic.csproj
# OR
dotnet add package WhiteMagic
Debugging Tips
Enable Detailed Logging
// Add diagnostic logging
using System.Diagnostics;
Debug.WriteLine($"Reading from 0x{address:X}");
int value = magic.Memory.Read<int>(address);
Debug.WriteLine($"Read result: {value}");
Verify with External Tools
- Cheat Engine: Verify memory addresses and values
- x64dbg/windbg: Verify function addresses and disassembly
- Process Hacker: Check process handles and permissions
Common Pitfalls
-
❌ Forgetting
isRelativefor module-relative addresses// WRONG int value = magic.Memory.Read<int>(0x1000); // RIGHT (if 0x1000 is module-relative) int value = magic.Memory.Read<int>(0x1000, isRelative: true); -
❌ Using
RemoteThreadExecutorfor game state// WRONG (crashes) int health = magic.RemoteThread.Execute<int>(fn, CallConvention.Cdecl); // RIGHT (crash-safe) int health = await pump.Enqueue(() => magic.Memory.Read<int>(addr)); -
❌ Not disposing
Magic// WRONG (leaks handles) var magic = Magic.Open(process); // ... use magic // Forgot to dispose! // RIGHT using (var magic = Magic.Open(process)) { // ... use magic } // Auto-disposes
Getting Help
If you're still stuck:
-
Check documentation:
-
Search issues: Check existing GitHub issues
-
Create minimal reproduction:
// Minimal code that reproduces the issue using var magic = Magic.Open(process); int value = magic.Memory.Read<int>(address); // What happens vs. what you expect -
Include system info:
- WhiteMagic version
- .NET version
- Target process (if applicable)
- Windows version
- x86 or x64
Common Error Codes
| Win32 Error | Meaning | Solution |
|---|---|---|
ERROR_ACCESS_DENIED (5) |
Insufficient permissions | Run as administrator |
ERROR_INVALID_HANDLE (6) |
Handle is invalid/closed | Re-open process |
ERROR_NOT_ENOUGH_MEMORY (8) |
Insufficient memory | Reduce buffer size |
ERROR_NOACCESS (998) |
Invalid access | Check memory protection |
ERROR_PARTIAL_COPY (299) |
Partial read/write | Retry or check address |
For more information, see the main README and architecture documentation.