Block like ShowDialog blocks, but without a form...

lordofduct

Well-known member
Joined
Jun 24, 2010
Messages
71
Programming Experience
3-5
So you know how when you call "Form.ShowDialog" from where ever you call it, it blocks until the form being displayed closes. And then the function returns whatever result it was.

I want to know how to block like, but without a form, yet still have the "main ui thread".


Basically I have this long process that needs to thread out and do several things... and it's heavily event based. It jumps between two threads, the main ui thread, and it's own background thread. The main ui thread needs to have access so that I can randomly open different dialog boxes for things... sometimes they're just windows asking to hit OK or Cancel to continue, others it's to perform some action, etc etc.

But there is NO central form related to it. It's a complete background thread running that randomly hops over to the main ui thread for things.


Right now I have this class I designed called lets say "DoJobProcessor". On it is a function called "BeginDoJob" that threads off from the thread pool... when ever I need to jump back I use a ManualResetEvent to block on the background thread, jump over to to the main thread with "invoke" and passing a reference to the MRE, then release the block when done there and continue on on this background thread.

All while this is going on I just set the form I'm trying to block the main ui thread from managing by just setting its Enabled property false.

It all feels just messy and annoying... and I was just writing something using ShowDialog and I was like... HEY, what if I could block this BeginDoJob method, hi-jack the main ui thread, then let that function return the result when done instead of having this "Complete" event with the return value in it. It's obviously possible because ShowDialog does it, but I don't know how.

This way the job I'm performing isn't always waiting for the main ui thread, instead its the other way around... the ui thread waiting for it, the way it aught to be. Show something, branch off do some stuff and just idle the display while it does it, come back show something else, branch off and just idle while it does that.

So any help?
 
Your analogy doesn't really hold water. You say that you want to block like ShowDialog but there's nothing multi-threaded about ShowDialog. Any form you display by calling ShowDialog is created on the same thread.

Either you want the UI thread blocked or you don't. If you don't then don't block it. If you do then you have two choices. You can call Thread.Join, but only if you created the Thread object yourself. Alternatively, you can use a WaitHandle, which it sounds like you already are. Blocking one thread until another thread says it can continue is exactly what the WaitHandle class is for, so it sounds like you're already doing exactly what you should be doing.
 
i want the method to block... not the thread... I do what I need by blocking threads right now, but I don't want to do it that way, because I'm constantly having to reach back to the main ui thread to do random things... where as if I could just stay on the main ui thread.

ShowDialog the method blocks from where it's called (the function blocks, it doesn't block the main ui thread though... how I don't know), and returns once that window is closed. That's the functionality I want... the "other thread" is merely something that's ALSO going on with my code. But my main thing is I want to steal the main ui thread from some place it's being called and hold it for my own for a while until I decide to return right back to where this was began... just like ShowDialog.



You know how you can say:

If myForm.ShowDialog() = Ok Then Do Something

Yet when this line of code runs a window opens and the window is allowed full freedom on the main ui thread, yet once that window closes the code just starts right up where this line of code called ShowDialog.




Of course I could accomplish this by creating a dummy form and letting it ride the thread for me... but that's just dirty code IMO. Why have a dummy form that serves no purpose but to block some function call?
 
Last edited:
Here's the implementation of ShowDialog, courtesy of .NET Reflector:
VB.NET:
Public Function ShowDialog(ByVal owner As IWin32Window) As DialogResult
    If (owner Is Me) Then
        Throw New ArgumentException(SR.GetString("OwnsSelfOrOwner", New Object() { "showDialog" }), "owner")
    End If
    If MyBase.Visible Then
        Throw New InvalidOperationException(SR.GetString("ShowDialogOnVisible", New Object() { "showDialog" }))
    End If
    If Not MyBase.Enabled Then
        Throw New InvalidOperationException(SR.GetString("ShowDialogOnDisabled", New Object() { "showDialog" }))
    End If
    If Not Me.TopLevel Then
        Throw New InvalidOperationException(SR.GetString("ShowDialogOnNonTopLevel", New Object() { "showDialog" }))
    End If
    If Me.Modal Then
        Throw New InvalidOperationException(SR.GetString("ShowDialogOnModal", New Object() { "showDialog" }))
    End If
    If Not SystemInformation.UserInteractive Then
        Throw New InvalidOperationException(SR.GetString("CantShowModalOnNonInteractive"))
    End If
    If (((Not owner Is Nothing) AndAlso ((CInt(UnsafeNativeMethods.GetWindowLong(New HandleRef(owner, Control.GetSafeHandle(owner)), -20)) And 8) = 0)) AndAlso TypeOf owner Is Control) Then
        owner = DirectCast(owner, Control).TopLevelControlInternal
    End If
    Me.CalledOnLoad = False
    Me.CalledMakeVisible = False
    Me.CloseReason = CloseReason.None
    Dim capture As IntPtr = UnsafeNativeMethods.GetCapture
    If (capture <> IntPtr.Zero) Then
        UnsafeNativeMethods.SendMessage(New HandleRef(Nothing, capture), &H1F, IntPtr.Zero, IntPtr.Zero)
        SafeNativeMethods.ReleaseCapture
    End If
    Dim activeWindow As IntPtr = UnsafeNativeMethods.GetActiveWindow
    Dim handle As IntPtr = IIf((owner Is Nothing), activeWindow, Control.GetSafeHandle(owner))
    MyBase.Properties.SetObject(Form.PropDialogOwner, owner)
    Dim ownerInternal As Form = Me.OwnerInternal
    If (TypeOf owner Is Form AndAlso (Not owner Is ownerInternal)) Then
        Me.Owner = DirectCast(owner, Form)
    End If
    Try 
        MyBase.SetState(&H20, True)
        Me.dialogResult = DialogResult.None
        MyBase.CreateControl
        If ((handle <> IntPtr.Zero) AndAlso (handle <> MyBase.Handle)) Then
            If (UnsafeNativeMethods.GetWindowLong(New HandleRef(owner, handle), -8) = MyBase.Handle) Then
                Throw New ArgumentException(SR.GetString("OwnsSelfOrOwner", New Object() { "showDialog" }), "owner")
            End If
            UnsafeNativeMethods.GetWindowLong(New HandleRef(Me, MyBase.Handle), -8)
            UnsafeNativeMethods.SetWindowLong(New HandleRef(Me, MyBase.Handle), -8, New HandleRef(owner, handle))
        End If
        Try 
            If (Me.dialogResult = DialogResult.None) Then
                [B][U]Application.RunDialog(Me)[/U][/B]
            End If
        Finally
            If Not UnsafeNativeMethods.IsWindow(New HandleRef(Nothing, activeWindow)) Then
                activeWindow = handle
            End If
            If (UnsafeNativeMethods.IsWindow(New HandleRef(Nothing, activeWindow)) AndAlso SafeNativeMethods.IsWindowVisible(New HandleRef(Nothing, activeWindow))) Then
                UnsafeNativeMethods.SetActiveWindow(New HandleRef(Nothing, activeWindow))
            ElseIf (UnsafeNativeMethods.IsWindow(New HandleRef(Nothing, handle)) AndAlso SafeNativeMethods.IsWindowVisible(New HandleRef(Nothing, handle))) Then
                UnsafeNativeMethods.SetActiveWindow(New HandleRef(Nothing, handle))
            End If
            Me.SetVisibleCore(False)
            If MyBase.IsHandleCreated Then
                If ((Not Me.OwnerInternal Is Nothing) AndAlso Me.OwnerInternal.IsMdiContainer) Then
                    Me.OwnerInternal.Invalidate(True)
                    Me.OwnerInternal.Update
                End If
                Me.DestroyHandle
            End If
            MyBase.SetState(&H20, False)
        End Try
    Finally
        Me.Owner = ownerInternal
        MyBase.Properties.SetObject(Form.PropDialogOwner, Nothing)
    End Try
    Return Me.DialogResult
End Function
You can follow the chain from there if you like but I think you'll find that there's various inaccessible methods involved. Basically, a message loop is created for the form. I'm not sure that that can be done without a window handle but you could investigate.
 
Right now I have this class I designed called lets say "DoJobProcessor". On it is a function called "BeginDoJob" that threads off from the thread pool... when ever I need to jump back I use a ManualResetEvent to block on the background thread, jump over to to the main thread with "invoke" and passing a reference to the MRE, then release the block when done there and continue on on this background thread.
Invoke is a blocking call, it returns when whatever is done in UI thread (from that call) has completed, so I don't understand why you need the MRE ?
 
lordofduct, are you asking if you want the background thread to open a dialog tied to the main form & the background thread is halted until the dialog is acknowledged, also the main form can't do anything until the dialog is acknowledged?
 
Invoke is a blocking call, it returns when whatever is done in UI thread (from that call) has completed, so I don't understand why you need the MRE ?

Now that's the kind of information I need!

Thanks JohnH



Also, thank jmcilhinney, that should be of help as well.
 
Back
Top