v0.23.2 — Fix AccessViolation pendant le démarrage de SteamVR

Cause : race condition. Quand l'utilisateur lance SteamVR alors que le
launcher tourne déjà, vrserver.exe apparaît dans la liste des process
avant que ses drivers et ses shared memories soient prêts à servir des
requêtes vtable. La séquence problématique :

  1. tick health loop → IsVrServerRunning() = true (vrserver vient juste
     d'apparaître dans la process list)
  2. VR_InitInternal2 → succeed (init est tolérant, ne lit pas la SHM)
  3. VR_GetGenericInterface → vtable pointer valide
  4. premier appel vtable (IsTrackedDeviceConnected, slot 21) → lit dans
     une shared memory pas encore allouée par vrserver → AccessViolation
  5. SEH passe au travers du CLR via Marshal.GetDelegateForFunctionPointer
     → process killed sans que catch (Exception) ne se déclenche.

Stack confirmée par l'Event Viewer Windows :
  System.AccessViolationException
    at OpenVrService.Query(System.String)
    at SystemHealthService.CheckVrDevice(...)
    at health loop background task

Fix : cooldown de 8 s après la première détection de vrserver. On note
le timestamp UTC de la première apparition, et on refuse l'init tant que
moins de 8 s n'ont passé. Pendant le warmup, la pill reste sur "SteamVR
non lancé" (HmdAbsent), puis bascule sur Ready au tick suivant. Si
vrserver disparaît, on reset le timestamp + on tear-down la session
proprement, prêts pour un prochain cycle de démarrage.

8 s = compromis : sur SSD le ramp-up des drivers SteamVR prend ~3-5 s,
sur HDD ça peut monter à 7 s. La marge couvre les deux.

Bonus diag : flush Serilog avant que le process meure dans le hook
AppDomain.UnhandledException. Sans ça, la trace de la fatale n'arrivait
jamais sur disque (exactement ce qu'on a vu dans les logs : silence
total avant le redémarrage suivant).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-03 20:08:32 +02:00
parent b94c0235b6
commit 07ead6011f
4 changed files with 69 additions and 11 deletions

View File

@@ -97,7 +97,19 @@ public partial class App : Application
Log.Information("PSLauncher starting (logs in {Path})", LogsDirectory);
AppDomain.CurrentDomain.UnhandledException += (_, args) =>
Log.Fatal((Exception)args.ExceptionObject, "Unhandled exception (AppDomain)");
{
// CRITIQUE : on flush AVANT que le process meure. Sans ça, sur les
// exceptions de corruption d'état (AccessViolation depuis P/Invoke,
// SEH natif…) le buffer Serilog n'est jamais écrit sur disque, et
// le crash est totalement invisible dans les logs — exactement le
// mode de panne qu'on a eu avec OpenVR pendant le démarrage SteamVR.
try
{
Log.Fatal((Exception)args.ExceptionObject, "Unhandled exception (AppDomain) IsTerminating={IsTerminating}", args.IsTerminating);
Log.CloseAndFlush();
}
catch { /* dernier recours — on ne peut plus rien faire */ }
};
DispatcherUnhandledException += (_, args) =>
{
Log.Error(args.Exception, "Unhandled UI exception");

View File

@@ -15,9 +15,9 @@
<Product>PROSERVE Launcher</Product>
<Copyright>© 2026 ASTERION VR — All rights reserved</Copyright>
<RootNamespace>PSLauncher.App</RootNamespace>
<Version>0.23.1</Version>
<AssemblyVersion>0.23.1.0</AssemblyVersion>
<FileVersion>0.23.1.0</FileVersion>
<Version>0.23.2</Version>
<AssemblyVersion>0.23.2.0</AssemblyVersion>
<FileVersion>0.23.2.0</FileVersion>
<!-- Single-file self-contained publish profile (used by `dotnet publish`) -->
<PublishSingleFile>true</PublishSingleFile>