none
CoreDispatcher.ProcessEvents() causes an indirect crash?

    Question

  • I have to port some legacy code, that uses modal dialog boxes all over the place to Metro/WinRT. Because these dialog boxes provide their own message loop (using DialogBoxParam()), the calling code will wait until the user has clicked a button on the message box.

    I'm currently trying to write a replacement for the old message box class, that uses XAML and the popup control. To reproduce the same behavior, I have to wait in the calling thread, but also have to keep the UI responsive. I've found out, that CoreDispatcher::ProcessEvents() can be used in a loop, to keep processing events (yeah I realize that this isn't very beautiful, but I don't want to change all of our legacy code to a new threading model). However I'm running into an issue that keeps crashing my app.

    Here is a minimal example that reproduces the issue (just create a XAML app and wire this to a button):

    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;
    }

    This seems to work well for the first one or two tries of opening and closing the popup, using the two buttons. After a few tries however, the application will crash in Windows.UI.Xaml.dll, while trying to dereference a null pointer. I can also reproduce this in C# (with practically the same code).

    Does anyone have an idea, what is going on in here? Or a suggestion for an alternative approach?

    • Edited by ollb Monday, September 03, 2012 9:21 AM
    Monday, September 03, 2012 9:21 AM

All replies

  • WinRT blocks nested message loops, so porting this directly won't work.

    instead you need to model this as an async operation.

    I'm new to Xaml but am familiar with WinRT. I put together this sample to demonstrate... the code that you would place after the "Show dialog" returns needs to be placed in the Popup Closed event handler. 

    void MainPage::PopupButtonClick(Object ^, RoutedEventArgs ^)
    {
        buttonClicked = true;   // record that a button was pressed here
        popup->IsOpen = false; // close popup
    }
    
    void MainPage::PopupClosed(Object ^popup, Object ^args)
    { 
        Output->Text = buttonClicked ? "Button Clicked" : "Light dismiss invoked";
        this->IsEnabled = true; // re-enable the main UI
    }
    
    void MainPage::PickFolder_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
    {
        popup = ref new Popup(); // its best to define this in a .xaml file so you don't have to construct it in code
        auto button = ref new Button();
        button->Content = "OK";
        button->Click += ref new RoutedEventHandler(this, &MainPage::PopupButtonClick);
        popup->Child = button;
        popup->Closed += ref new EventHandler<Object^>(this, &MainPage::PopupClosed);
        this->IsEnabled = false; // disable the main UI to keep it from getting input
        popup->IsLightDismissEnabled = true;
        buttonClicked = false;
        popup->IsOpen = true; // show the dialog
    }

     

    • Edited by Chris Guzak Tuesday, September 04, 2012 3:36 AM
    Monday, September 03, 2012 9:22 PM
  • Thanks for your answer and code sample. I'm looking into ways to rewrite parts of the existing code, but I'm afraid that this will be a lot of work.

    Are you sure that this is in fact unsupported? If WinRT would block nested message loops, shouldn't I get a nice and clean exception, when trying to do this? Right now the behavior is just confusing, because it partially works, but MAY also crash. Frankly, I think it looks like a bug in the WinRT libraries. They shouldn't just crash the program if the user uses them incorrectly.


    • Edited by ollb Tuesday, September 04, 2012 2:30 PM
    Tuesday, September 04, 2012 2:29 PM
  • it is a bug that it does not fail directly.

    Tuesday, September 11, 2012 4:24 AM
  •  A workaround that works fine for me is to replace the line:

    Window::Current->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessOneAndAllPending);

    with:

    auto myDispatchedHandler = ref new DispatchedHandler([&](){

    dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessOneAndAllPending);

    });

    dispatcher->RunAsync(CoreDispatcherPriority::Normal,myDispatchedHandler);


    I got this from here:
    http://social.msdn.microsoft.com/Forums/windowsapps/en-US/ce522990-90ed-4f70-a8d9-dfcce97abc82/incompatibility-report-dispatcherprocessevents-is-no-longer-allowed-in-background-calls


    • Edited by AidanMck Wednesday, October 16, 2013 3:25 PM
    Wednesday, October 16, 2013 3:24 PM
  •  A workaround...
    For anyone reading my answer, note that crashing can still occur so it is not a real solution to the problem.
    Saturday, November 02, 2013 2:29 PM