winform实现托盘语音提醒
测试环境:
visual studio 2022
window 10
.net framework 4.6
本文实现的功能有:
1 托盘最小化
2 语音定时播放
3 检测到操作系统被客户点静音后,需要程序控制开启音量(在运行过程中,由于语音重复播放,客户很烦,就会点静音,后面搞了一个在客户点静音后,如果客户一直没处理问题,就在10分钟后重新开启音量播放音频)
4 连接sql server数据库,如果不需要可以把代码注释掉
步骤如下:
1 新增名为DrawerAudio的winfrom程序,默认带出的窗体重新命名为:DrawerAudio
2 nuget安装NAudio,版本选择1.9.0,如下图:
3 网上找一张icon的图片,重新命名为ico_audio.ico,并放到debug目录,接着网上找一个要播放的mp3音频,并重新命名为"发生药槽错误,请盘点变红的药槽.mp3"
4 App.config配置文件编辑如下:
<?xml version="1.0" encoding="utf-8" ?>
<configuration><startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" /></startup><connectionStrings><add name="Sql" connectionString="server=127.0.0.1;uid=sa;pwd=密码;database=sql server数据库名称"/></connectionStrings><appSettings><add key="IsTest" value="0" /><add key="SetAudioVolume" value="60" /><add key="MuteMinute" value="10" /></appSettings>
</configuration>
IsTest为1为测试模式,0为正式运行模式
SetAudioValume是设置音量
MuteMinute为最小音量,为了客户把音量调得过小,小于该音量后,程序自动把音量调大
5 新建名为SqlHelper的类并编辑如下(需要添加System.Configuration.dll引用):
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;namespace DrawerAudio
{// Token: 0x02000003 RID: 3internal class SqlHelper{// Token: 0x06000004 RID: 4 RVA: 0x00002124 File Offset: 0x00000324public static string GetSqlConnectionString(){return System.Configuration.ConfigurationManager.ConnectionStrings["Sql"].ConnectionString;}// Token: 0x06000005 RID: 5 RVA: 0x0000214C File Offset: 0x0000034Cpublic static IList<T> ExecuteDataTable<T>(string sqlText, params SqlParameter[] parameters) where T : class, new(){IList<T> result;using (SqlDataAdapter sqlDataAdapter = new SqlDataAdapter(sqlText, SqlHelper.GetSqlConnectionString())){DataTable dataTable = new DataTable();sqlDataAdapter.SelectCommand.Parameters.AddRange(parameters);sqlDataAdapter.Fill(dataTable);IList<T> list = new List<T>();PropertyInfo[] properties = typeof(T).GetProperties();for (int i = 0; i < dataTable.Rows.Count; i++){T t = Activator.CreateInstance<T>();foreach (PropertyInfo propertyInfo in properties){propertyInfo.SetValue(t, dataTable.Rows[i][propertyInfo.Name], null);}list.Add(t);}result = list;}return result;}}
}
6 新建名为AudioHelper的类并编辑如下:
using NAudio.CoreAudioApi;
using NAudio.Wave;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;namespace DrawerAudio
{public class AudioHelper{private static bool IsPlaying = false;private static WaveOut waveOut;private static Mp3FileReader reader;private static string audioName;private static object lockobj = new object();public static bool Init(string audioName){AudioHelper.audioName = audioName;AudioHelper.IsPlaying = true;return true;}public static bool GetIsPalying(){ return IsPlaying;}public static bool CheckCanStart(){if (waveOut!=null&&waveOut.PlaybackState == PlaybackState.Playing){return false;}return true;}public static void PlayAudio(){try{lock (lockobj){using (var ms = File.OpenRead(audioName)){using (reader = new Mp3FileReader(ms))using (var wavStream = WaveFormatConversionStream.CreatePcmStream(reader))using (var baStream = new BlockAlignReductionStream(wavStream))using (waveOut = new WaveOut(WaveCallbackInfo.FunctionCallback())){waveOut.Init(baStream);waveOut.Play();while (waveOut.PlaybackState == PlaybackState.Playing&& AudioHelper.GetIsPalying()){Thread.Sleep(100);}}}}}catch (Exception ex){Console.WriteLine("异常信息:"+ex.Message);}}public static bool StopPlay(){try{if (waveOut != null){IsPlaying = false;waveOut.Stop();}}catch (Exception ex){Console.WriteLine("停止错误:"+ex.Message);}return true;}/// <summary>/// 获取当前音量/// </summary>/// <returns></returns>public static int GetCurrentSpeakerVolume(){int volume = 0;var enumerator = new MMDeviceEnumerator();//获取音频输出设备IEnumerable<MMDevice> speakDevices = enumerator.EnumerateAudioEndPoints(DataFlow.Render, DeviceState.Active).ToArray();if (speakDevices.Count() > 0){MMDevice mMDevice = speakDevices.ToList()[0];volume = Convert.ToInt16(mMDevice.AudioEndpointVolume.MasterVolumeLevelScalar * 100);}return volume;}public static void SetCurrentSpeakerVolume(int volume){var enumerator = new MMDeviceEnumerator();IEnumerable<MMDevice> speakDevices = enumerator.EnumerateAudioEndPoints(DataFlow.Render, DeviceState.Active).ToArray();if (speakDevices.Count() > 0){MMDevice mMDevice = speakDevices.ToList()[0];mMDevice.AudioEndpointVolume.MasterVolumeLevelScalar = volume / 100.0f;}}/// <summary>/// 检测是否是静音/// </summary>/// <returns></returns>public static bool CheckIsNoNoise(){var enumerator = new MMDeviceEnumerator();IEnumerable<MMDevice> speakDevices = enumerator.EnumerateAudioEndPoints(DataFlow.Render, DeviceState.Active).ToArray();MMDevice mMDevice = speakDevices.ToList()[0];return mMDevice.AudioEndpointVolume.Mute;}/// <summary>/// 关闭静音/// </summary>public static void CloseNoNoise(){var enumerator = new MMDeviceEnumerator();IEnumerable<MMDevice> speakDevices = enumerator.EnumerateAudioEndPoints(DataFlow.Render, DeviceState.Active).ToArray();MMDevice mMDevice = speakDevices.ToList()[0];mMDevice.AudioEndpointVolume.Mute = false;}}
}
7 新建Model目录,并新建名为DrawerModel的类,并编辑如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace DrawerAudio.Model
{// Token: 0x02000004 RID: 4public class DrawerModel{// Token: 0x17000001 RID: 1// (get) Token: 0x06000007 RID: 7 RVA: 0x00002245 File Offset: 0x00000445// (set) Token: 0x06000006 RID: 6 RVA: 0x0000223C File Offset: 0x0000043Cpublic int IdDrawer { get; set; }// Token: 0x17000002 RID: 2// (get) Token: 0x06000009 RID: 9 RVA: 0x00002256 File Offset: 0x00000456// (set) Token: 0x06000008 RID: 8 RVA: 0x0000224D File Offset: 0x0000044Dpublic int IdProduct { get; set; }}
}
8 编辑FrmMain如下:
using DrawerAudio.Model;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;namespace DrawerAudio
{public partial class FrmMain : Form{bool isTest = ConfigurationManager.AppSettings["IsTest"].Equals("1");int audioVolume = Convert.ToInt32(ConfigurationManager.AppSettings["SetAudioVolume"]);//静音的分钟数int muteMinute= Convert.ToInt32(ConfigurationManager.AppSettings["MuteMinute"]);const int WM_SYSCOMMAND = 0x112;const int SC_MINIMIZE = 0xF020;Thread audioThread = null;int secondCount = 0;public FrmMain(){InitializeComponent();this.WindowState = FormWindowState.Minimized;this.ShowInTaskbar = false;this.Load += FrmMain_Load;audioThread = new Thread(StartAudio);audioThread.IsBackground=true;AudioHelper.Init("发生药槽错误,请盘点变红的药槽.mp3");audioThread.Start();System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();timer.Interval = 1000;timer.Tick += Timer_Tick;timer.Start();}private const int CP_NOCLOSE_BUTTON = 0x200;protected override CreateParams CreateParams{get{CreateParams myCp = base.CreateParams;myCp.ClassStyle = myCp.ClassStyle | CP_NOCLOSE_BUTTON;return myCp;}}private void Timer_Tick(object sender, EventArgs e){int currentVlume=AudioHelper.GetCurrentSpeakerVolume();if (currentVlume < 20){AudioHelper.SetCurrentSpeakerVolume(audioVolume);}if (AudioHelper.CheckIsNoNoise()){secondCount++;if (secondCount > (60 * muteMinute)){secondCount = 0;AudioHelper.CloseNoNoise();}}else{secondCount = 0;}}protected override void WndProc(ref Message m){if (m.Msg == WM_SYSCOMMAND){if (m.WParam.ToInt32() == SC_MINIMIZE){this.ShowInTaskbar = false;this.WindowState= FormWindowState.Minimized;}}base.WndProc(ref m);}private void FrmMain_Load(object sender, EventArgs e){//实例化一个NotifyIcon对象NotifyIcon notifyIcon = new NotifyIcon();//设置图标notifyIcon.Icon = new Icon("ico_audio.ico");//设置提示文本notifyIcon.Text = "药槽错误语音提醒";//设置双击托盘图标后显示窗体notifyIcon.DoubleClick += NotifyIcon_DoubleClick;//显示托盘notifyIcon.Visible = true;}private void NotifyIcon_DoubleClick(object sender, EventArgs e){this.WindowState = FormWindowState.Normal;this.ShowInTaskbar = true;this.Activate();}private void btnStart_Click(object sender, EventArgs e){AudioHelper.Init("发生药槽错误,请盘点变红的药槽.mp3");if (audioThread.ThreadState == (ThreadState.Background|ThreadState.Suspended)){audioThread.Resume();}else if(audioThread.ThreadState==(ThreadState.Background|ThreadState.Unstarted)){audioThread.Start();}MessageBox.Show("开始成功");}private void StartAudio(){while (true){Thread.Sleep(500);if (!isTest){if (GetDrawerError()){if (AudioHelper.GetIsPalying() && AudioHelper.CheckCanStart()){AudioHelper.PlayAudio();}}}else{if (AudioHelper.GetIsPalying() && AudioHelper.CheckCanStart()){AudioHelper.PlayAudio();}}}}private bool GetDrawerError(){string sqlText = "select IdDrawer,IdProduct from 表名 where 条件";try{IList<DrawerModel> list = SqlHelper.ExecuteDataTable<DrawerModel>(sqlText, Array.Empty<SqlParameter>());if (list.Count > 0){return true;}}catch (Exception ex){Console.WriteLine("查询失败,错误信息:" + ex.Message);}return false;}/// <summary>/// 停止按钮的逻辑,根据需要进行加入/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void btnStop_Click(object sender, EventArgs e){audioThread.Suspend();AudioHelper.StopPlay();}}
}
要上班了,代码就不解释了