Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / .NET Framework / New Users / December 2007

Tip: Looking for answers? Try searching our database.

One for the multithreading Gurus

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
dgleeson3@eircom.net - 09 Dec 2007 21:55 GMT
Hello All

Im having lots of fun with window handles and invoke.

The code started off in a single class. Main thread set up a worker
thread and the worker thread updated the progress bar on form1 (which
only has a progress bar and a button). All worked well - No problems.

The implementation then changed to 2 classes. This code is given
below.

First problem was with beginInvoke. An exception was being generated
saying that
"Invoke or BeginInvoke cannot be called on a control until the window
handle has been created."
So to ensure I had a handle to the progress bar I simply referenced
the handle in the constructor see
       winhandle = ProgressBar1.Handle
This solved this problem. Though Im not sure whats going on.

Now Im on the next problem. When BeginInvoke is called as in
"        result = Form1.ProgressBar1.BeginInvoke(New
InvokeDelegate(AddressOf Form1.UpdateProgressDisplay), 25)"
and when Im debugging the code I find myself stepping through the
constructor for Form1 again. Its like as if the form1 object has been
removed and a reference to it is causing it to be created again.

Iv tried "    Public Shared ProgressBar1 As New ProgressBar" in an
effort to see if the form1 object is being removed. And ive passed the
form1 to the second class in its constructor. All to no avail.

Any Gurus able to point me in the right direction.

-----------------------------------------------------------------------------------------------------------
Imports System
Imports System.Collections.Generic
Imports System.Text
Imports System.Threading
Imports System.IO

Public Class Form1
   'Dim Communications_thread As New Thread(AddressOf
DoRemoteCommunications)
   Dim worker As System.Threading.Thread                ' Thread
   Dim worker_obj As New worker_class(Me)                   ' Object
from worker class
   ' Set up delegate for assync function call.
--------------------------------
   'Public Delegate Sub Async_Update_Progress_caller(ByVal
Value_for_progress_bar As Integer)
   Public Shared ProgressBar1 As New ProgressBar
   'Delegate Sub InvokeDelegate()
   Public winhandle As IntPtr

   Public Sub New()
       Me.InitializeComponent()
       'ProgressBar1
       '
       Me.SuspendLayout()

       ProgressBar1 = New System.Windows.Forms.ProgressBar

       ProgressBar1.Name = "ProgressBar1"
       ProgressBar1.Maximum = 100
       ProgressBar1.Value = 10
       ProgressBar1.Location = New System.Drawing.Point(69, 120)
       ProgressBar1.Size = New System.Drawing.Size(145, 23)
       ProgressBar1.TabIndex = 0
       ' To have handle for progress bar available. Othyerwise
beginInvoke fails because handle is not available.
       ' I think this may actually be creating the handle, which may
not exist otherwise.
       winhandle = ProgressBar1.Handle

       Me.Controls.Add(ProgressBar1)

       Me.ResumeLayout(False)

   End Sub

   Public Sub UpdateProgressDisplay(ByVal Value_for_progress_bar As
Integer)

       ProgressBar1.Value = Value_for_progress_bar

   End Sub

   Private Sub Button1_Click(ByVal sender As System.Object, ByVal e
As System.EventArgs) Handles Button1.Click

       worker = New Thread(AddressOf
worker_obj.DoRemoteCommunications)

       ' Now actually start the comms thread.
       worker.Start()
   End Sub

End Class

Public Class worker_class

   Delegate Sub InvokeDelegate(ByVal temp As Integer)
   Public form_reference As Form

   Public Sub New(ByVal form_ref)
       form_reference = form_ref
   End Sub

   Public Sub DoRemoteCommunications()
       Console.WriteLine("Comms worker thread started.....")

       ' Initiate the asynchronous call.
       Dim result As IAsyncResult

       result = Form1.ProgressBar1.BeginInvoke(New
InvokeDelegate(AddressOf Form1.UpdateProgressDisplay), 25)
       Console.WriteLine("Progress value 25 passed ")
       Thread.Sleep(3000)

       result = Form1.ProgressBar1.BeginInvoke(New
InvokeDelegate(AddressOf Form1.UpdateProgressDisplay),
Form1.ProgressBar1.Maximum)
       Console.WriteLine("Comms worker thread Finished.")

   End Sub

End Class

-----------------------------------------------------------------------------------------------------

Thanks

Denis

____________________
http://www.CentronSolutions.com
Mike Williams - 09 Dec 2007 22:02 GMT
> Hello All. Im having lots of fun with window handles
> and invoke.

Welcome to the imposter.

> The code started off in a single class. Main thread set
> up a worker thread and the worker thread updated . . .

Welcome to the imposter.

> First problem was with beginInvoke. An exception
> was being generated saying that "Invoke or . . "

Welcome to the imposter.

> Any Gurus able to point me in the right direction.

Yes. Have a look for newsgroups that deal with the imposter. This group is
for the *real* Visual Basic.

Mike
Steve Gerrard - 09 Dec 2007 23:26 GMT
As Mike said (cryptically), this should be in a .Net newsgroup, so leave off the
microsoft.public.vb.general.discussion.

You need  to understand forms and classes and instances better before you tackle
worker threads. You should probably change your worker class  to work within the
same thread first, and get the form reference all squared away, before doing a
worker thread and invoking a method in the main thread.

>        ' I think this may actually be creating the handle, which may
> not exist otherwise.
>        winhandle = ProgressBar1.Handle
>    Dim worker_obj As New worker_class(Me)

Creating a new instance of a form does *not* load it, and therefore does *not*
create the windows, or handles to those windows. Only when the form loads are
those available. You are forcing the form to load in the constructor, which is
daft. You think you need this because your attempt at invoking a method is
creating a second instance of Form1, which otherwise never gets loaded. See
below.

>    Dim worker_obj As New worker_class(Me)

Okay, your worker_obj instance of class worker_class gets passed a reference to
the actual instance of the calling form.

> Public Class worker_class....

>    Public form_reference As Form

Why is form_reference of class Form, not class Form1? And why is it Public?

>    Public Sub New(ByVal form_ref)
>        form_reference = form_ref
>    End Sub

Okay, that will store a reference to the calling form, when an instance of
worker_class is created. It would be smarter to specify the type, not leave it
as Object.

Now that you have it, it seems like it would make sense to use form_reference
somewhere in your worker_class code, don't you think?

>        result = Form1.ProgressBar1.BeginInvoke(New

Having just saved a reference to the actual form instance, why are you now
referring to Form1? Form1 is a class name. It can also be a default instance
name, but often not the instance you want. The above line is triggering the
creation of a new (second) instance of Form1, not referring to the already
loaded instance that called this method.

Can you guess what you should put instead of Form1 in the line above?
MikeD - 10 Dec 2007 00:50 GMT
>> Hello All. Im having lots of fun with window handles
>> and invoke.
[quoted text clipped - 15 lines]
> Yes. Have a look for newsgroups that deal with the imposter. This group is
> for the *real* Visual Basic.

And he probably has no clue at all what you're talking about.  <g>

To the OP, you cross-posted to both VB classic (VB6 and under) and .NET
newsgroups.  Don't do this.

Signature

Mike
Microsoft MVP Visual Basic

Peter Duniho - 09 Dec 2007 23:39 GMT
> [...]
> First problem was with beginInvoke. An exception was being generated  
[quoted text clipped - 5 lines]
>         winhandle = ProgressBar1.Handle
> This solved this problem. Though Im not sure whats going on.

Yes, retrieving the handle forces it to be created.  However, it would be  
better to just either not start whatever processing might call  
BeginInvoke() until the form has been loaded and/or shown (see FormLoad  
and FormShown events).  Alternatively, the processing code calling  
BeginInvoke() should just check the IsHandleCreated property before trying  
to invoke and not bother if the control hasn't been fully initialized yet.

The way you're doing it now, you allow some update to be invoked on the  
progress bar before the form is really completely initialized.  By forcing  
the progress bar itself to be initialized enough, you may avoid problems.  
But there's no guarantee of that, and it could easily lead to a subtle,  
hard to find bug later.

> Now Im on the next problem. When BeginInvoke is called as in
> "        result = Form1.ProgressBar1.BeginInvoke(New
[quoted text clipped - 6 lines]
> effort to see if the form1 object is being removed. And ive passed the
> form1 to the second class in its constructor. All to no avail.

Do you have an example of the code that generates that behavior?  The code  
you posted is the version with the static/shared ProgressBar instance.

One thing I notice is that BeginInvoke() takes as the second parameter an  
array, not a single value.  I'm not that familiar with the VB syntax, and  
if you're sure your code is correct then I'll take your word for it.  But  
what I expected to see there is you created a new Object() array with one  
element, set to the value of 25 (which will wind up boxed, being a value  
type).

If you have that parameter wrong, in C# you just wouldn't be able to  
compile the code.  But in VB maybe it compiles but then causes some weird  
error.

The other thing I notice is that it _looks_ like you are calling an  
instance method in your Form1 class (UpdateProgressDisplay), but using the  
synax for a static/shared method.  Again, not being that familiar with VB  
syntax maybe I'm wrong, but I thought you'd have a "Shared" in the method  
declaration if it were a static method.  If it _is_ an instance method,  
then maybe VB is creating a new dummy instance for you when you call  
BeginInvoke(), since you didn't provide an instance.

Again, in C# you just wouldn't be able to do that.  But maybe in VB it's  
doing something on your behalf (scary, but possible).

Finally, you should look at the BackgroundWorker class.  At least for this  
simple kind of processing you've got here, it's exactly the right thing to  
use.  It has a ProgressChanged event that the Form1 class can subscribe  
to, as well as a RunWorkerCompleted event.  Both of these events will be  
raised on the form instance's thread, eliminating any need for you to mess  
with BeginInvoke() at all.  It would eliminate the issues you've described  
here.

For RunWorkerCompleted, it's raised automatically for you when your DoWork  
handler returns.  For the ProgressChanged event, your worker thread code  
will need to call the ReportProgress() method any time you want the event  
raised.

Pete
Jim Mack - 10 Dec 2007 01:38 GMT
Please trim the group 'microsoft.public.vb.general.discussion' from
any replies you make to this thread

It's inappropriate for that group.

Thanks.
dgleeson3@eircom.net - 10 Dec 2007 11:24 GMT
Hi Guys

Thanks for the input. Appologies to the VB6'rs

Pete responses below.

On Dec 9, 11:39 pm, "Peter Duniho" <NpOeStPe...@nnowslpianmk.com>
wrote:
> > [...]
> > First problem was with beginInvoke. An exception was being generated  
[quoted text clipped - 18 lines]
> But there's no guarantee of that, and it could easily lead to a subtle,  
> hard to find bug later.

So the worker thread is not setup until the user can press the button
on the form.
Long after form load. But Im now checking with IsHandleCreated. Very
strange!

> > Now Im on the next problem. When BeginInvoke is called as in
> > "        result = Form1.ProgressBar1.BeginInvoke(New
[quoted text clipped - 16 lines]
> element, set to the value of 25 (which will wind up boxed, being a value  
> type).

Ok Ive modified this setting up an array. No change in operation with
my problem.VB was
helping me on this.

> If you have that parameter wrong, in C# you just wouldn't be able to  
> compile the code.  But in VB maybe it compiles but then causes some weird  
[quoted text clipped - 7 lines]
> then maybe VB is creating a new dummy instance for you when you call  
> BeginInvoke(), since you didn't provide an instance.

Nice one.
This appears to be the problem.
Changing the method decleration to include "shared" removes the
behaviour where
the form1 object is being re created.

   Public Shared Sub UpdateProgressDisplay(ByVal
Value_for_progress_bar As
Integer)

This is strange as Im not receiving even a warning during the build.
Multi threaded VB apps
may be a step too far. Might be time for C#.

Thanks for your help.

Denis

> Again, in C# you just wouldn't be able to do that.  But maybe in VB it's  
> doing something on your behalf (scary, but possible).
[quoted text clipped - 13 lines]
>
> Pete

Rate this thread:







Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.