Quick answer: Three causes dominate: missing runtimes (VC++, .NET, DirectX), antivirus quarantine, and DLL hijacking. Add phased logging to disk, show a “Open log folder” button on failure, and code-sign every binary you ship. Unsigned launchers are a first-day support nightmare.
Your launcher works on every dev machine. Players report it silently disappears after double-click. No window, no error. No log. You cannot debug what doesn’t crash with a visible stack. Launcher failures need their own instrumentation because the usual crash reporter never runs.
Phased Logging
Log every phase of launcher startup to a file the moment it begins. Even if the process dies in phase 3, phases 1-2 are on disk.
void LaunchGame()
{
Logger.Phase("Init");
InitLogging();
Logger.Phase("CheckRuntime");
if (!CheckVCRuntimeInstalled()) { Logger.Error("VC++ missing"); ShowRuntimeError(); return; }
Logger.Phase("CheckFiles");
foreach (var f in _criticalFiles) {
if (!File.Exists(f)) { Logger.Error($"Missing: {f}"); ShowIntegrityError(); return; }
}
Logger.Phase("LaunchGame");
var p = Process.Start(gameExe);
Logger.Info($"Game PID: {p.Id}");
}
Write to %LOCALAPPDATA%/YourGame/launcher.log. On failure, pop a dialog with an “Open log folder” button. Players who can find the log and send it will.
The Three Common Causes
1. Missing runtimes. Visual C++ Redistributable, .NET Desktop Runtime, DirectX components. Bundle the installers with your game and run them on first launch if missing. Don’t assume system installations are present.
2. Antivirus quarantine. Windows Defender, Bitdefender, Kaspersky, Avast flag unsigned executables as suspicious. A launcher that packs assets or uses a custom installer is especially likely to get quarantined. Code-sign every shipped binary with a real certificate (not self-signed) and submit to Microsoft’s SmartScreen attestation service. Signed binaries trip far fewer false-positives.
3. DLL hijacking. Windows searches the exe’s directory for DLLs before system paths. A malicious or stale DLL named version.dll or dxgi.dll next to your exe loads instead of the system one. Use SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32) at launcher entry to force system DLLs first.
Capturing Process Start Failures
If Process.Start silently fails (exit code non-zero, no window), capture stderr and exit code:
var psi = new ProcessStartInfo(gameExe) {
RedirectStandardError = true,
RedirectStandardOutput = true,
UseShellExecute = false
};
var p = Process.Start(psi);
string stderr = p.StandardError.ReadToEnd();
p.WaitForExit(5000);
Logger.Info($"Exit code: {p.ExitCode}, stderr: {stderr}");
Non-zero exit codes often indicate DLL load failures that are otherwise silent.
Remote Log Upload
For launcher failures, add an automatic log upload to a backend (with a consent prompt). Players who don’t know how to send a zip file still produce telemetry. The backend groups failures by error category and you see systemic issues (e.g. a Win7 DX12 regression) within hours.
“The launcher is the one piece of your game where you can’t rely on a crash report. Log everything to disk, and build tools for support to ask for the log in one click.”
Related Issues
For broader crash handling, see how to write a crash report submission flow. For preventing false-positive AV detections, see how to reduce false positive crash reports from anti-cheat.
Code-sign every binary. Unsigned launchers are the single biggest cause of first-week support tickets for indie PC releases.