Files
TimerApp/NativeMethods.cs

260 lines
8.6 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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);
/// <summary>
/// 获取系统空闲时间(毫秒)
/// </summary>
/// <returns></returns>
public static long GetIdleTime()
{
LASTINPUTINFO lastInputInfo = new LASTINPUTINFO
{
cbSize = (uint)Marshal.SizeOf<LASTINPUTINFO>(),
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();
/// <summary>
/// 检测是否有媒体正在播放(视频或音频)
/// </summary>
/// <returns>如果有媒体正在播放返回 true否则返回 false</returns>
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);
}
}
}