Windows Mobile呼叫消音器(译文)
By S.F.
本文链接 https://www.kyfws.com/news/windows-mobile-call-silencer/
版权声明 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
- 9 分钟阅读 - 4082 个词 阅读量 0Windows Mobile呼叫消音器(译文)
原文地址:https://www.codeproject.com/Articles/31498/Windows-Mobile-Call-Silencer
原文作者:Joel Ivory Johnson
译文由本站翻译
前言
用于在计算机上显示CallerID信息并在电话上静音计算机声音的程序.
- 下载源代码519.15 KB
介绍
通过一些C#代码,我为Windows Mobile电话构建了一个解决方案,该解决方案将在电话响起时在桌面上显示被叫ID信息,并在电话处于活动状态或以语音方式显示呼叫者的姓名时使计算机静音.电话通话结束后,计算机将自动取消静音.
该程序背后的故事
我的计算机上有很多程序会由于各种原因发出声音.有传入的即时消息或电子邮件,预定的提醒,媒体播放器以及偶尔发出意外声音的Web广告的通知.不管声音的目的是什么,我都不想在使用手机时播放声音.如果我忘记使计算机静音,则会出现意外消息,并且电话线另一端的人会大声分散注意力.当我使计算机静音时,我可能会忘记取消静音并错过有关新消息和提醒的通知.为了解决此问题,我决定让手机在使用时使计算机静音,并在通话结束后取消静音. 我认为有必要演示使用电话状态信息的目的,而不是使计算机静音,从而增加了两个功能.我为该系统的桌面客户端添加了功能,以在通知气球中显示呼叫者信息.我还添加了一个程序,该程序利用Vista的语音合成功能来口头宣布呼叫者的姓名.
要求
在电话上运行的部分代码需要Windows Mobile Professional设备(带有触摸屏的设备).在桌面上运行的程序需要Windows Vista.此代码在Windows XP上不起作用.
配套文章
为了处理计算机音频系统的静音和取消静音,我使用了Ray M.在[Vista Core Audio API Master Volume]上的文章中的代码控制](http://www.codeproject.com/KB/vista/CoreAudio.aspx).如果您打算使用此代码并拥有64位系统,请阅读其文章的评论部分;它列出了必须更改的代码,否则代码将无法工作.
查询电话状态
对于此程序,我们必须能够查询手机的状态.这包括检测传入呼叫,知道谁在打传入呼叫,检测是否提供数据连接等等.所有这些信息都通过状态和通知代理进行跟踪.在紧凑的框架内,状态和通知代理可以通过SystemState
类进行访问.如果浏览一下SystemState类的成员,您会发现它可用于获取有关存在或不存在数据连接的信息,有关当前和以前的电话的信息以及许多其他信息.
SystemState类易于使用.创建此类的实例时,开发人员必须指定需要为其更改元素的通知.然后,每次值更改时,都会引发一个事件.本文包括一个名为" PhoneStatus"的示例程序.它使用SystemState类来检测电话是否处于活动状态,是否存在数据连接并显示呼叫者的姓名.
SystemState _statePhoneIncoming;
SystemState _StatePhoneActiveCount;
SystemState _phoneCaller;
SystemState _localData;
public MainForm()
{
InitializeComponent();
_statePhoneIncoming = new SystemState(SystemProperty.PhoneIncomingCall, true);
_statePhoneIncoming.Changed += new ChangeEventHandler(_statePhoneIncoming_Changed);
_StatePhoneActiveCount = new SystemState(SystemProperty.PhoneActiveCallCount, true);
_StatePhoneActiveCount.Changed += new ChangeEventHandler
(_StatePhoneActiveCount_Changed);
_localData = new SystemState(SystemProperty.ConnectionsCount, true);
_localData.Changed += new ChangeEventHandler(_localData_Changed);
_phoneCaller = new SystemState(SystemProperty.PhoneIncomingCallerNumber, true);
_phoneCaller.Changed += new ChangeEventHandler(_phoneCaller_Changed);
}
void _phoneCaller_Changed(object sender, ChangeEventArgs args)
{
lblLastCallerName.Text = SystemState.PhoneIncomingCallerName;
lblLastCallerNumber.Text = SystemState.PhoneIncomingCallerNumber;
}
void _localData_Changed(object sender, ChangeEventArgs args)
{
chkLocalData.Checked = ((int)args.NewValue) > 0;
}
void _StatePhoneActiveCount_Changed(object sender, ChangeEventArgs args)
{
chkCallActive.Checked = (SystemState.PhoneActiveCallCount > 0) ||
SystemState.PhoneIncomingCall;
}
void _statePhoneIncoming_Changed(object sender, ChangeEventArgs args)
{
chkCallActive.Checked = SystemState.PhoneIncomingCall ||
SystemState.PhoneActiveCallCount > 0;
}
发送静音和取消静音消息
除了自动使计算机静音之外,用户还可以使用他/她的电话使计算机静音或取消静音.发送静音和取消静音消息的界面仅需图片即可.
将状态发送到桌面
我使用的Windows Mobile Professional电话有两个适配器,可用于与计算机通信. WiFi适配器和Bluetooth适配器.该应用程序通过UDP执行其通信.因此,它既可以在带有WiFi适配器的电话上使用,也可以在蓝牙个人局域网中的设备上使用.但是我所拥有的计算机没有蓝牙功能,因此我仅使用手机的WiFi适配器测试了该程序.最初,我只需要程序发送四个消息即可.为了表示电话的活动,有"活动"和"无效"消息.该程序还允许人们发送"静音"和"取消静音"消息,以更改计算机音频硬件的状态.后来我决定添加"CallerID
“消息.
选择收件人机器
有三种方法可用于通过UDP向计算机组发送消息.消息可以广播到网络节点上的所有计算机,也可以发送到多播组(在这种情况下,对某些消息感兴趣的计算机可以注册以接收消息),也可以将消息分别发送到每台计算机.不推荐使用UDP广播;它仍然可以在IPv4上运行,但它不是IPv6标准的一部分.多播比较理想,但是我发现我连接的某些路由器不允许我的机器发送多播流量.我没有说服路由器的所有者重新配置路由器以满足我的个人需求,而是决定直接将信息发送到应该接收信息的机器.以下UI允许用户输入设备的名称或IP地址,这些设备应使用电话状态信息进行更新,并指定所有设备将在其上监听的端口.
讯息安全
在实施该程序时,我考虑了很长时间的安全性.最初,此程序要做的就是在手机启动时使计算机静音并取消静音.我对我使用的网络的安全性以及这些网络上用户的良性意图充满信心.但是职业责任感使我在开发该程序时需要考虑其他方面.当将主叫方ID信息添加到该程序传输的消息中时,安全性变得更加重要.通过加密消息,我能够解决消息安全问题.我正在使用Rijndael加密算法(也称为AES)的紧凑框架实现.启动后,程序将在其目录中查找已保存的加密密钥.如果找不到,它将创建一个新的加密密钥.必须将密钥从设备上复制下来,并导入手机将共享状态的所有计算机中.该系统的桌面部分在文件菜单中具有导入加密密钥的选项.
void SendMessage(byte[] message)
{
for (int i = 0; i < _clientList.Count; ++i)
{
//attempt to send each message to the clients individually
try
{
UdpClient client = _clientList[i];
byte[] encryptedMessage = Encrypt(message);
client.Send(encryptedMessage, encryptedMessage.Length);
}
catch (Exception )
{
//If a message could not be sent simply do nothing.
}
}
}
byte[] Encrypt(byte[] message)
{
MemoryStream ms = new MemoryStream();
CryptoStream encStream = new CryptoStream(ms, _transform, CryptoStreamMode.Write);
encStream.Write(message, 0, message.Length);
encStream.FlushFinalBlock();
encStream.Close();
return ms.ToArray();
}
接收消息
该解决方案的桌面部分将等待电话活动消息的处理.在” UdpClient"类上接收消息将涉及对其" Read"方法的调用.此类的Read方法是一种阻塞方法;调用它的线程将被暂停,直到有一条消息要读取为止.在单线程应用程序中,尝试从UI线程上的" UdpClient"中读取内容将导致程序似乎处于冻结状态,直到消息到达为止.为了防止这种情况的发生,必须在不同的线程上执行对" UdpClient"." Read"的调用. 监听线程的管理和接收消息的功能打包在其自己的类中,该类实现了IMessageListener接口.该界面列出了两种方法(“激活"和"取消激活”)来开始和停止侦听消息,一个属性分配一个" ICryptoTransform"对象以解密消息,以及事件处理程序为传入消息和侦听更改提供通知实现类的状态.接口定义如下:
public interface IMessageListener: IDisposable
{
ICryptoTransform CryptoProvider { get; set; }
void Activate();
void Deactivate();
event EventHandler<messagereceivedeventargs> MessageReceived ;
event EventHandler<listenerstatechangedeventargs> ListenerStateChanged ;
}</listenerstatechangedeventargs></messagereceivedeventargs>
Activate和Deactivate的实现是唯一定义在IMessageListener接口的实现中可能有所不同的方法. MessageListener abstract类包含侦听器实现中不太可能变化的部分.在UDP端口上监听的特定细节在UdpMessageListener
类中定义.如果要实现另一个侦听器,则需要:
- MessageListener
- 启用
- 停用
- 收到消息
- ListenerStateChanged
正确实现后,此类将通过MessageReceived事件处理程序的MessageReceivedEventArgs参数传递未加密的消息.
处理消息
消息的处理由" switch"语句处理.该语句通常将更改计算机声音设备的静音状态,并更新状态文本以指示电话的状态.如果该消息是" CallerID"消息,则设备将通过通知气球显示" CallerID"信息.
switch (e.MessageType)
{
case MessageType.Active:
if (!_senderActive)
previouslyMuted = device.AudioEndpointVolume.Mute;
_senderActive = true;
device.AudioEndpointVolume.Mute = true;
UpdateStatus("Phone Active");
break;
case MessageType.Inactive:
_senderActive = false;
device.AudioEndpointVolume.Mute = previouslyMuted;
UpdateStatus("Phone Inactive");
break;
case MessageType.Mute:
device.AudioEndpointVolume.Mute = previouslyMuted = true;
UpdateStatus("Mute request received");
break;
case MessageType.Unmute:
previouslyMuted = false;
device.AudioEndpointVolume.Mute = false;
UpdateStatus("Unmute request received");
break;
case MessageType.CallerID:
callerIdNotification.Visible = true;
callerIdNotification.ShowBalloonTip(9000,"Incoming Call",String.Format
("Call received from {0} ({1})", e.Name, e.Number),ToolTipIcon.Info);
break;
default:
break;
}
呼叫播音员
“呼叫播音员"程序将口头宣布呼叫者并保留已接听呼叫的列表.该程序背后的实际代码非常简单.该项目使用相同的MessageListener
类.为了表达调用者的名字,该程序使用了.NET 3.0框架中的SpeechSynthsizer
类.此类依赖Windows Vista附带的本机功能,但不是Windows XP的一部分.要测试此程序,必须使用运行Vista的计算机.该类有两种方法来渲染语言化的文本名称” Speak"和" SpeakAsync".顾名思义,
Speak是一个阻塞的调用,而
Speak`Async将允许您的代码在说话时继续执行.
编组对UI线程的调用
在整个代码中,您偶尔会看到通过Control.Invoke调用自己的方法.之所以存在此模式,是因为除主线程外没有其他线程可以修改UI元素.在其他线程上发生的调用将无法更改在"文本框"中显示的文本,“复选框"的选中状态或任何其他可视元素.控件具有Control.InvokeRequired属性,该属性在被辅助线程调用时返回true,在UI线程上调用时返回false.我使用Control.InvokeRequired来确定是否需要将调用编组到主线程.如果需要编组,则使用” Control.Invoke"使调用在适当的线程上进行.下面显示了使用此模式来更新状态栏上文本的UpdateStatus
方法:
void UpdateStatus(String statusMsg)
{
if (this.InvokeRequired)
{
this.Invoke(_updateText, new object[] { statusMsg });
}
else
{
txtFeedback.Text = statusMsg;
}
}
未来增强
使用此代码的初始版本,我的目标很简单;我只是想让代码执行其功能.现在,我已经实现了这个目标,可以添加一些可用性增强功能,例如将电话停靠到机器上时自动传输加密密钥.几周后,我应该会收到蓝牙适配器和Windows Mobile Standard电话,并且可能会修改该程序,使其能够在无触摸屏的电话以及UDP之外通过蓝牙的电话上使用.
历史
许可
本文以及所有相关的源代码和文件均已获得The Code Project Open License (CPOL)的许可。
C#2.0 WinMobile5 C# WinMobile .NETCF Dev Intermediate WinMobile6 新闻 翻译