我必须移植一些遗留代码,它们使用模态对话框到Metro / WinRT(使用C ++ / CX)。因为这些对话框提供了自己的消息循环(使用DialogBoxParam()
),所以调用代码将等到用户单击消息框上的按钮。
我目前正在尝试为旧的消息框类编写替代品,该类使用XAML和弹出控件。为了重现相同的行为,我必须在调用线程中等待,但也必须保持UI响应。我发现,CoreDispatcher::ProcessEvents()
可以在循环中使用,以保持处理事件(是的,我知道这不是很漂亮,但我不想将所有遗留代码更改为新的线程模型)。但是我遇到了一个让我的应用程序崩溃的问题。
这是一个重现问题的最小示例(只需创建一个XAML应用并将其连接到一个按钮):
void CPPXamlTest::MainPage::Button_Click_1(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
bool cancel = false;
auto popup = ref new Popup();
auto button = ref new Button();
button->Content = "Boom";
auto token = (button->Click += ref new RoutedEventHandler([&cancel] (Object ^, RoutedEventArgs ^) { cancel = true; }));
popup->Child = button;
popup->IsOpen = true;
while (!cancel)
{
Window::Current->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessOneAndAllPending);
}
popup->IsOpen = false;
button->Click -= token;
}
这似乎适用于使用两个按钮打开和关闭弹出窗口的前一两次尝试。但是,经过几次尝试后,应用程序将立即在Windows.UI.Xaml.dll
中崩溃,同时尝试取消引用空指针。我也可以在C#中重现这一点(实际上代码相同)。
有没有人有想法,这里发生了什么?或建议采用其他方法?
答案 0 :(得分:0)
如果有人有兴趣:几天后我在MSDN论坛上问了同样的问题,并得到了微软员工的回复:
显然这里的问题是在事件处理程序中调用ProcessEvents引起的嵌套消息循环。 WinRT似乎不支持这种做法,但这不会以明确定义的方式失败,而是会导致崩溃。
这是我能找到的最好和唯一的答案,所以我最终通过将事件处理程序(和许多其他代码)分派到另一个线程来解决问题。然后,我可以通过等待用户在我的XAML“对话框”弹出窗口中单击/点击按钮时发出信号的事件来模拟DialogBox()
/ DialogBoxParam()
(主线程之外)的等待行为。
答案 1 :(得分:0)
对我来说很好的解决方法是替换以下行:
Window::Current->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessOneAndAllPending);
使用:
auto myDispatchedHandler = ref new DispatchedHandler([&](){
Window::Current->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessOneAndAllPending);
});
dispatcher->RunAsync(CoreDispatcherPriority::Normal,myDispatchedHandler);
有关详细信息,请参阅MSDN上的this post。