我正在开发一个类似skype的应用程序,我有一个外部DLL来完成大部分工作并触发我的类ip2ip中处理的事件,其中一个事件是当有一个来电时,就像名字所暗示的那样触发了incoming_call。我试图管理未接来电。 现在这是这个类中代码的相关部分:
private void ics_IncomingCall(object sender, string authenticationData, int socketHandle, string callbackid, string callbackipaddress, int callbackvideoport, int callbackaudiotcpport, int callbackaudiudpport)
{
if (Calling)
{
ics.RejectCall("The contact have another call", (IntPtr)socketHandle);
Message = "An incoming call from [" + callbackipaddress + "] has rejected.";
}
else
{
AcceptIncomingCall = null;
UserCaller = FindUserName(callbackipaddress);
IncomingCall = true;
//waiting for the call to be accepted from outside of this class
while (AcceptIncomingCall.HasValue == false) Thread.Sleep(100);
if(AcceptIncomingCall.Value == true)
{
//call back to have a 1 on one video conference
icc.Parent.BeginInvoke(new MethodInvoker(delegate
{
//accept the incoming call
ics.AcceptCall("n/a", socketHandle);
icc.Call(callbackipaddress, callbackvideoport, 0, 0,
"n/a", callbackid,
ics.GetLocalIp()[0].ToString(), 0, 0, 0, "");
Calling = true;
}));
}
else
{
ics.RejectCall("Call not accepted", (IntPtr)socketHandle);
Log = "Incoming call not accepted";
Calling = false;
}
AcceptIncomingCall = null;
IncomingCall = false;
}
}
IncomingCall是一个生成PropertyChangedEvent的属性,它在我的主类中被捕获,我有这个代码:
private void ip2ip_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e != null && string.IsNullOrEmpty(e.PropertyName) == false)
{
..............
if (e.PropertyName.Equals("IncomingCall") && ip2ip.IncomingCall == true)
{
Invoke(new MethodInvoker(delegate
{
pnlCalling.Visible = true;
aTimer.Start();
}));
}
................
}
}
public Form1()
{
.......
aTimer = new System.Windows.Forms.Timer();
aTimer.Interval = 10000;
aTimer.Tick += aTimer_Tick;
}
void aTimer_Tick(object sender, EventArgs e)
{
aTimer.Stop();
btnNo.PerformClick();
}
private void btnNo_Click(object sender, EventArgs e)
{
aTimer.Stop();
ip2ip.AcceptIncomingCall = false;
}
private void btnOk_Click(object sender, EventArgs e)
{
aTimer.Stop();
ip2ip.AcceptIncomingCall = true;
}
我需要定时器来管理未接来电,当有来电时会出现一个面板,并带有接听/拒绝来电的按钮。如果用户等待太多,则认为呼叫被拒绝(错过)。 通过这种方式,它不起作用,可能是我对计时器做错了,因为没有任何计时器一切正常。我也尝试了类System.Timers的计时器,结果相同。有什么想法吗?
修改
这是我的期望,有一个传入的调用所以事件ics_IncomingCall
被触发,IncomingCall=true
导致执行进入主类(我们仍然在同一个线程中,我看到它调试在VS)中一步一步地在GUI线程中调用面板可见并启动计时器,现在我们有一个线程,其中while循环阻止执行,直到在另一个线程用户做某事(接受/拒绝)。
当用户接受调用时,存在问题,while循环后的代码总是被执行,调用者完全没有问题并且接收流,但是在接收者(我在wireshark中验证的人接收流)DLL(谁负责显示传入的视频)由于我不知道但由计时器引起的某些原因而无法完成其工作。
答案 0 :(得分:0)
遗憾的是,您的问题不包括可靠地重现问题的a good, minimal, complete code example。拥有这样的代码示例会使某人提供有用的答案更加实用。
如上所述,正如评论者varocarbas所解释的那样,您的基本问题似乎是您已经阻止了UI线程(使用while
循环),同时希望获得UI线程处理其他活动(例如计时器的tick事件)。实际上,您也在阻止按钮单击产生效果。按钮Click
事件处理程序也无法执行,而UI线程被阻止。
解决此问题的一种方法是使用TaskCompletionSource<T>
为ics_IncomingCall()
提供一个可等待的对象,按钮和计时器可以用来发出信号。例如:
// Change from "bool?" to this:
private TaskCompletionSource<bool> AcceptIncomingCall;
public void HandleCall(bool accept)
{
AcceptIncomingCall.SetResult(accept);
}
private async Task ics_IncomingCall(object sender, string authenticationData, int socketHandle, string callbackid, string callbackipaddress, int callbackvideoport, int callbackaudiotcpport, int callbackaudiudpport)
{
if (Calling)
{
ics.RejectCall("The contact have another call", (IntPtr)socketHandle);
Message = "An incoming call from [" + callbackipaddress + "] has rejected.";
}
else
{
AcceptIncomingCall = new TaskCompletionSource<bool>();
UserCaller = FindUserName(callbackipaddress);
IncomingCall = true;
//waiting for the call to be accepted from outside of this class
if (await AcceptIncomingCall.Task)
{
//call back to have a 1 on one video conference
icc.Parent.BeginInvoke(new MethodInvoker(delegate
{
//accept the incoming call
ics.AcceptCall("n/a", socketHandle);
icc.Call(callbackipaddress, callbackvideoport, 0, 0,
"n/a", callbackid,
ics.GetLocalIp()[0].ToString(), 0, 0, 0, "");
Calling = true;
}));
}
else
{
ics.RejectCall("Call not accepted", (IntPtr)socketHandle);
Log = "Incoming call not accepted";
Calling = false;
}
AcceptIncomingCall.Dispose();
IncomingCall = false;
}
}
和
void aTimer_Tick(object sender, EventArgs e)
{
aTimer.Stop();
btnNo.PerformClick();
}
private void btnNo_Click(object sender, EventArgs e)
{
aTimer.Stop();
genericServerClient.HandleCall(false);
}
private void btnOk_Click(object sender, EventArgs e)
{
aTimer.Stop();
genericServerClient.HandleCall(false);
}
这导致ics_IncomingCall()
方法在到达await
语句时返回,允许其线程继续执行。按钮Click
事件处理程序将回调封装您的字段的公共方法(公共字段非常危险,几乎在所有情况下都应该避免),设置TaskCompletionSource
对象的结果值等待着。
一旦设置了结果值,这将导致框架继续执行它停止的ics_IncomingCall()
方法,但现在使用按钮Click
事件处理程序返回的值。即true
如果用户点击了btnOk
和false
,则会点击btnNo
或定时器间隔时间。
请注意,这会更改ics_IncomingCall()
方法的签名,这将强制更改调用方。处理这种情况的最佳方法是更改调用者,使用async
并使用await ics_IncomingCall(...)
。这当然会强制改变其调用者及其调用者的调用者,等等。但是你需要释放UI线程,这是最好的方法。希望你没有很多呼叫者可以改变,但即使你这样做,这也是要走的路。
如果以上似乎无法解决您的问题,请提供一个好的MCVE。请注意,良好的MCVE是完整和最小。您将需要从示例中删除任何不是严格要求重现问题的代码。同时,确保有人可以将代码复制并粘贴到一个空项目中,并使其最多只运行非常小的,最好不运行。