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.
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_UNLOCKevents 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
OnApplicationQuitto avoid leaks.
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 = falsefor 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:
For large GPU assets (like 4K textures), consider manually releasing them when entering the background and reloading when returning to the foreground.Resources.UnloadUnusedAssets(); System.GC.Collect(); - 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.
Combine the two solutions above to eliminate the crash:
- When
WTS_SESSION_LOCK/WTS_SESSION_LOGOFFtriggers, immediately pause rendering, unload non-critical GPU resources, and throttle the frame rate. - When
WTS_SESSION_UNLOCK/WTS_SESSION_LOGONtriggers, 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

