[UWP]:系统音量设置为关闭时的警报通知音频



大家好:)我们正在为当地消防部门编写一个应用程序,因为他们计划删除两个寻呼机频率之一并将其替换为一个应用程序(他们有两个警报通道,经典寻呼机和手机)。如果收到警报,手机会创建带有音频的警报方案 ToastNotification。到目前为止一切顺利,问题是如果用户将系统主音量设置为零,则不会播放音频文件。我知道到目前为止不可能从应用程序更改系统音量,但对于我的场景,无论手机音量设置等如何,我都需要播放音频通知。

如果有人知道如何克服这个问题,我将不胜感激:)

我知道

这不是你想听到的,但Win10Mobile设备是消费者设备。如果你买了一部手机,把它静音了,它仍然发出噪音,你会有什么感觉?

当一个人将手机静音时,他们说它不应该发出声音。您说要覆盖用户设置,这不是设备应该做的。

如果您有专业要求,则可能需要专业设备。我尚未检查,但可能有可用的设备(不是 UWP)没有用户可访问的硬件卷设置。

如果您无法控制正在使用的设备,那么我建议您获取(或制作?)一个隐藏音量按钮访问权限的情况。

归根结底,如果一个人负责在收到电话/消息时接听电话,那么在具有一组固定能力的设备上,您可以在代码中执行的操作只有这么多。

我遇到了完全相同的问题。幸运的是,我通过使用 P/Invoke 的 IAudioEndpointVolume from C# 接口找到了有效的解决方案。

using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using Windows.Media.Devices;
namespace AudioUtils
{
    public static class VolumeControl
    {
        public static void ChangeVolumeToMinLevel(double level)
        {
            if (level > 1)
                level = 1;
            else if (level < 0)
                level = 0;
            try
            {
                var masterVol = GetAudioEndpointVolume();
                if (masterVol == null)
                    return;
                var hr = masterVol.GetMute(out var muted);
                if (hr != (uint)HResult.S_OK)
                    return;
                if (muted)
                {
                    masterVol.SetMute(false, Guid.Empty);
                }
                // Only adapt volume if the current level is below the specified minumum level
                hr = masterVol.GetMasterVolumeLevelScalar(out float currentAudioValue);
                float newAudioValue = Convert.ToSingle(level);
                if (currentAudioValue > newAudioValue)
                    return;
                masterVol.SetMasterVolumeLevelScalar(newAudioValue, Guid.Empty);
            }
            catch { }
        }
        private static IAudioEndpointVolume GetAudioEndpointVolume()
        {
            var speakerId = MediaDevice.GetDefaultAudioRenderId(AudioDeviceRole.Default);
            var completionHandler = new ActivateAudioInterfaceCompletionHandler<IAudioEndpointVolume>();
            var hr = ActivateAudioInterfaceAsync(
                speakerId,
                typeof(IAudioEndpointVolume).GetTypeInfo().GUID,
                IntPtr.Zero,
                completionHandler,
                out var activateOperation);
            Debug.Assert(hr == (uint)HResult.S_OK);
            return completionHandler.WaitForCompletion();
        }
        [DllImport("Mmdevapi.dll", ExactSpelling = true, PreserveSig = false)]
        [return: MarshalAs(UnmanagedType.Error)]
        private static extern uint ActivateAudioInterfaceAsync(
                [In, MarshalAs(UnmanagedType.LPWStr)]string deviceInterfacePath,
                [In, MarshalAs(UnmanagedType.LPStruct)]Guid riid,
                [In] IntPtr activationParams,
                [In] IActivateAudioInterfaceCompletionHandler completionHandler,
                out IActivateAudioInterfaceAsyncOperation activationOperation);
    }
    internal class ActivateAudioInterfaceCompletionHandler<T> : IActivateAudioInterfaceCompletionHandler
    {
        private AutoResetEvent _completionEvent;
        private T _result;
        public ActivateAudioInterfaceCompletionHandler()
        {
            _completionEvent = new AutoResetEvent(false);
        }
        public void ActivateCompleted(IActivateAudioInterfaceAsyncOperation operation)
        {
            operation.GetActivateResult(out var hr, out var activatedInterface);
            Debug.Assert(hr == (uint)HResult.S_OK);
            _result = (T)activatedInterface;
            var setResult = _completionEvent.Set();
            Debug.Assert(setResult != false);
        }
        public T WaitForCompletion()
        {
            var waitResult = _completionEvent.WaitOne();
            Debug.Assert(waitResult != false);
            return _result;
        }
    }
    internal enum HResult : uint
    {
        S_OK = 0
    }
    [ComImport]
    [Guid("5CDF2C82-841E-4546-9722-0CF74078229A"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    internal interface IAudioEndpointVolume
    {
        [PreserveSig]
        int NotImpl1();
        [PreserveSig]
        int NotImpl2();
        [PreserveSig]
        int GetChannelCount([Out] [MarshalAs(UnmanagedType.U4)] out uint channelCount);
        [PreserveSig]
        int SetMasterVolumeLevel(
            [In] [MarshalAs(UnmanagedType.R4)] float level,
            [In] [MarshalAs(UnmanagedType.LPStruct)] Guid eventContext);
        [PreserveSig]
        int SetMasterVolumeLevelScalar(
            [In] [MarshalAs(UnmanagedType.R4)] float level,
            [In] [MarshalAs(UnmanagedType.LPStruct)] Guid eventContext);
        [PreserveSig]
        int GetMasterVolumeLevel([Out] [MarshalAs(UnmanagedType.R4)] out float level);
        [PreserveSig]
        int GetMasterVolumeLevelScalar([Out] [MarshalAs(UnmanagedType.R4)] out float level);
        [PreserveSig]
        int SetChannelVolumeLevel(
            [In] [MarshalAs(UnmanagedType.U4)] UInt32 channelNumber,
            [In] [MarshalAs(UnmanagedType.R4)] float level,
            [In] [MarshalAs(UnmanagedType.LPStruct)] Guid eventContext);
        [PreserveSig]
        int SetChannelVolumeLevelScalar(
            [In] [MarshalAs(UnmanagedType.U4)] uint channelNumber,
            [In] [MarshalAs(UnmanagedType.R4)] float level,
            [In] [MarshalAs(UnmanagedType.LPStruct)] Guid eventContext);
        int GetChannelVolumeLevel(
            [In] [MarshalAs(UnmanagedType.U4)] uint channelNumber,
            [Out] [MarshalAs(UnmanagedType.R4)] out float level);
        [PreserveSig]
        int GetChannelVolumeLevelScalar(
            [In] [MarshalAs(UnmanagedType.U4)] uint channelNumber,
            [Out] [MarshalAs(UnmanagedType.R4)] out float level);
        [PreserveSig]
        int SetMute(
            [In] [MarshalAs(UnmanagedType.Bool)] bool isMuted,
            [In] [MarshalAs(UnmanagedType.LPStruct)] Guid eventContext);
        [PreserveSig]
        int GetMute([Out] [MarshalAs(UnmanagedType.Bool)] out bool isMuted);
        [PreserveSig]
        int GetVolumeStepInfo(
            [Out] [MarshalAs(UnmanagedType.U4)] out uint step,
            [Out] [MarshalAs(UnmanagedType.U4)] out uint stepCount);
        [PreserveSig]
        int VolumeStepUp([In] [MarshalAs(UnmanagedType.LPStruct)] Guid eventContext);
        [PreserveSig]
        int VolumeStepDown([In] [MarshalAs(UnmanagedType.LPStruct)] Guid eventContext);
        [PreserveSig]
        int QueryHardwareSupport([Out] [MarshalAs(UnmanagedType.U4)] out uint hardwareSupportMask);
        [PreserveSig]
        int GetVolumeRange(
            [Out] [MarshalAs(UnmanagedType.R4)] out float volumeMin,
            [Out] [MarshalAs(UnmanagedType.R4)] out float volumeMax,
            [Out] [MarshalAs(UnmanagedType.R4)] out float volumeStep);
    }
    [ComImport]
    [Guid("72A22D78-CDE4-431D-B8CC-843A71199B6D")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    internal interface IActivateAudioInterfaceAsyncOperation
    {
        void GetActivateResult(
            [MarshalAs(UnmanagedType.Error)]out uint activateResult,
            [MarshalAs(UnmanagedType.IUnknown)]out object activatedInterface);
    }
    [ComImport]
    [Guid("41D949AB-9862-444A-80F6-C261334DA5EB")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    internal interface IActivateAudioInterfaceCompletionHandler
    {
        void ActivateCompleted(IActivateAudioInterfaceAsyncOperation activateOperation);
    }
}

学分

  • Reddit用户sunius。请参阅此线程和此代码。
  • GitHub 用户 vannatech。请参阅此代码。

sunius提供的代码有两个主要缺点:

  • 它使用不安全的代码
  • 它在发布模式下不起作用(应用程序崩溃)

为了摆脱不安全的代码和崩溃,使用了更明确的编组属性并保留了签名(参见vannatech源代码)

最新更新