06 August 2013

Xojo - Using MsgBox from a thread

I recently needed to debug some threaded REALbasic code. I could not use the IDE debugger because the issue I had occured only in built apps. Even worse, I needed to stop the thread at certain places so that I could investigate what it had done so far (i.e. writing a file, and looking at the data written).

An easy way to do this usually is to use MsgBox - it stops the code execution until you press the OK button.

However, calling MsgBox from within a thread is not working, at least not when building for Cocoa.

The solution is to put the thread to sleep temporarily, then have non-threaded code call MsgBox, and when that's done, wake up the thread again.

To put your own thread to sleep, call App.CurrentThread.Suspend.
To wake it up again, call its Resume function.

Here's a code example of how I solved this. Place both methods into a Module, and invoke MsgBoxFromThread from your thread.

Sub MsgBoxFromThread(msg as String)
  // Invoke this method to use MsgBox from within a thread.
  
  // Note: This is not multi-thread-safe! I.e, if you have more
  // than one thread from which you invoke this function, the
  // code needs to move away from single global/static variables
  // and instead maintain a queue for the threads and their
  // respective messages to be shown.
  // (See the "Tasks" project from the Xojo Examples to learn
  // how to maintain such a queue).
  
  dim currentThread as Thread = App.CurrentThread
  if currentThread = nil then
    // the main thread is active - we can simply call MsgBox here
    MsgBox msg
    return
  end if
  
  // Create a timer for executing the MsgBox instruction
  // and waking up the thread afterwards again
  static t as Timer
  if t = nil then
    t = new Timer
    AddHandler t.Action, AddressOf MsgBoxFromThreadShow
    t.Period = 0
  end if
  
  // Store the msg and thread for the Timer
  mMsg = msg
  mThread = currentThread
  
  // Start the timer and put this thread to sleep
  t.Mode = 1
  currentThread.Suspend
End Sub

Private Sub MsgBoxFromThreadShow(t as Timer)
  MsgBox mMsg
  // Once MsgBox is finished, wake up the thread again:
  mThread.Resume
End Sub

No comments:

Post a Comment