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:
@@ -93,10 +93,16 @@ public static class OpenVRNative
|
||||
Idle_Timeout = 4,
|
||||
}
|
||||
|
||||
// ATTENTION : OpenVR renvoie des bool C++ (1 octet). Sans MarshalAs(I1), le
|
||||
// marshaller .NET lit 4 octets (BOOL Win32) et obtient des valeurs random ;
|
||||
// c'est ce qui faisait passer VR_IsHmdPresent à "true" même casque débranché,
|
||||
// déclenchant ensuite une init qui plante quand SteamVR n'est pas lancé.
|
||||
[DllImport(DllName, EntryPoint = "VR_IsRuntimeInstalled", CallingConvention = CallingConvention.Cdecl)]
|
||||
[return: MarshalAs(UnmanagedType.I1)]
|
||||
public static extern bool VR_IsRuntimeInstalled();
|
||||
|
||||
[DllImport(DllName, EntryPoint = "VR_IsHmdPresent", CallingConvention = CallingConvention.Cdecl)]
|
||||
[return: MarshalAs(UnmanagedType.I1)]
|
||||
public static extern bool VR_IsHmdPresent();
|
||||
|
||||
[DllImport(DllName, EntryPoint = "VR_InitInternal2", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user