大家好:)我们正在为当地消防部门编写一个应用程序,因为他们计划删除两个寻呼机频率之一并将其替换为一个应用程序(他们有两个警报通道,经典寻呼机和手机)。如果收到警报,手机会创建带有音频的警报方案 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源代码)