5ce2c8a982
为 Claude Code 提供原生 Windows toast 通知:点击跳回原窗口、切回 Windows Terminal 标签、跨虚拟桌面、调用方图标、非阻塞投递;NativeAOT 单文件分发。
68 lines
1.7 KiB
C#
68 lines
1.7 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Runtime.InteropServices;
|
|
using Avalonia.Platform;
|
|
|
|
namespace Notify.Interop;
|
|
|
|
/// <summary>
|
|
/// 播放打包的提示音 wav
|
|
///
|
|
/// 用 winmm 的 PlaySound 从内存异步播放;为配合 SND_ASYNC,wav 拷到不会被 GC
|
|
/// 移动的非托管内存里常驻
|
|
/// </summary>
|
|
internal static partial class Sound
|
|
{
|
|
private const uint SND_ASYNC = 0x0001;
|
|
private const uint SND_NODEFAULT = 0x0002;
|
|
private const uint SND_MEMORY = 0x0004;
|
|
|
|
private static IntPtr _wavPtr;
|
|
private static DateTime _lastPlay = DateTime.MinValue;
|
|
|
|
[LibraryImport("winmm.dll", EntryPoint = "PlaySoundW")]
|
|
[return: MarshalAs(UnmanagedType.Bool)]
|
|
private static partial bool PlaySound(IntPtr pszSound, IntPtr hmod, uint fdwSound);
|
|
|
|
public static void Play()
|
|
{
|
|
// 防连环音:300ms 内只响一次
|
|
var now = DateTime.UtcNow;
|
|
if ((now - _lastPlay).TotalMilliseconds < 300)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_lastPlay = now;
|
|
|
|
try
|
|
{
|
|
EnsureLoaded();
|
|
if (_wavPtr != IntPtr.Zero)
|
|
{
|
|
PlaySound(_wavPtr, IntPtr.Zero, SND_MEMORY | SND_ASYNC | SND_NODEFAULT);
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// 播放失败无所谓
|
|
}
|
|
}
|
|
|
|
private static void EnsureLoaded()
|
|
{
|
|
if (_wavPtr != IntPtr.Zero)
|
|
{
|
|
return;
|
|
}
|
|
|
|
using var s = AssetLoader.Open(new Uri("avares://notify/Assets/notification.wav"));
|
|
using var ms = new MemoryStream();
|
|
s.CopyTo(ms);
|
|
var bytes = ms.ToArray();
|
|
|
|
_wavPtr = Marshal.AllocHGlobal(bytes.Length);
|
|
Marshal.Copy(bytes, 0, _wavPtr, bytes.Length);
|
|
}
|
|
}
|