You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity独立版exe切换Windows用户崩溃:检测与内存优化问询

Let’s tackle your two key questions first, then tie them back to fixing that nasty 0xc0000005 access violation you’re seeing.


Detecting Windows User Switches in Unity C#

Unity doesn’t have a built-in API for tracking Windows session changes, so you’ll need to hook into Windows’ native Terminal Services (WTS) API. Here’s a drop-in MonoBehaviour implementation that detects user locks/unlocks, logons/logoffs—exactly the events you need for user switches:

using System;
using System.Runtime.InteropServices;
using UnityEngine;

public class UserSwitchDetector : MonoBehaviour
{
    // Windows API constants for session events
    private const int WM_WTSSESSION_CHANGE = 0x02B1;
    private const int WTS_SESSION_LOCK = 0x7;
    private const int WTS_SESSION_UNLOCK = 0x8;
    private const int WTS_SESSION_LOGOFF = 0x6;
    private const int WTS_SESSION_LOGON = 0x5;

    [DllImport("user32.dll")]
    private static extern IntPtr GetActiveWindow();

    [DllImport("Wtsapi32.dll")]
    private static extern bool WTSRegisterSessionNotification(IntPtr hWnd, uint dwFlags);

    [DllImport("Wtsapi32.dll")]
    private static extern bool WTSUnRegisterSessionNotification(IntPtr hWnd);

    private IntPtr _unityWindowHandle;

    void Start()
    {
        _unityWindowHandle = GetActiveWindow();
        // Register our window to receive session change notifications
        WTSRegisterSessionNotification(_unityWindowHandle, 0);
    }

    void OnApplicationQuit()
    {
        // Clean up the registration to avoid memory leaks
        WTSUnRegisterSessionNotification(_unityWindowHandle);
    }

    void OnGUI()
    {
        // Process Windows messages through Unity's message loop
        var msg = new MSG();
        while (PeekMessage(out msg, IntPtr.Zero, 0, 0, 1))
        {
            TranslateMessage(ref msg);
            DispatchMessage(ref msg);

            if (msg.message == WM_WTSSESSION_CHANGE)
            {
                switch (msg.wParam.ToInt32())
                {
                    case WTS_SESSION_LOCK:
                        Debug.Log("User switched away (session locked)");
                        // Trigger background resource cleanup here
                        HandleEnterBackground();
                        break;
                    case WTS_SESSION_UNLOCK:
                        Debug.Log("User switched back (session unlocked)");
                        // Restore resources and resume operations here
                        HandleEnterForeground();
                        break;
                    case WTS_SESSION_LOGOFF:
                        Debug.Log("User logged off from session");
                        HandleEnterBackground();
                        break;
                    case WTS_SESSION_LOGON:
                        Debug.Log("User logged into session");
                        HandleEnterForeground();
                        break;
                }
            }
        }
    }

    // Helper methods to handle state changes
    private void HandleEnterBackground()
    {
        // Add your background logic here (e.g., pause rendering, release resources)
    }

    private void HandleEnterForeground()
    {
        // Add your foreground logic here (e.g., resume rendering, reload resources)
    }

    // Required Windows API structs and functions for message processing
    [StructLayout(LayoutKind.Sequential)]
    private struct MSG
    {
        public IntPtr hwnd;
        public uint message;
        public IntPtr wParam;
        public IntPtr lParam;
        public uint time;
        public POINT pt;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct POINT
    {
        public int x;
        public int y;
    }

    [DllImport("user32.dll")]
    private static extern bool PeekMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax, uint wRemoveMsg);

    [DllImport("user32.dll")]
    private static extern bool TranslateMessage(ref MSG lpMsg);

    [DllImport("user32.dll")]
    private static extern IntPtr DispatchMessage(ref MSG lpMsg);
}

Key Notes:

  • The WTS_SESSION_LOCK/WTS_SESSION_UNLOCK events are your primary triggers for user switches (since most users lock their session instead of logging out).
  • Always clean up the session notification registration in OnApplicationQuit to avoid leaks.

Reducing Memory/GPU Usage When Running in the Background

Your 0xc0000005 crash is almost certainly caused by Windows reclaiming GPU resources for the active user, while your app still tries to access them. Here’s how to slim down your app’s footprint when in the background:

  • Pause GPU rendering: Disable non-essential cameras, turn off post-processing, or set Camera.enabled = false for your main camera. You can also temporarily drop quality levels with:
    QualitySettings.SetQualityLevel(0, true); // 0 = lowest quality
    
  • Unload unused resources: Free up memory by cleaning up unneeded assets:
    Resources.UnloadUnusedAssets();
    System.GC.Collect();
    
    For large GPU assets (like 4K textures), consider manually releasing them when entering the background and reloading when returning to the foreground.
  • Throttle frame rate: Reduce CPU/GPU load by limiting the frame rate:
    Application.targetFrameRate = 5; // Or 0 to let Windows control it
    
  • Leverage Unity’s render device callbacks: Handle GPU resource loss gracefully with these built-in methods:
    void OnRenderDeviceLost()
    {
        Debug.Log("GPU resources lost—cleaning up");
        Resources.UnloadUnusedAssets();
        System.GC.Collect();
    }
    
    void OnRenderDeviceReinitialized()
    {
        Debug.Log("GPU resources restored—reloading assets");
        // Reload critical assets here
    }
    
  • Pause animations and coroutines: Stop any unnecessary particle systems, animations, or coroutines that don’t need to run in the background.

Fixing the Access Violation (0xc0000005) Crash

Combine the two solutions above to eliminate the crash:

  1. When WTS_SESSION_LOCK/WTS_SESSION_LOGOFF triggers, immediately pause rendering, unload non-critical GPU resources, and throttle the frame rate.
  2. When WTS_SESSION_UNLOCK/WTS_SESSION_LOGON triggers, reload resources, resume rendering, and restore normal quality/frame rate settings.

Also, check your Player Settings: If "Run In Background" is enabled, Unity keeps running when inactive (required for detecting user switches), but demands careful resource management. If you don’t need the app to run in the background, disabling this setting will pause the app entirely when inactive, which can prevent the crash without extra code.

内容的提问来源于stack exchange,提问作者MBS

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 03:22:42