2

I have a scripting language implemented in WPF that allows a user to script multiple concurrent actions. These actions are implemented using async/await and all of them run on the main thread.

I'm now introducing a step debugger into this script language. Currently, there is one window associated with the script engine and a different window for the step debugger.

I'm trying to accomplish stopping processes in the engine window just before a scripted action executes while not blocking interactivity on the debugger window. MessageBox.Show() works great for this especially because it pauses animations which may themselves start new actions on completion.

Thread.Sleep() of course doesn't work at all because everyone is on the main thread.

Is there a pausing mechanism in C# similar to MessageBox.Show() but with no UI? I've tried calling Dispatcher.DisableProcessing() which I could have sworn worked to pause the engine window, but now I get the error:

'Dispatcher processing has been suspended, but messages are still being processed'

I think ideally I'd like to do something like this:

//Engine Window
private debugger;
void RunAction(ScriptAction action){
 if(action.BreakPoint){
  var pauserObject = PauseProcessesOnThisWindow();
  debugger.break(action, pauseObject);
}
}

//Debugger Window

void Continue_Click(){
 pauseObject.release();
}

If I have to go with the MessageBox approach, I can see that closing it from the other window can be tricky, not to mention that it's an awkward UI to have some control in the debugger window but still require the user to close the box from the engine window.

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
jchristof
  • 2,794
  • 7
  • 32
  • 47
  • 3
    What if create invisible window (with size 1x1) and call ShowDialog on it? To release you can just set Result of that window. – Evk May 11 '17 at 17:31
  • Interesting idea - I'll give it a try. – jchristof May 11 '17 at 17:37
  • I tried var win = new Window(); win.Owner = EngineWindow; win.ShowDialog(); It appears to block interaction with both the debugger and engine windows. – jchristof May 11 '17 at 18:24
  • 2
    @jchristof, have a look at [this one](http://stackoverflow.com/a/21655620/1768303), but note it still runs a nested message loop, which opens a whole can of worms (like re-entrancy issues described [here](https://blogs.msdn.microsoft.com/jfoscoding/2005/08/06/keeping-your-ui-responsive-and-the-dangers-of-application-doevents/)). At least you should disable the UI (`ShowDialog` uses `EnableWindow` API for this). – noseratio May 11 '17 at 19:13
  • I found your problem interesting. It is far above my present level of knowledge but if it could help there is maybe something to do with *Begin/EndModalMessageLoop* like this question (about forms but there are lots of similiraties sometimes) [entering-form-modal-message-loop-without-actual-form-showing](http://stackoverflow.com/questions/22305324/) – NGI May 11 '17 at 19:13

1 Answers1

5

Personally, I avoid nested message loops. They're considerably dangerous due to reentrancy.

I would recommend using a dedicated thread for your script window (see "Multiple Windows, Multiple Threads" in the WPF Threading Model documentation). Running a separate window in a separate thread is much easier on WPF than it ever was with WinForms.

If you do want a nested message loop, though, you can create one using PushFrame.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • I think that might be the ticket esp. since I already have a script editor and console window that also run separately from the engine window but on the main thread. Script editing can be janky when the engine is running a lot of animations. I'll take a closer look at the page. Thanks! – jchristof May 11 '17 at 21:24
  • Great reference - thank you. I decided to prototype stopping processes by having each await a "Debug" Task that waits until Timeout.Infinite. The debugger window can eventually cancel that task and allow them all to continue. The precision is not like calling wait(), but it might be sufficient for what I need and doesn't require me to spawn a new UI thread. – jchristof May 16 '17 at 20:57