-1

I have 3 functions:

  • wait(): to show loading form while processing.
  • runwait(): to make form loading work in thread.
  • stopwait: to stop loading from after process done.

The problem: the functions work good but some times and in some forms show this error "Invoke or BeginInvoke cannot be called on a control until the window handle has been created"

Public Sub wait()
    ld = New Wform
    Application.Run(ld)
End Sub

Public Sub runwait()
    Dim th As System.Threading.Thread = New Threading.Thread(AddressOf wait)
    th.SetApartmentState(ApartmentState.STA)
    th.Start()
End Sub

Public Sub stopwait()
    ld.BeginInvoke(New Action(Sub() ld.Dispose()))
End Sub
Jaghnoon
  • 3
  • 3
  • What is the point of this? Can you just `ld.Show()`, `ld.Dispose()`? – djv Sep 03 '19 at 20:47
  • Without this UI hanging – Jaghnoon Sep 03 '19 at 20:48
  • Possible duplicate of [Invoke or BeginInvoke cannot be called on a control until the window handle has been created](https://stackoverflow.com/questions/808867/invoke-or-begininvoke-cannot-be-called-on-a-control-until-the-window-handle-has) you need to create the control first before doing anything with it; instead of throwing it away off in thread world. [Look](https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.control.createcontrol?view=netframework-4.8) into the `Control.CreateControl` method. – Trevor Sep 03 '19 at 20:50
  • Check if it's disposed, and null: `If Not ld?.IsDisposed Then ld?.BeginInvoke(New Action(Sub() ld?.Dispose()))`. But the way you are threading it introduces some thread synchronization issues so this is a bit hacky and still not entirely atomic. – djv Sep 03 '19 at 20:52
  • Basically this allows you to call `stopwait` twice in a row without an exception, which is probably how you get the error – djv Sep 03 '19 at 20:55
  • Still not working – Jaghnoon Sep 03 '19 at 21:11
  • What kind of application is this where you can just start a new message loop? – djv Sep 03 '19 at 21:27
  • Inventory management system – Jaghnoon Sep 03 '19 at 21:33
  • 2
    I think what @djv is asking is what is the use case for this? I've done many inventory management systems over the years and never once have I had to do anything like this. Perhaps if we understand exactly why you are trying to do this we might be able to provide you with better answers – Hursey Sep 04 '19 at 04:19
  • You shouldn't open the wait form in another thread. You should do the long-running/heavy work in another thread and show the wait form in the existing UI thread. Creating a new message loop just for one form is extremely unnecessary. – Visual Vincent Sep 04 '19 at 07:53
  • @VisualVincent unless this is a console app ;) that's what I was really asking – djv Sep 04 '19 at 15:14
  • @djv : Well that's true. That would however be an odd choice for an inventory system in my opinion, though (also in that case you could just write the "wait" message directly in the console instead :). – Visual Vincent Sep 04 '19 at 16:40

1 Answers1

1

Surely one of these options should satisfy you

1. This is simple. Just show the form and dispose it. It's not modal and it runs on your original UI thread. wait is not a blocking call so there's no need for any additional threading. When the window is closed, Dispose it automatically called after some time. If you try to dispose after closing, there is a chance that it is already disposed and you will get the exception you are getting. See the stopwait method at the bottom which handles this case.

Public Sub wait()
    ld = New Wform()
    ld.Show()
End Sub

2. This is blocking. Showing a dialog window prevents your other code to run. stopwait needs to be called if you want to dispose, because a dialog window is not disposed automatically when closed. Until the form is closed you can't use your calling form.

Public Sub wait()
    ld = New Wform()
    ld.ShowDialog()
End Sub

3. For when wait is being called from a non-UI thread. (1. and 2. can both use this method for UI thread safety.) It is generally good practice to do use the InvokeRequired/Invoke pattern when multiple threads are present in a UI application.

Public Sub wait()
    If Me.InvokeRequired Then
        Me.Invoke(New Action(AddressOf wait))
    Else
        ld = New Wform()
        ld.Show()
    End If
End Sub

ShowDialog and Show should satisfy what you are trying to do in most cases; to block or not to block.

In all cases, stopwait looks the same.

Public Sub stopwait()
    If ld IsNot Nothing Then
        If ld.InvokeRequired Then
            ld.Invoke(New Action(AddressOf stopwait))
        Else
            If Not ld.IsDisposed Then ld.Dispose()
        End If
    End If
End Sub
djv
  • 15,168
  • 7
  • 48
  • 72