WinRT - MessageDialog.ShowAsync将在我的自定义类中抛出UnauthorizedAccessException

时间:2012-12-11 03:12:25

标签: c# .net windows-runtime unauthorizedaccessexcepti messagedialog

我想编写自己的控件,当调用ctor时,会显示一个MessageBox。

public class Class1
{
    public Class1()
    {
        ShowDialog();
    }
    void ShowDialog()
    {
        SynchronizationContext context = SynchronizationContext.Current;
        if (context != null)
        {
            context.Post((f) =>
            {
                MessageDialog dialog = new MessageDialog("Hello!");
                dialog.ShowAsync();
            }, null);
        }
    }
}

如果我的类被某人使用,并编写如下代码,则dialog.ShowAsync();

中始终会抛出UnauthorizedAccessException。
void MainPage_Loaded(object sender, RoutedEventArgs e)
        {

            ClassLibrary1.Class1 c1 = new ClassLibrary1.Class1();
            MessageDialog dialog1 = new MessageDialog("");
            dialog1.ShowAsync();
        }

有没有办法毫无例外地显示消息对话框?


我找到了一种方式,享受它!

Task ShowDialog()
        {
            CoreDispatcher dispatcher = Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher;
            Func<object, Task<bool>> action = null;
            action = async (o) =>
            {
                try
                {
                    if (dispatcher.HasThreadAccess)
                        await new MessageDialog("Hello!").ShowAsync();
                    else
                    {
                        dispatcher.RunAsync(CoreDispatcherPriority.Normal,
                        () => action(o));
                    }
                    return true;
                }
                catch (UnauthorizedAccessException)
                {
                    if (action != null)
                    {
                        Task.Delay(500).ContinueWith(async t => await action(o));
                    }
                }
                return false;
            };
            return action(null);
        }

4 个答案:

答案 0 :(得分:3)

由于MessageDialogue需要在UI线程上运行,您可以尝试将其切换到:

var dispatcher = Windows.UI.Core.CoreWindow.GetForCurrentThread().Dispatcher;
dispatcher.RunAsync(DispatcherPriority.Normal, <lambda for your code which should run on the UI thread>);

答案 1 :(得分:1)

清洁解决方案可能如下所示。有趣的部分隐藏在showDialogAsync()中。为方便起见,您可以使用Close()方法以编程方式再次关闭当前对话框。 IUIDispatcher是另一个可以轻松重建的帮助程序接口:

    private readonly IUIDispatcher _dispatcher;
    readonly Object _queueMonitor = new object();
    readonly Object _showMonitor = new object();
    private IAsyncOperation<IUICommand> _currentDialogOperation;
    readonly Queue<MessageDialog> _dialogQueue = new Queue<MessageDialog>();


    public async Task ShowAsync(string content)
    {
        var md = new MessageDialog(content);
        await showDialogAsync(md);
    }

    public async Task ShowAsync(string content, string caption)
    {
        var md = new MessageDialog(content, caption);
        await showDialogAsync(md);
    }

    public async Task<MessageDialogResult> ShowAsync(string content, MessageDialogType dialogType)
    {
        var messageDialogResult = await ShowAsync(content, null, dialogType);
        return messageDialogResult;
    }

    public async Task<MessageDialogResult> ShowAsync(string content, string caption, MessageDialogType dialogType)
    {
        var result = MessageDialogResult.Ok;


            var md = string.IsNullOrEmpty(caption) ?  new MessageDialog(content) : new MessageDialog(content, caption);

        switch (dialogType)
        {
            case MessageDialogType.Ok:
                md.Commands.Add(new UICommand(ResWrapper.Strings["MessageDialogButtonOk"], command => result = MessageDialogResult.Ok));
                md.CancelCommandIndex = 0;
                md.DefaultCommandIndex = 0;
                break;
            case MessageDialogType.OkCancel:
                md.Commands.Add(new UICommand(ResWrapper.Strings["MessageDialogButtonOk"], command => result = MessageDialogResult.Ok));
                md.Commands.Add(new UICommand(ResWrapper.Strings["MessageDialogButtonCancel"], command => result = MessageDialogResult.Cancel));
                md.DefaultCommandIndex = 0;
                md.CancelCommandIndex = 1;
                break;
            case MessageDialogType.YesNo:
                md.Commands.Add(new UICommand(ResWrapper.Strings["MessageDialogButtonYes"], command => result = MessageDialogResult.Yes));
                md.Commands.Add(new UICommand(ResWrapper.Strings["MessageDialogButtonNo"], command => result = MessageDialogResult.No));
                  md.DefaultCommandIndex = 0;
                md.CancelCommandIndex = 1;
                break;
            case MessageDialogType.YesNoCancel:
                md.Commands.Add(new UICommand(ResWrapper.Strings["MessageDialogButtonYes"], command => result = MessageDialogResult.Yes));
                md.Commands.Add(new UICommand(ResWrapper.Strings["MessageDialogButtonNo"], command => result = MessageDialogResult.No));
                md.Commands.Add(new UICommand(ResWrapper.Strings["MessageDialogButtonCancel"], command => result = MessageDialogResult.Cancel));
                md.DefaultCommandIndex = 0;
                md.CancelCommandIndex = 1;
                break;
            default:
                throw new ArgumentOutOfRangeException("dialogType");
        }

        await showDialogAsync(md);

        return result;
    }


    /// <summary>
    /// Shows the dialogs, queued and one after the other.
    /// We need this as a workaround for the the UnauthorizedAcsess exception. 
    /// </summary>
    /// <param name="messageDialog">The message dialog.</param>
    /// <returns></returns>
    async Task showDialogAsync(MessageDialog messageDialog)
    {
        //Calls this function in a separated task to avoid ui thread deadlocks.
        await Task.Run(async () => 
        { 
            lock (_queueMonitor)
            {
                _dialogQueue.Enqueue(messageDialog);
            }
            try
            {
                while (true)
                {
                    MessageDialog nextMessageDialog;
                    lock (_queueMonitor)
                    {
                        if (_dialogQueue.Count > 1)
                        {
                            Debug.WriteLine("MessageDialogService.cs | showDialogAsync | Next dialog is waiting for MessageDialog to be accessable!!");
                            Monitor.Wait(_queueMonitor); //unlock and wait - regains lock after waiting
                        }

                        nextMessageDialog = _dialogQueue.Peek();
                    }

                    var showing = false;
                    _dispatcher.Execute(async () =>  
                    {
                        try
                        {
                            lock (_showMonitor)
                            {
                                showing = true;
                                _currentDialogOperation = nextMessageDialog.ShowAsync();
                            }

                            await _currentDialogOperation;

                            lock (_showMonitor)
                                _currentDialogOperation = null;
                        }
                        catch (Exception e)
                        {
                            Debug.WriteLine("MessageDialogService.cs | showDialogAsync | " + e);
                        }
                        lock (_showMonitor)
                        {
                            showing = false;
                            Monitor.Pulse(_showMonitor); //unlock and wait - regains lock after waiting
                        }
                    });


                    lock (_showMonitor)
                    {
                        if (showing)
                        {
                            Debug.WriteLine("MessageDialogService.cs | showDialogAsync | Waiting for MessageDialog to be closed!!");
                            //we must wait here manually for the closing of the dialog, because the dispatcher does not return a waitable task.
                            Monitor.Wait(_showMonitor); //unlock and wait - regains lock after waiting
                        }
                    }
                    Debug.WriteLine("MessageDialogService.cs | showDialogAsync | MessageDialog  was closed.");
                    return true;
                }
            }
            finally
            {
                //make sure we leave this in a clean state
                lock (_queueMonitor)
                {
                    _dialogQueue.Dequeue();
                    Monitor.Pulse(_queueMonitor);
                }
            }
        });
    }


    public void Close(string keyContent="")
    {
        try
        {
            if (keyContent.IsNullOrEmpty())
            {
                lock (_showMonitor)
                {
                    if (_currentDialogOperation == null) return;
                    _currentDialogOperation.Cancel();
                    _currentDialogOperation = null;
                }
            }
            else
            {
                var cancel = false;
                lock (_queueMonitor)
                {
                    if (_dialogQueue.Count == 0)
                        return;

                    var currentDialog = _dialogQueue.Peek();

                    Debug.WriteLine("MessageDialogService.cs | Close | {0}", currentDialog.Content);
                    if (currentDialog.Content == keyContent)
                    {
                        cancel = true;
                    }
                }
                if (!cancel) return;
                lock (_showMonitor)
                {
                    if (_currentDialogOperation == null) return;
                    _currentDialogOperation.Cancel();
                    _currentDialogOperation = null;
                }
            }
        }
        catch (Exception e)
        {
            Debug.WriteLine("MessageDialogService.cs | Close | " + e);
        }

    }

答案 2 :(得分:0)

我想我已经找到了。在主线程之外的任何其他线程中创建消息框时我遇到了同样的问题。 这是C ++解决方案,但我认为您可以轻松转换它;)

    IAsyncOperation<IUICommand^> ^Op = msgbox->ShowAsync();
    task<IUICommand^>( Op ).then([=](IUICommand^ C)
    {

    }).then([](task<void> t) 
        {
            try 
            {
                t.get();
            }
            catch (Platform::Exception ^e)
            {   
                //ERROR!                            
            }           
        });

另外注意,这是处理任何WinRT / Windows 8 Store C ++异常的正确方法。

答案 3 :(得分:0)

您可以随时使用

Execute.OnUIThread( async () => {
...
var dialog = new MessageDialog(yourMessage);
await dialog.ShowAsync();
...
});

如果您尝试从后台线程启动多个对话框,则无法解决UI中的竞争条件。但是你可以使用try / catch来确保覆盖这种情况。