Dynamic DLL Hot-Swapping for Live Game Servers: Techniques to Replace Locked DLLs Without Service Interruption


1 views

When running game servers or any long-lived processes on Windows, replacing DLL files becomes problematic because Windows locks loaded DLLs to prevent modification while in use. This creates particular challenges for:

  • Game server operators needing zero-downtime updates
  • Developers implementing hotfixes in production
  • Continuous deployment scenarios

Windows employs a reference-counting mechanism for DLLs. When a process loads a DLL:

  1. The system creates a memory-mapped view of the file
  2. A handle counter increments for the DLL
  3. The file becomes locked against modifications

This explains why standard file replacement attempts fail with "Access Denied" errors.

Here are three proven approaches to achieve live DLL replacement:

1. DLL Redirection Technique

Create a new version with a different filename and modify the loading mechanism:

// In your server's bootstrap code:
string dllVersion = GetCurrentDLLVersion(); // e.g. "game_logic_v2.dll"
IntPtr module = LoadLibrary(dllVersion);

// When updating:
File.Copy("game_logic_v3.dll", "game_logic_v2.dll", overwrite: true);

2. Using MoveFileEx with MOVEFILE_DELAY_UNTIL_REBOOT

Schedule the replacement for the next restart:

[DllImport("kernel32.dll", SetLastError=true, CharSet=CharSet.Unicode)]
static extern bool MoveFileEx(string lpExistingFileName, 
                            string lpNewFileName, 
                            MoveFileFlags dwFlags);

enum MoveFileFlags
{
    MOVEFILE_DELAY_UNTIL_REBOOT = 0x00000004
}

// Usage:
MoveFileEx("new_version.dll", "current_version.dll", 
          MoveFileFlags.MOVEFILE_DELAY_UNTIL_REBOOT);

3. Shadow Copy Approach

Leverage Volume Shadow Copy Service (VSS) for enterprise solutions:

// Requires VSS SDK
IVssBackupComponents backup = (IVssBackupComponents)new VsSoftwareSnapshotProvider();
backup.InitializeForBackup(null);
backup.SetContext(VSSVolumeSnapshotAttributes.Persistent);
backup.StartSnapshotSet(out Guid snapshotSetId);
backup.AddToSnapshotSet(@"C:\GameServers", Guid.Empty, out Guid snapshotId);
backup.PrepareForBackup();
Approach Pros Cons
DLL Redirection Clean, versioned approach Requires code changes
MoveFileEx Simple implementation Requires restart
Shadow Copy Enterprise-grade solution Complex setup

For advanced scenarios, consider runtime patching:

// Dangerous but powerful technique
byte[] patch = { 0x90, 0x90, 0x90 }; // NOP sled example
IntPtr targetAddress = GetProcAddress(module, "TargetFunction");
VirtualProtect(targetAddress, (UIntPtr)patch.Length, 
              0x40, out uint oldProtect); // PAGE_EXECUTE_READWRITE
Marshal.Copy(patch, 0, targetAddress, patch.Length);
VirtualProtect(targetAddress, (UIntPtr)patch.Length, 
              oldProtect, out _);

Remember that each solution carries different risk profiles and implementation complexity. Choose based on your specific requirements and operational constraints.


When maintaining game servers that rely on shared DLL files, administrators often face a dilemma: Windows locks loaded DLLs during runtime, preventing updates while servers are active. This creates operational headaches when you need to deploy patches or security fixes without disrupting active game sessions.

Windows maintains a reference count for loaded DLLs and enforces exclusive access to prevent version conflicts. When a process loads a DLL:

// Pseudocode of DLL loading mechanism
HANDLE hModule = LoadLibrary("gameserver.dll");
if(hModule) {
    // DLL is now locked by the process
    // Any attempt to modify the file will fail with ERROR_SHARING_VIOLATION
}

Here are three proven approaches to update DLLs without restarting running instances:

1. Versioned DLL Deployment

Deploy new versions with different filenames (e.g., gameserver_v2.dll) and modify the server's loading logic:

// In your server bootstrap code
string dllVersion = GetConfigValue("CurrentDLLVersion");
string dllPath = $"server_modules/gameserver_{dllVersion}.dll";
LoadLibrary(dllPath);

2. Delayed Replacement via Move Operations

Windows allows moving/renaming locked files:

// Command sequence
rename gameserver.dll gameserver.old
copy gameserver_new.dll gameserver.dll
// Running servers continue using gameserver.old
// New servers will load gameserver.dll

3. Using Shadow Copies (Volume Snapshot Service)

For advanced scenarios, leverage VSS:

// PowerShell example
$vss = (New-Object -ComObject "VSScripting.VSSCoordinator")
$vss.StartSnapshot("C:")
Copy-Item .\updated.dll C:\servers\bin\gameserver.dll
$vss.EndSnapshot()

While tools like Unlocker can force-close file handles, they pose significant risks:

  • Potential memory leaks in running processes
  • Undefined behavior if the DLL is actively being used
  • Possible corruption of both old and new instances

For enterprise-grade solutions consider:

  1. Implementing a module manifest system that checks DLL hashes
  2. Using memory-mapped files for critical components
  3. Designing servers with hot-reload capabilities via IPC
// Example IPC reload protocol
struct ReloadMessage {
    uint32_t magic = 0xDEADBEEF;
    char dll_path[256];
    bool graceful_restart;
};

A popular fantasy MMO uses this versioned approach:

// Their loading sequence
if(!LoadPrimaryDLL()) {
    // Fallback to previous version
    LoadFallbackDLL();
    Log("Using fallback version - update pending");
    NotifyPlayers("Maintenance incoming in 30 minutes");
}

The system maintains three versions simultaneously (current/previous/next) with automated health checks rolling back failed updates.