using System; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; namespace TimerApp { public static class NativeMethods { [StructLayout(LayoutKind.Sequential)] struct LASTINPUTINFO { public static readonly int SizeOf = Marshal.SizeOf(typeof(LASTINPUTINFO)); [MarshalAs(UnmanagedType.U4)] public UInt32 cbSize; [MarshalAs(UnmanagedType.U4)] public UInt32 dwTime; } [DllImport("user32.dll")] static extern bool GetLastInputInfo(ref LASTINPUTINFO plii); /// /// 获取系统空闲时间(毫秒) /// /// public static long GetIdleTime() { LASTINPUTINFO lastInputInfo = new LASTINPUTINFO { cbSize = (uint)Marshal.SizeOf(), dwTime = 0 }; if (GetLastInputInfo(ref lastInputInfo)) { uint tickCount = GetTickCount(); uint idleMs = unchecked(tickCount - lastInputInfo.dwTime); return idleMs; } return 0; } [DllImport("kernel32.dll")] static extern uint GetTickCount(); /// /// 检测是否有媒体正在播放(视频或音频) /// /// 如果有媒体正在播放返回 true,否则返回 false public static bool IsMediaPlaying() { EnsureMediaPlaybackStatusFresh(); return Volatile.Read(ref _mediaPlaying); } private static bool _mediaPlaying; private static long _mediaLastUpdateMs = -1; private static int _mediaUpdateInProgress; private static void EnsureMediaPlaybackStatusFresh() { long now = Environment.TickCount64; long last = Interlocked.Read(ref _mediaLastUpdateMs); if (last >= 0 && now - last < 3000) { return; } if (Interlocked.Exchange(ref _mediaUpdateInProgress, 1) == 1) { return; } _ = Task.Run(async () => { bool playing = false; try { await Task.Yield(); playing = TryIsAudioPlaying(); } catch { playing = false; } finally { Volatile.Write(ref _mediaPlaying, playing); Interlocked.Exchange(ref _mediaLastUpdateMs, Environment.TickCount64); Interlocked.Exchange(ref _mediaUpdateInProgress, 0); } }); } private static bool TryIsAudioPlaying() { object? deviceEnumeratorObj = null; IMMDeviceEnumerator? deviceEnumerator = null; IMMDevice? device = null; object? sessionManagerObj = null; IAudioSessionManager2? sessionManager = null; IAudioSessionEnumerator? sessionEnumerator = null; try { Type? enumeratorType = Type.GetTypeFromCLSID(CLSID_MMDeviceEnumerator); if (enumeratorType is null) return false; deviceEnumeratorObj = Activator.CreateInstance(enumeratorType); if (deviceEnumeratorObj is null) return false; deviceEnumerator = (IMMDeviceEnumerator)deviceEnumeratorObj; Marshal.ThrowExceptionForHR(deviceEnumerator.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia, out device)); Guid iid = typeof(IAudioSessionManager2).GUID; Marshal.ThrowExceptionForHR(device.Activate(ref iid, CLSCTX.CLSCTX_ALL, IntPtr.Zero, out sessionManagerObj)); if (sessionManagerObj is null) return false; sessionManager = (IAudioSessionManager2)sessionManagerObj; Marshal.ThrowExceptionForHR(sessionManager.GetSessionEnumerator(out sessionEnumerator)); Marshal.ThrowExceptionForHR(sessionEnumerator.GetCount(out int count)); for (int i = 0; i < count; i++) { Marshal.ThrowExceptionForHR(sessionEnumerator.GetSession(i, out IAudioSessionControl? sessionControl)); if (sessionControl is null) continue; try { Marshal.ThrowExceptionForHR(sessionControl.GetState(out AudioSessionState state)); if (state != AudioSessionState.Active) continue; if (sessionControl is IAudioMeterInformation meter) { Marshal.ThrowExceptionForHR(meter.GetPeakValue(out float peak)); if (peak > 0.001f) return true; } else { return true; } } finally { Marshal.FinalReleaseComObject(sessionControl); } } return false; } catch { return false; } finally { if (sessionEnumerator is not null) Marshal.FinalReleaseComObject(sessionEnumerator); if (sessionManagerObj is not null) Marshal.FinalReleaseComObject(sessionManagerObj); if (device is not null) Marshal.FinalReleaseComObject(device); if (deviceEnumeratorObj is not null) Marshal.FinalReleaseComObject(deviceEnumeratorObj); } } private static readonly Guid CLSID_MMDeviceEnumerator = new Guid("BCDE0395-E52F-467C-8E3D-C4579291692E"); private enum EDataFlow { eRender = 0, eCapture = 1, eAll = 2 } private enum ERole { eConsole = 0, eMultimedia = 1, eCommunications = 2 } private enum AudioSessionState { Inactive = 0, Active = 1, Expired = 2 } [Flags] private enum CLSCTX : uint { CLSCTX_INPROC_SERVER = 0x1, CLSCTX_INPROC_HANDLER = 0x2, CLSCTX_LOCAL_SERVER = 0x4, CLSCTX_REMOTE_SERVER = 0x10, CLSCTX_ALL = CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER } [ComImport] [Guid("A95664D2-9614-4F35-A746-DE8DB63617E6")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] private interface IMMDeviceEnumerator { int NotImpl1(); int GetDefaultAudioEndpoint(EDataFlow dataFlow, ERole role, out IMMDevice ppDevice); } [ComImport] [Guid("D666063F-1587-4E43-81F1-B948E807363F")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] private interface IMMDevice { int Activate(ref Guid iid, CLSCTX dwClsCtx, IntPtr pActivationParams, [MarshalAs(UnmanagedType.IUnknown)] out object ppInterface); } [ComImport] [Guid("77AA99A0-1BD6-484F-8BC7-2C654C9A9B6F")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] private interface IAudioSessionManager2 { int NotImpl1(); int NotImpl2(); int GetSessionEnumerator(out IAudioSessionEnumerator sessionEnum); } [ComImport] [Guid("E2F5BB11-0570-40CA-ACDD-3AA01277DEE8")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] private interface IAudioSessionEnumerator { int GetCount(out int sessionCount); int GetSession(int sessionCount, out IAudioSessionControl session); } [ComImport] [Guid("F4B1A599-7266-4319-A8CA-E70ACB11E8CD")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] private interface IAudioSessionControl { int NotImpl1(); int GetState(out AudioSessionState state); } [ComImport] [Guid("C02216F6-8C67-4B5B-9D00-D008E73E0064")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] private interface IAudioMeterInformation { int GetPeakValue(out float peak); } } }