MessageBox.Show偶尔不会在捕获异常时显示

时间:2019-10-14 16:22:03

标签: c# visual-studio-extensions

我正在用C#编写Visual Studio扩展,但是在管理异常和显示错误消息时出现奇怪的行为。基本上,我只想向异常消息添加一些细节,以帮助我在出现问题时进行调查。

所有这些都从上下文菜单项上的命令开始,我怀疑它可能与异步/等待机制背后的线程管理有关。但是我不确定我猜对了,也找不到任何解决方案。帮助!

它从我的菜单项回调开始:

internal sealed class My_RunAnalysis
{
    //...
    public static async Task InitializeAsync(AsyncPackage package)
    {
        // Switch to the main thread - the call to AddCommand in PS_RunAnalysis's constructor requires
        // the UI thread.
        await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken);

        OleMenuCommandService commandService = await package.GetServiceAsync((typeof(IMenuCommandService))) as OleMenuCommandService;
        Instance = new My_RunAnalysis(package, commandService);
    }

    //...
    private async void ExecuteAsync(object sender, EventArgs e)
    {
        try
        {
            await My_ViewModel.RunAnalysisAsync();
        }
        catch (Exception exc)
        {
            await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken);

            MessageBox.Show(exc.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
        }
    }
}

//...

class My_ViewModel
{
    async public static Task RunAnalysisAsync()
    {
        await My_Model.GetResultsListAsync();
    }
}

//...

class My_Model
    async public static Task GetResultsListAsync()
    {
        ResultsList = new My_ResultsList();

        var rawResultsList = await QueryServerAsync<RawResultsListResponse>("GET", My_Request.GetResults());

        //...
    }

    async public static Task<JsonResponse> QueryServerAsync<JsonResponse>(string method, 
        string request)
    {
        try
        {
            HttpResponseMessage response;

            switch (method)
            {
                case "GET":
                    response = await _httpClient.GetAsync(request);
                    break;
                case "POST":
                default:
                    StringContent httpContent = new StringContent("", Encoding.UTF8, "application/json");
                    response = await _httpClient.PostAsync(request, httpContent);
                    break;
            }

            if (!response.IsSuccessStatusCode) //<<<<<<CASE #1
            {
                throw new My_Exception(
                response.ReasonPhrase,
                "Exception while querying server for " + request);
            }

            string serializedJson = await response.Content.ReadAsStringAsync();
            // CASE #2>>>>>
            var jsonResponse = serializer.Deserialize<JsonResponse>(serializedJson); 

            return jsonResponse;
        }
        catch (Exception e)
        {
            throw new My_Exception(
                e.Message,
                "Exception while querying server for " + request);
        }
    }

奇怪的是:

  • 在情况#1中发生错误时,我创建了一个自定义异常(我的服务器已响应,但发生内部错误,并且我有一个干净的错误代码),在My_ViewModel :: RunAnalysisAsync( )将立即正确显示。

  • 如果#2发生本机异常(我的服务器响应格式错误的json,并且我从serializer.Deserialize收到异常),则My_ViewModel :: RunAnalysisAsync()捕获中的MessageBox将不会显示, IDE将在重新启动之前挂起15秒钟左右(并且仍然不显示MessageBox)。

有什么想法吗?

谢谢!

  

编辑:

     

看到我的自定义命令的模板也使用SwitchToMainThreadAsync初始化,我尝试使用Execute方法执行相同的操作。我更新了上面的代码,但仍然无法使用:由序列化程序引发的异常。反序列化仍将UI冻结10至15秒,并且MessageBox将不会显示!

     

还请注意,调试器可以立即进入“等待ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken);”。然后继续到MessageBox。我倾向于以为这意味着立即切换到主线程,但是仍然有问题...

     

有什么想法吗?我真的需要以可靠的方式捕获异常...

2 个答案:

答案 0 :(得分:1)

我找不到对MessageBox处理某个案例而不对另一个案例的任何解释。我最终使用FileStream.WriteAsync寻求一些日志解决方案。因此,所有内容都保持异步,并且我不再需要使用MessageBox。

答案 1 :(得分:0)

使用await JoinableTaskFactory.SwitchToMainThreadAsync();切换到主线程JoinableTaskFactoryAsyncPackage的成员。 如果仍然无法正常工作,请尝试

public static void ShowMessageBox(string title, string text)
        {
            Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread();

            IVsUIShell uiShell = Microsoft.VisualStudio.Shell.ServiceProvider.GlobalProvider.GetService(typeof(SVsUIShell)) as IVsUIShell;
            Guid clsid = Guid.Empty;
            int result;
            Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(uiShell.ShowMessageBox(
                       0,
                       ref clsid,
                       title,
                       text,
                       string.Empty,
                       0,
                       OLEMSGBUTTON.OLEMSGBUTTON_OK,
                       OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST,
                       OLEMSGICON.OLEMSGICON_INFO,
                       0,        // false
                       out result));
        }