v0.23.1 — Fix crash OpenVR quand SteamVR non lancé

Deux bugs corrigés :

1. Marshalling bool incorrect sur VR_IsHmdPresent / VR_IsRuntimeInstalled.
   OpenVR renvoie un bool C++ (1 octet) ; sans MarshalAs(I1) le marshaller
   .NET lit 4 octets (Win32 BOOL) et obtient des valeurs random. Conséquence :
   VR_IsHmdPresent passait parfois à "true" même si aucun casque n'était
   détecté, et on tentait alors une init OpenVR sans backend → crash.

2. Garde process avant init. Même avec le bool marshalling correct,
   VR_IsHmdPresent peut renvoyer true si un HMD est branché en USB sans
   que SteamVR soit lancé. VR_InitInternal2 en mode Background tente alors
   de charger les drivers (lighthouse, oculus_link…) qui peuvent crash en
   SEH si vrserver n'a jamais été démarré sur cette session. On ajoute
   une vérification de la présence de vrserver.exe avant de tenter l'init,
   avec cache 2 s pour ne pas faire un GetProcessesByName à chaque tick.

Résultat : si SteamVR est installé mais pas lancé → pill "SteamVR pas lancé"
proprement, sans tenter d'init et donc sans risque de crash.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-03 19:38:35 +02:00
parent 4d0e287227
commit c997859750
4 changed files with 56 additions and 4 deletions

View File

@@ -1,6 +1,8 @@
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Extensions.Logging;
// Alias pour éviter la collision avec le namespace PSLauncher.Core.Process
using SysProcess = System.Diagnostics.Process;
namespace PSLauncher.Core.Health.OpenVRInterop;
@@ -142,6 +144,17 @@ public sealed class OpenVrService : IOpenVrService, IDisposable
return VrRuntimeState.HmdAbsent;
}
// Garde supplémentaire avant l'init : vrserver.exe DOIT être actif. Sans
// lui, VR_InitInternal2 en mode Background peut soit hang, soit charger
// des drivers (lighthouse, oculus_link…) qui plantent en SEH si SteamVR
// n'a jamais été démarré sur cette session. Cette garde évite ce scénario
// au prix de ne plus rien lire jusqu'à ce que l'utilisateur lance SteamVR
// — ce qui est le comportement attendu de toute façon.
if (!IsVrServerRunning())
{
return VrRuntimeState.HmdAbsent;
}
// Init en mode Background : on lit les states sans devenir une scene app.
// L'init peut prendre 100-500 ms la première fois ; bloquer le caller est OK
// car le health loop tourne sur thread dédié.
@@ -304,6 +317,39 @@ public sealed class OpenVrService : IOpenVrService, IDisposable
catch { return null; }
}
/// <summary>
/// vrserver.exe est le process central de SteamVR ; sa présence garantit qu'on
/// peut init OpenVR sans déclencher de chargement de drivers risqué. Cache de
/// 2 s pour ne pas faire un GetProcessesByName à chaque tick.
/// </summary>
private DateTime _vrServerCacheUntil = DateTime.MinValue;
private bool _vrServerCacheValue;
private bool IsVrServerRunning()
{
var now = DateTime.UtcNow;
if (now < _vrServerCacheUntil) return _vrServerCacheValue;
try
{
var procs = SysProcess.GetProcessesByName("vrserver");
try
{
_vrServerCacheValue = procs.Length > 0;
}
finally
{
foreach (var p in procs) p.Dispose();
}
}
catch (Exception ex)
{
_logger.LogDebug(ex, "vrserver process probe failed, assuming absent");
_vrServerCacheValue = false;
}
_vrServerCacheUntil = now.AddSeconds(2);
return _vrServerCacheValue;
}
private void MarkSessionBroken()
{
ShutdownInternal();