Files
whitemagic/docs/troubleshooting.md
kbe 6300bebe33 Fix API documentation and examples; add comprehensive documentation suite
- 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.
2026-07-22 22:26:07 +02:00

13 KiB

Troubleshooting Guide

This guide covers common issues, errors, and solutions when using WhiteMagic.

Table of Contents


Process Access Issues

"Process is not open for read/write"

Symptom:

System.InvalidOperationException: Process is not open for read/write.

Causes:

  1. Process has exited
  2. Process handle is closed
  3. 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 Process object alive
  • Don't dispose Magic while using it
  • Use using statement for automatic cleanup

"OpenProcess failed: Access Denied"

Symptom:

Process.Open returns false, or Magic.Open throws Win32Exception

Causes:

  1. Insufficient privileges (not running as administrator)
  2. Target process is protected (anti-cheat, system process)
  3. 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:

  1. Address is invalid
  2. Memory protection prevents reading
  3. Fewer bytes read than expected
  4. 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:

  1. Memory is read-only (e.g., .text section)
  2. Address is invalid
  3. Process has exited
  4. 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:

  1. Target process is protected
  2. Insufficient permissions
  3. 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:

  1. Wrong function address
  2. Target already called function before hook installed
  3. 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:

  1. Pattern is incorrect
  2. Module not loaded
  3. Memory layout changed (ASLR, update)
  4. 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:

  1. Reading individual elements in a loop
  2. Using marshalled types extensively
  3. 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:

  1. Race conditions (concurrent memory access)
  2. Anti-cheat interference
  3. 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

  1. Forgetting isRelative for 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);
    
  2. Using RemoteThreadExecutor for 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));
    
  3. 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:

  1. Check documentation:

  2. Search issues: Check existing GitHub issues

  3. 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
    
  4. 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.