在Win32和UWP之间进行通信时,SendMessageAsync会随机引发异常

时间:2018-04-09 14:50:39

标签: c# winforms uwp

在我的UWP应用程序中,我需要不断地从WinForms(Win32)组件向UWP应用程序发送数据,反之亦然。但是,我的WinForms组件中有一个奇怪的错误。有时,在启动WinForm时,我在致电System.InvalidOperationException时会收到await connection.SendMessageAsync(message)说:A method was called at an unexpected time其他时候,它会完美运行。

我的代码:

private async void SendToUWPVoidAsync(object content)
{
    ValueSet message = new ValueSet();
    if (content != "request") message.Add("content", content);
    else message.Add(content as string, "");

    #region SendToUWP

    // if connection isn't inited
    if (connection == null)
    {
        // init
        connection = new AppServiceConnection();
        connection.PackageFamilyName = Package.Current.Id.FamilyName;
        connection.AppServiceName = "NotifyIconsUWP";
        connection.ServiceClosed += Connection_ServiceClosed;

        // attempt connection 
        AppServiceConnectionStatus connectionStatus = await connection.OpenAsync();
    }

    AppServiceResponse serviceResponse = await connection.SendMessageAsync(message);

    // get response
    if (serviceResponse.Message.ContainsKey("content"))
    {
        object newMessage = null;
        serviceResponse.Message.TryGetValue("content", out newMessage);

        // if message is an int[]
        if (newMessage is int[])
        {
            // init field vars
            int indexInArray = 0;
            foreach (int trueorfalse in (int[])newMessage)
            {
                // set bool state based on index
                switch (indexInArray)
                {
                    case 0:
                        notifyIcon1.Visible = Convert.ToBoolean(trueorfalse);
                        break;
                    case 1:
                        notifyIcon2.Visible = Convert.ToBoolean(trueorfalse);
                        break;
                    case 2:
                        notifyIcon3.Visible = Convert.ToBoolean(trueorfalse);
                        break;
                    default:
                        break;
                }
                indexInArray++;
            }
        }
    }
    #endregion
}

这个方法的调用如下:

private void TCheckLockedKeys_Tick(object sender, EventArgs e)
{
    ...

    if (statusesChanged)
    {
        // update all bools
        bool1 = ...;
        bool2 = ...;
        bool3 = ...;

        // build int[] from bool values
        int[] statuses = new int[] { Convert.ToInt32(bool1), Convert.ToInt32(bool2), Convert.ToInt32(bool3) };

        // update UWP sibling
        SendToUWPVoidAsync(statuses);
    }

    // ask for new settings
    SendToUWPVoidAsync("request");
}

TCheckLockedKeys_Tick.Interval设置为250毫秒。

有没有办法在没有WinForm组件退出但仍然建立重要通信路径的情况下阻止或正确处理此异常?

有什么想法吗?

由于

2 个答案:

答案 0 :(得分:0)

好的,我找到了解决方案。人们可能实际上称之为解决方法。

在我的WinForm中,我更改了代码如下:

AppServiceResponse serviceResponse = await connection.SendMessageAsync(message);

为:

AppServiceResponse serviceResponse = null;
try
{
    // send message
    serviceResponse = await connection.SendMessageAsync(message);
}
catch (Exception)
{
     // exit 
     capsLockStatusNI.Visible = false;
     numLockStatusNI.Visible = false;
     scrollLockStatusNI.Visible = false;

     Application.Exit();
 }

我还更改了App.xaml.cs文件中的代码:

private async void OnTaskCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
{
    if (this.appServiceDeferral != null)
    {
        // Complete the service deferral.
        this.appServiceDeferral.Complete();
    }
}

到:

private async void OnTaskCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
{
    if (reason == BackgroundTaskCancellationReason.SystemPolicy)
    {
        // WinForm called Application.Exit()
        await FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync();
    }
    if (this.appServiceDeferral != null)
    {
        // Complete the service deferral.
        this.appServiceDeferral.Complete();
    }
}

我知道我所做的一切,从技术上来说,重新启动表格直到它成功,这不完全是解决它的正确方法。但是,它有效。

答案 1 :(得分:0)

基于

的一些建议

参考Async/Await - Best Practices in Asynchronous Programming

  

除了事件处理程序之外,避免使用async void。比async Task种方法更喜欢async void种方法。

异步void会导致忘记,这会导致遇到问题,因为异常不会在正确的上下文中抛出。

  

Async void方法具有不同的错误处理语义。当异步任务或异步任务方法抛出异常时,将捕获该异常并将其放在Task对象上。使用async void方法时,没有Task对象,因此异步void方法抛出的任何异常都将直接在async void方法启动时处于活动状态的SynchronizationContext上引发。

假定在事件处理程序中调用该方法。然后重构该方法以使用async Task

private async Task SendToUWPVoidAsync(object content) {

    //...

}

并将事件处理程序更新为异步

private async void TCheckLockedKeys_Tick(object sender, EventArgs e) {
    try {
        //...

        if (statusesChanged) {
            // update all bools
            bool1 = ...;
            bool2 = ...;
            bool3 = ...;

            // build int[] from bool values
            int[] statuses = new int[] { Convert.ToInt32(bool1), Convert.ToInt32(bool2), Convert.ToInt32(bool3) };

            // update UWP sibling
            await SendToUWPVoidAsync(statuses);
        }

        // ask for new settings
        await SendToUWPVoidAsync("request");

    }catch {
        //...handle error appropriately
    }
}

还应该允许捕获任何异常,如上例所示。