Confusion: Threading & COM Interop

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Z D

    Confusion: Threading & COM Interop

    Hello,

    I'm having a strange problem that is probably due to my lack of
    understanding of how threading & COM Interop works in a WinForms.NET
    application.

    Here's the situation:

    I have a 3rd party COM component that takes about 5 seconds to run one of
    its functions (Network IO bound call). Since I dont want my GUI to freeze
    during this 5 seconds, I decided to:

    1) Create a new class that accepts a callback delegate and the COM object
    from the winforms application
    2) This new class will then create a thread, and execute the long running
    function on the COM object
    3) when it completes, the callback delegate that was passed to the class is
    invoked which then changes something in my GUI so I know its done.


    I thought that this would work perfectly because now the IO bound call would
    execute on another thread and would call a delegate which points to a
    function in my GUI that would update a textbox in my GUI.


    Unfortunately, when this new thread calls the COM objects function,
    EVERYTHING in my app freezes until the COM objects function call completes!!
    I dont understand why my GUI thread freezes since the COM call is happening
    on another thread?? In debugger I can see the new thread that is created yet
    my main GUI form freezes???

    NOTE: If I comment out the function call to the COM object and replace it
    with Thread.Sleep(50 00) then it works as expected and my GUI does not
    freeze. So my guess is somehow the COM object takes controll of all threads
    and blocks everything until its completed?? I dont understand! What am I
    doing wrong?


    Also: I just discovered if I change my Form from STA to MTA then it works
    fine! Why is this? I dont want to run my form as MTA because then drag/drop
    OLE wont work.



    Any suggestions? I'm sure I'm doing something wrong.

    Here is the relavent code from my VB.NET winforms and my C# classes:

    Thanks in advance for any help, guidance, comments, suggestions, etc!
    -ZD



    '----FUNCTION CALL FROM THE FORM:
    Private Sub RenderMap()
    Dim cb As New PSTGI.GIS.Map.I mageUpdateHandl er(AddressOf
    UpdateImage)
    _map.RenderMap( picMap.Height, picMap.Width, cb)
    End Sub

    '----CALL-BACK DELEGATE WHEN CALL COMPLETES:
    Private Sub UpdateImage(ByV al arImage As Byte())
    Dim memStream As New System.IO.Memor yStream(arImage )
    picMap.Image = Image.FromStrea m(memStream)
    End Sub



    public class Map{

    .......

    public delegate void ImageUpdateHand ler(Byte[] arImage);

    ....


    //----RENDER MAP FUNCTION from the _map object:
    public void RenderMap(int HEIGHT, int WIDTH, ImageUpdateHand ler cb){
    map.Height = HEIGHT;
    map.Width = WIDTH;

    PSTGI.GIS.Map.I mageRetriever c = new ImageRetriever( cb);
    c.GetImage();
    }

    .....



    //class nested within map class for convenience
    public class ImageRetriever{

    PSTGI.GIS.Map.I mageUpdateHandl er _cb;
    aims.MapClass _map;

    //constructor
    public ImageRetriever( ImageUpdateHand ler cb, aims.MapClass
    map){
    _cb=cb;
    _map=map;
    }


    //spawns a thread to get the image
    public void GetImage(){
    System.Threadin g.Thread t = new System.Threadin g.Thread(new
    System.Threadin g.ThreadStart(D oIt));
    t.Name="GetImag eThread";
    t.IsBackground= true;
    t.Start();
    }


    // The funcion the new thread executes
    public void DoIt(){
    Byte[] arImage;
    System.Net.WebC lient wClient = new System.Net.WebC lient();

    //System.Threadin g.Thread.Sleep( 5000);

    _map.Refresh(); // THIS IS THE 5 SECOND FUNCTION CALL

    string imgURL = _map.GetImageAs Url();
    arImage = wClient.Downloa dData(imgURL);

    object[] args;
    args = new object[]{(object)arImag e};
    _cb.DynamicInvo ke(args); //CALL THE DELEGATE IN THE WINFORM
    SO GUI KNOWS ITS DONE
    }

    } //end of ImageRetreiver class

    } //end of map class




  • Nicholas Paldino [.NET/C# MVP]

    #2
    Re: Confusion: Threading & COM Interop

    Z D,

    See inline:
    [color=blue]
    > 1) Create a new class that accepts a callback delegate and the COM object
    > from the winforms application
    > 2) This new class will then create a thread, and execute the long running
    > function on the COM object[/color]

    This is a bad idea. The COM object has thread affinity (most likely),
    and then you are running it on another thread. If you don't marshal it
    correctly, it will not work. Rather, pass the type of the com object and
    create it on the new thread. Before you create it, set the ApartmentState
    property of the current Thread instance (obtained by calling
    Thread.CurrentT hread) to ApartmentState. STA (if your COM component is an STA
    component, or MTA if it is a MTA component, most likely, it is STA).

    Then make the calls on the COM object.
    [color=blue]
    > 3) when it completes, the callback delegate that was passed to the class
    > is invoked which then changes something in my GUI so I know its done.[/color]

    As well as passing the type of the COM object and the callback, you
    should also pass an implementation of ISynchronizeInv oke, which has an
    Invoke method. The Control class implements this, and you should pass a
    control from your UI (or the main form, if possible). You would pass the
    delegate and the parameters to the Invoke method, and the call will be made
    from the UI in the UI thread (which is causing some of your problems).

    Hope this helps.


    --
    - Nicholas Paldino [.NET/C# MVP]
    - mvp@spam.guard. caspershouse.co m
    [color=blue]
    >
    >
    > I thought that this would work perfectly because now the IO bound call
    > would execute on another thread and would call a delegate which points to
    > a function in my GUI that would update a textbox in my GUI.
    >
    >
    > Unfortunately, when this new thread calls the COM objects function,
    > EVERYTHING in my app freezes until the COM objects function call
    > completes!! I dont understand why my GUI thread freezes since the COM call
    > is happening on another thread?? In debugger I can see the new thread that
    > is created yet my main GUI form freezes???
    >
    > NOTE: If I comment out the function call to the COM object and replace it
    > with Thread.Sleep(50 00) then it works as expected and my GUI does not
    > freeze. So my guess is somehow the COM object takes controll of all
    > threads and blocks everything until its completed?? I dont understand!
    > What am I doing wrong?
    >
    >
    > Also: I just discovered if I change my Form from STA to MTA then it works
    > fine! Why is this? I dont want to run my form as MTA because then
    > drag/drop OLE wont work.
    >
    >
    >
    > Any suggestions? I'm sure I'm doing something wrong.
    >
    > Here is the relavent code from my VB.NET winforms and my C# classes:
    >
    > Thanks in advance for any help, guidance, comments, suggestions, etc!
    > -ZD
    >
    >
    >
    > '----FUNCTION CALL FROM THE FORM:
    > Private Sub RenderMap()
    > Dim cb As New PSTGI.GIS.Map.I mageUpdateHandl er(AddressOf
    > UpdateImage)
    > _map.RenderMap( picMap.Height, picMap.Width, cb)
    > End Sub
    >
    > '----CALL-BACK DELEGATE WHEN CALL COMPLETES:
    > Private Sub UpdateImage(ByV al arImage As Byte())
    > Dim memStream As New System.IO.Memor yStream(arImage )
    > picMap.Image = Image.FromStrea m(memStream)
    > End Sub
    >
    >
    >
    > public class Map{
    >
    > .......
    >
    > public delegate void ImageUpdateHand ler(Byte[] arImage);
    >
    > ....
    >
    >
    > //----RENDER MAP FUNCTION from the _map object:
    > public void RenderMap(int HEIGHT, int WIDTH, ImageUpdateHand ler cb){
    > map.Height = HEIGHT;
    > map.Width = WIDTH;
    >
    > PSTGI.GIS.Map.I mageRetriever c = new ImageRetriever( cb);
    > c.GetImage();
    > }
    >
    > .....
    >
    >
    >
    > //class nested within map class for convenience
    > public class ImageRetriever{
    >
    > PSTGI.GIS.Map.I mageUpdateHandl er _cb;
    > aims.MapClass _map;
    >
    > //constructor
    > public ImageRetriever( ImageUpdateHand ler cb, aims.MapClass
    > map){
    > _cb=cb;
    > _map=map;
    > }
    >
    >
    > //spawns a thread to get the image
    > public void GetImage(){
    > System.Threadin g.Thread t = new System.Threadin g.Thread(new
    > System.Threadin g.ThreadStart(D oIt));
    > t.Name="GetImag eThread";
    > t.IsBackground= true;
    > t.Start();
    > }
    >
    >
    > // The funcion the new thread executes
    > public void DoIt(){
    > Byte[] arImage;
    > System.Net.WebC lient wClient = new System.Net.WebC lient();
    >
    > //System.Threadin g.Thread.Sleep( 5000);
    >
    > _map.Refresh(); // THIS IS THE 5 SECOND FUNCTION CALL
    >
    > string imgURL = _map.GetImageAs Url();
    > arImage = wClient.Downloa dData(imgURL);
    >
    > object[] args;
    > args = new object[]{(object)arImag e};
    > _cb.DynamicInvo ke(args); //CALL THE DELEGATE IN THE
    > WINFORM SO GUI KNOWS ITS DONE
    > }
    >
    > } //end of ImageRetreiver class
    >
    > } //end of map class
    >
    >
    >
    >[/color]


    Comment

    • Z D

      #3
      Re: Confusion: Threading & COM Interop

      Hi Nicholas,

      Thank's very much for your reply.

      A) I did a test and only instantiated the COM object inside my new thread
      and now things seem to work fine, even when I set my winform's main method
      to STA.

      I find this curious. Why would the COM object have an afinity to the thread
      on which it was created? Is this how all .NET objects work? I always
      thought that the thread that made the request to the object is the thread on
      which it is run???

      Am I correct in saying that the original problem was that even though I
      called the method from the COM object on a new thread, it was being executed
      on the thread on which it was created (Winform/GUI thread) and that is why
      the GUI froze?

      B)
      As for the GUI being updated, I've changed the function the callback
      delegate points to to look like this: (essentially I just check to see if an
      invoke is required. If so, I call the same function using me.invoke so that
      it will be called from the Form's thread. This works like a charm.

      '-------Callback delegate points to this function. Called when the image is
      ready.
      Private Sub UpdateImage(ByV al arImage As Byte())
      If Me.InvokeRequir ed() Then
      Dim args As Object() = {arImage}
      Dim dg As New PSTGI.GIS.Map.I mageUpdateHandl er(AddressOf
      UpdateImage)
      Me.Invoke(dg, args)
      Else
      Dim memStream As New System.IO.Memor yStream(arImage )
      picMap.Image = Image.FromStrea m(memStream)
      End If
      End Sub


      C)
      Unfortunately, this solution doesn't quite work for me. If I instantiate
      the COM object on the new thread then my main form cant access it. I need
      to interact with it on my main form in order to render other parts of my
      GUI. This COM object also maintains alot of state and is expensive to
      instantiate.

      Do you have any suggestions on how to architect this? I'm a little lost.

      D) Do you know why my original problem worked fine when I set my main form
      to MTA instead of STA? i.e. in my previous post, everything worked OK if I
      set my form to MTA.

      Thanks so much for all your help I really appreciate it!

      -ZD




      "Nicholas Paldino [.NET/C# MVP]" <mvp@spam.guard .caspershouse.c om> wrote in
      message news:eNLrSKFuEH A.1308@tk2msftn gp13.phx.gbl...[color=blue]
      >Z D,
      >
      > See inline:
      >[color=green]
      >> 1) Create a new class that accepts a callback delegate and the COM object
      >> from the winforms application
      >> 2) This new class will then create a thread, and execute the long running
      >> function on the COM object[/color]
      >
      > This is a bad idea. The COM object has thread affinity (most likely),
      > and then you are running it on another thread. If you don't marshal it
      > correctly, it will not work. Rather, pass the type of the com object and
      > create it on the new thread. Before you create it, set the ApartmentState
      > property of the current Thread instance (obtained by calling
      > Thread.CurrentT hread) to ApartmentState. STA (if your COM component is an
      > STA component, or MTA if it is a MTA component, most likely, it is STA).
      >
      > Then make the calls on the COM object.
      >[color=green]
      >> 3) when it completes, the callback delegate that was passed to the class
      >> is invoked which then changes something in my GUI so I know its done.[/color]
      >
      > As well as passing the type of the COM object and the callback, you
      > should also pass an implementation of ISynchronizeInv oke, which has an
      > Invoke method. The Control class implements this, and you should pass a
      > control from your UI (or the main form, if possible). You would pass the
      > delegate and the parameters to the Invoke method, and the call will be
      > made from the UI in the UI thread (which is causing some of your
      > problems).
      >
      > Hope this helps.
      >
      >
      > --
      > - Nicholas Paldino [.NET/C# MVP]
      > - mvp@spam.guard. caspershouse.co m
      >[color=green]
      >>
      >>
      >> I thought that this would work perfectly because now the IO bound call
      >> would execute on another thread and would call a delegate which points to
      >> a function in my GUI that would update a textbox in my GUI.
      >>
      >>
      >> Unfortunately, when this new thread calls the COM objects function,
      >> EVERYTHING in my app freezes until the COM objects function call
      >> completes!! I dont understand why my GUI thread freezes since the COM
      >> call is happening on another thread?? In debugger I can see the new
      >> thread that is created yet my main GUI form freezes???
      >>
      >> NOTE: If I comment out the function call to the COM object and replace it
      >> with Thread.Sleep(50 00) then it works as expected and my GUI does not
      >> freeze. So my guess is somehow the COM object takes controll of all
      >> threads and blocks everything until its completed?? I dont understand!
      >> What am I doing wrong?
      >>
      >>
      >> Also: I just discovered if I change my Form from STA to MTA then it works
      >> fine! Why is this? I dont want to run my form as MTA because then
      >> drag/drop OLE wont work.
      >>
      >>
      >>
      >> Any suggestions? I'm sure I'm doing something wrong.
      >>
      >> Here is the relavent code from my VB.NET winforms and my C# classes:
      >>
      >> Thanks in advance for any help, guidance, comments, suggestions, etc!
      >> -ZD
      >>
      >>
      >>
      >> '----FUNCTION CALL FROM THE FORM:
      >> Private Sub RenderMap()
      >> Dim cb As New PSTGI.GIS.Map.I mageUpdateHandl er(AddressOf
      >> UpdateImage)
      >> _map.RenderMap( picMap.Height, picMap.Width, cb)
      >> End Sub
      >>
      >> '----CALL-BACK DELEGATE WHEN CALL COMPLETES:
      >> Private Sub UpdateImage(ByV al arImage As Byte())
      >> Dim memStream As New System.IO.Memor yStream(arImage )
      >> picMap.Image = Image.FromStrea m(memStream)
      >> End Sub
      >>
      >>
      >>
      >> public class Map{
      >>
      >> .......
      >>
      >> public delegate void ImageUpdateHand ler(Byte[] arImage);
      >>
      >> ....
      >>
      >>
      >> //----RENDER MAP FUNCTION from the _map object:
      >> public void RenderMap(int HEIGHT, int WIDTH, ImageUpdateHand ler cb){
      >> map.Height = HEIGHT;
      >> map.Width = WIDTH;
      >>
      >> PSTGI.GIS.Map.I mageRetriever c = new ImageRetriever( cb);
      >> c.GetImage();
      >> }
      >>
      >> .....
      >>
      >>
      >>
      >> //class nested within map class for convenience
      >> public class ImageRetriever{
      >>
      >> PSTGI.GIS.Map.I mageUpdateHandl er _cb;
      >> aims.MapClass _map;
      >>
      >> //constructor
      >> public ImageRetriever( ImageUpdateHand ler cb, aims.MapClass
      >> map){
      >> _cb=cb;
      >> _map=map;
      >> }
      >>
      >>
      >> //spawns a thread to get the image
      >> public void GetImage(){
      >> System.Threadin g.Thread t = new
      >> System.Threadin g.Thread(new System.Threadin g.ThreadStart(D oIt));
      >> t.Name="GetImag eThread";
      >> t.IsBackground= true;
      >> t.Start();
      >> }
      >>
      >>
      >> // The funcion the new thread executes
      >> public void DoIt(){
      >> Byte[] arImage;
      >> System.Net.WebC lient wClient = new System.Net.WebC lient();
      >>
      >> //System.Threadin g.Thread.Sleep( 5000);
      >>
      >> _map.Refresh(); // THIS IS THE 5 SECOND FUNCTION CALL
      >>
      >> string imgURL = _map.GetImageAs Url();
      >> arImage = wClient.Downloa dData(imgURL);
      >>
      >> object[] args;
      >> args = new object[]{(object)arImag e};
      >> _cb.DynamicInvo ke(args); //CALL THE DELEGATE IN THE
      >> WINFORM SO GUI KNOWS ITS DONE
      >> }
      >>
      >> } //end of ImageRetreiver class
      >>
      >> } //end of map class
      >>
      >>
      >>
      >>[/color]
      >
      >[/color]


      Comment

      • Nicholas Paldino [.NET/C# MVP]

        #4
        Re: Confusion: Threading &amp; COM Interop

        Z D,

        See inline:
        [color=blue]
        > A) I did a test and only instantiated the COM object inside my new thread
        > and now things seem to work fine, even when I set my winform's main method
        > to STA.
        >
        > I find this curious. Why would the COM object have an afinity to the
        > thread on which it was created? Is this how all .NET objects work? I
        > always thought that the thread that made the request to the object is the
        > thread on which it is run???[/color]

        COM objects have affinity to the apartment that they are created in.
        They can be created in a Single Threaded Apartment (which has only one
        thread and one thread only in it), or the Multi Threaded Apartment (which
        has many threads in it). Most COM objects are STA objects, and are bound to
        the thread that they are created in. The COM subsystem handles the
        marshalling of calls across apartment boundaries (or custom proxies,
        possibly). However, if you don't create the right proxies to make calls
        across the boundaries, you will have adverse effects.
        [color=blue]
        > Am I correct in saying that the original problem was that even though I
        > called the method from the COM object on a new thread, it was being
        > executed on the thread on which it was created (Winform/GUI thread) and
        > that is why the GUI froze?[/color]

        No, it was being called in the thread that you made the call from,
        directly to the object in memory (instead of through a proxy) which was
        created on another thread. Because the call wasn't set up correctly, this
        contributed to the freezing.
        [color=blue]
        > B)
        > As for the GUI being updated, I've changed the function the callback
        > delegate points to to look like this: (essentially I just check to see if
        > an invoke is required. If so, I call the same function using me.invoke so
        > that it will be called from the Form's thread. This works like a charm.
        >
        > '-------Callback delegate points to this function. Called when the image
        > is ready.
        > Private Sub UpdateImage(ByV al arImage As Byte())
        > If Me.InvokeRequir ed() Then
        > Dim args As Object() = {arImage}
        > Dim dg As New PSTGI.GIS.Map.I mageUpdateHandl er(AddressOf
        > UpdateImage)
        > Me.Invoke(dg, args)
        > Else
        > Dim memStream As New System.IO.Memor yStream(arImage )
        > picMap.Image = Image.FromStrea m(memStream)
        > End If
        > End Sub[/color]

        Looks good.
        [color=blue]
        > C)
        > Unfortunately, this solution doesn't quite work for me. If I instantiate
        > the COM object on the new thread then my main form cant access it. I need
        > to interact with it on my main form in order to render other parts of my
        > GUI. This COM object also maintains alot of state and is expensive to
        > instantiate.[/color]

        In this case, you are going to have to marshal a reference to the COM
        object to the new threads. However, this will cause your GUI thread to
        freeze up, as the call will be marshaled back to the Main UI thread, and the
        long expensive call will still take place.
        [color=blue]
        > Do you have any suggestions on how to architect this? I'm a little lost.
        >
        > D) Do you know why my original problem worked fine when I set my main form
        > to MTA instead of STA? i.e. in my previous post, everything worked OK if I
        > set my form to MTA.[/color]

        You could set it to MTA, then what happens is that you will always be
        accessing the call through a proxy (through COM), and it would work on all
        threads (which default to MTA I believe). However, the problem with this is
        that there are other components that you might use (if you use anything that
        requires an ActiveX control) which would cause your UI to break. Generally
        speaking, placing the MTAThread attribute on your UI thread handler method
        is a bad idea.

        What I would do is create the object on another thread, and then
        register the object in the global interface table, then I would have that
        thread wait on a WaitHandle (so that the thread stays alive, and therefore,
        the object), which you then set when you want to dispose of it.

        I'm working on a piece of code that would do this now. I'll post it
        when I am done.


        --
        - Nicholas Paldino [.NET/C# MVP]
        - mvp@spam.guard. caspershouse.co m

        [color=blue]
        >
        > Thanks so much for all your help I really appreciate it!
        >
        > -ZD
        >
        >
        >
        >
        > "Nicholas Paldino [.NET/C# MVP]" <mvp@spam.guard .caspershouse.c om> wrote
        > in message news:eNLrSKFuEH A.1308@tk2msftn gp13.phx.gbl...[color=green]
        >>Z D,
        >>
        >> See inline:
        >>[color=darkred]
        >>> 1) Create a new class that accepts a callback delegate and the COM
        >>> object from the winforms application
        >>> 2) This new class will then create a thread, and execute the long
        >>> running function on the COM object[/color]
        >>
        >> This is a bad idea. The COM object has thread affinity (most likely),
        >> and then you are running it on another thread. If you don't marshal it
        >> correctly, it will not work. Rather, pass the type of the com object and
        >> create it on the new thread. Before you create it, set the
        >> ApartmentState property of the current Thread instance (obtained by
        >> calling Thread.CurrentT hread) to ApartmentState. STA (if your COM
        >> component is an STA component, or MTA if it is a MTA component, most
        >> likely, it is STA).
        >>
        >> Then make the calls on the COM object.
        >>[color=darkred]
        >>> 3) when it completes, the callback delegate that was passed to the class
        >>> is invoked which then changes something in my GUI so I know its done.[/color]
        >>
        >> As well as passing the type of the COM object and the callback, you
        >> should also pass an implementation of ISynchronizeInv oke, which has an
        >> Invoke method. The Control class implements this, and you should pass a
        >> control from your UI (or the main form, if possible). You would pass the
        >> delegate and the parameters to the Invoke method, and the call will be
        >> made from the UI in the UI thread (which is causing some of your
        >> problems).
        >>
        >> Hope this helps.
        >>
        >>
        >> --
        >> - Nicholas Paldino [.NET/C# MVP]
        >> - mvp@spam.guard. caspershouse.co m
        >>[color=darkred]
        >>>
        >>>
        >>> I thought that this would work perfectly because now the IO bound call
        >>> would execute on another thread and would call a delegate which points
        >>> to a function in my GUI that would update a textbox in my GUI.
        >>>
        >>>
        >>> Unfortunately, when this new thread calls the COM objects function,
        >>> EVERYTHING in my app freezes until the COM objects function call
        >>> completes!! I dont understand why my GUI thread freezes since the COM
        >>> call is happening on another thread?? In debugger I can see the new
        >>> thread that is created yet my main GUI form freezes???
        >>>
        >>> NOTE: If I comment out the function call to the COM object and replace
        >>> it with Thread.Sleep(50 00) then it works as expected and my GUI does not
        >>> freeze. So my guess is somehow the COM object takes controll of all
        >>> threads and blocks everything until its completed?? I dont understand!
        >>> What am I doing wrong?
        >>>
        >>>
        >>> Also: I just discovered if I change my Form from STA to MTA then it
        >>> works fine! Why is this? I dont want to run my form as MTA because then
        >>> drag/drop OLE wont work.
        >>>
        >>>
        >>>
        >>> Any suggestions? I'm sure I'm doing something wrong.
        >>>
        >>> Here is the relavent code from my VB.NET winforms and my C# classes:
        >>>
        >>> Thanks in advance for any help, guidance, comments, suggestions, etc!
        >>> -ZD
        >>>
        >>>
        >>>
        >>> '----FUNCTION CALL FROM THE FORM:
        >>> Private Sub RenderMap()
        >>> Dim cb As New PSTGI.GIS.Map.I mageUpdateHandl er(AddressOf
        >>> UpdateImage)
        >>> _map.RenderMap( picMap.Height, picMap.Width, cb)
        >>> End Sub
        >>>
        >>> '----CALL-BACK DELEGATE WHEN CALL COMPLETES:
        >>> Private Sub UpdateImage(ByV al arImage As Byte())
        >>> Dim memStream As New System.IO.Memor yStream(arImage )
        >>> picMap.Image = Image.FromStrea m(memStream)
        >>> End Sub
        >>>
        >>>
        >>>
        >>> public class Map{
        >>>
        >>> .......
        >>>
        >>> public delegate void ImageUpdateHand ler(Byte[] arImage);
        >>>
        >>> ....
        >>>
        >>>
        >>> //----RENDER MAP FUNCTION from the _map object:
        >>> public void RenderMap(int HEIGHT, int WIDTH, ImageUpdateHand ler cb){
        >>> map.Height = HEIGHT;
        >>> map.Width = WIDTH;
        >>>
        >>> PSTGI.GIS.Map.I mageRetriever c = new ImageRetriever( cb);
        >>> c.GetImage();
        >>> }
        >>>
        >>> .....
        >>>
        >>>
        >>>
        >>> //class nested within map class for convenience
        >>> public class ImageRetriever{
        >>>
        >>> PSTGI.GIS.Map.I mageUpdateHandl er _cb;
        >>> aims.MapClass _map;
        >>>
        >>> //constructor
        >>> public ImageRetriever( ImageUpdateHand ler cb, aims.MapClass
        >>> map){
        >>> _cb=cb;
        >>> _map=map;
        >>> }
        >>>
        >>>
        >>> //spawns a thread to get the image
        >>> public void GetImage(){
        >>> System.Threadin g.Thread t = new
        >>> System.Threadin g.Thread(new System.Threadin g.ThreadStart(D oIt));
        >>> t.Name="GetImag eThread";
        >>> t.IsBackground= true;
        >>> t.Start();
        >>> }
        >>>
        >>>
        >>> // The funcion the new thread executes
        >>> public void DoIt(){
        >>> Byte[] arImage;
        >>> System.Net.WebC lient wClient = new
        >>> System.Net.WebC lient();
        >>>
        >>> //System.Threadin g.Thread.Sleep( 5000);
        >>>
        >>> _map.Refresh(); // THIS IS THE 5 SECOND FUNCTION CALL
        >>>
        >>> string imgURL = _map.GetImageAs Url();
        >>> arImage = wClient.Downloa dData(imgURL);
        >>>
        >>> object[] args;
        >>> args = new object[]{(object)arImag e};
        >>> _cb.DynamicInvo ke(args); //CALL THE DELEGATE IN THE
        >>> WINFORM SO GUI KNOWS ITS DONE
        >>> }
        >>>
        >>> } //end of ImageRetreiver class
        >>>
        >>> } //end of map class
        >>>
        >>>
        >>>
        >>>[/color]
        >>
        >>[/color]
        >
        >[/color]


        Comment

        • Willy Denoyette [MVP]

          #5
          Re: Confusion: Threading &amp; COM Interop

          Before you start creating threads and initialize these to enter an STA, I
          would suggest you to check the "Apartment" requirements of your COM object.
          You can do this using oleview.exe
          (http://www.microsoft.com/downloads/d...isplaylang=en).
          or using regedit and search the value of the 'ThreadingModel ' value of your
          component in the registry.
          If your COM object is marked 'ThreadingModel ' = 'Free', you have to
          initialize your thread to enter an MTA.
          If it's marked 'Both' you can initialize the thread as STA or MTA, taking
          care that some (well most) COM objects marked as 'Both' are not tested in a
          multithreaded application.
          Anyway the main thread of a windows application must be an STA thread. This
          required by Windows.Forms OLE drag and Drop support and some COM activeX UI
          elements that could be placed on the form.[color=blue]
          > C)
          > Unfortunately, this solution doesn't quite work for me. If I instantiate
          > the COM object on the new thread then my main form cant access it. I need
          > to interact with it on my main form in order to render other parts of my
          > GUI. This COM object also maintains alot of state and is expensive to
          > instantiate.
          >[/color]
          No, you should update the UI from the alternative thread, the UI thread
          should not use the COM interface, as it will freeze the UI during the call.
          Don't forget to marshal the calls when updating the UI from other threads
          than the main (UI) thread.
          See http://www.pobox.com/~skeet/csharp/t...winforms.shtml
          [color=blue]
          >D[/color]
          The reason that it worked is a non issue, it's not that it works most of the
          time that it will work all time.

          Willy.


          "Z D" <nospam@nospam. com> wrote in message
          news:%2324YEzFu EHA.3088@tk2msf tngp13.phx.gbl. ..[color=blue]
          > Hi Nicholas,
          >
          > Thank's very much for your reply.
          >
          > A) I did a test and only instantiated the COM object inside my new thread
          > and now things seem to work fine, even when I set my winform's main method
          > to STA.
          >
          > I find this curious. Why would the COM object have an afinity to the
          > thread on which it was created? Is this how all .NET objects work? I
          > always thought that the thread that made the request to the object is the
          > thread on which it is run???
          >
          > Am I correct in saying that the original problem was that even though I
          > called the method from the COM object on a new thread, it was being
          > executed on the thread on which it was created (Winform/GUI thread) and
          > that is why the GUI froze?
          >
          > B)
          > As for the GUI being updated, I've changed the function the callback
          > delegate points to to look like this: (essentially I just check to see if
          > an invoke is required. If so, I call the same function using me.invoke so
          > that it will be called from the Form's thread. This works like a charm.
          >
          > '-------Callback delegate points to this function. Called when the image
          > is ready.
          > Private Sub UpdateImage(ByV al arImage As Byte())
          > If Me.InvokeRequir ed() Then
          > Dim args As Object() = {arImage}
          > Dim dg As New PSTGI.GIS.Map.I mageUpdateHandl er(AddressOf
          > UpdateImage)
          > Me.Invoke(dg, args)
          > Else
          > Dim memStream As New System.IO.Memor yStream(arImage )
          > picMap.Image = Image.FromStrea m(memStream)
          > End If
          > End Sub
          >
          >
          > C)
          > Unfortunately, this solution doesn't quite work for me. If I instantiate
          > the COM object on the new thread then my main form cant access it. I need
          > to interact with it on my main form in order to render other parts of my
          > GUI. This COM object also maintains alot of state and is expensive to
          > instantiate.
          >
          > Do you have any suggestions on how to architect this? I'm a little lost.
          >
          > D) Do you know why my original problem worked fine when I set my main form
          > to MTA instead of STA? i.e. in my previous post, everything worked OK if I
          > set my form to MTA.
          >
          > Thanks so much for all your help I really appreciate it!
          >
          > -ZD
          >
          >
          >
          >
          > "Nicholas Paldino [.NET/C# MVP]" <mvp@spam.guard .caspershouse.c om> wrote
          > in message news:eNLrSKFuEH A.1308@tk2msftn gp13.phx.gbl...[color=green]
          >>Z D,
          >>
          >> See inline:
          >>[color=darkred]
          >>> 1) Create a new class that accepts a callback delegate and the COM
          >>> object from the winforms application
          >>> 2) This new class will then create a thread, and execute the long
          >>> running function on the COM object[/color]
          >>
          >> This is a bad idea. The COM object has thread affinity (most likely),
          >> and then you are running it on another thread. If you don't marshal it
          >> correctly, it will not work. Rather, pass the type of the com object and
          >> create it on the new thread. Before you create it, set the
          >> ApartmentState property of the current Thread instance (obtained by
          >> calling Thread.CurrentT hread) to ApartmentState. STA (if your COM
          >> component is an STA component, or MTA if it is a MTA component, most
          >> likely, it is STA).
          >>
          >> Then make the calls on the COM object.
          >>[color=darkred]
          >>> 3) when it completes, the callback delegate that was passed to the class
          >>> is invoked which then changes something in my GUI so I know its done.[/color]
          >>
          >> As well as passing the type of the COM object and the callback, you
          >> should also pass an implementation of ISynchronizeInv oke, which has an
          >> Invoke method. The Control class implements this, and you should pass a
          >> control from your UI (or the main form, if possible). You would pass the
          >> delegate and the parameters to the Invoke method, and the call will be
          >> made from the UI in the UI thread (which is causing some of your
          >> problems).
          >>
          >> Hope this helps.
          >>
          >>
          >> --
          >> - Nicholas Paldino [.NET/C# MVP]
          >> - mvp@spam.guard. caspershouse.co m
          >>[color=darkred]
          >>>
          >>>
          >>> I thought that this would work perfectly because now the IO bound call
          >>> would execute on another thread and would call a delegate which points
          >>> to a function in my GUI that would update a textbox in my GUI.
          >>>
          >>>
          >>> Unfortunately, when this new thread calls the COM objects function,
          >>> EVERYTHING in my app freezes until the COM objects function call
          >>> completes!! I dont understand why my GUI thread freezes since the COM
          >>> call is happening on another thread?? In debugger I can see the new
          >>> thread that is created yet my main GUI form freezes???
          >>>
          >>> NOTE: If I comment out the function call to the COM object and replace
          >>> it with Thread.Sleep(50 00) then it works as expected and my GUI does not
          >>> freeze. So my guess is somehow the COM object takes controll of all
          >>> threads and blocks everything until its completed?? I dont understand!
          >>> What am I doing wrong?
          >>>
          >>>
          >>> Also: I just discovered if I change my Form from STA to MTA then it
          >>> works fine! Why is this? I dont want to run my form as MTA because then
          >>> drag/drop OLE wont work.
          >>>
          >>>
          >>>
          >>> Any suggestions? I'm sure I'm doing something wrong.
          >>>
          >>> Here is the relavent code from my VB.NET winforms and my C# classes:
          >>>
          >>> Thanks in advance for any help, guidance, comments, suggestions, etc!
          >>> -ZD
          >>>
          >>>
          >>>
          >>> '----FUNCTION CALL FROM THE FORM:
          >>> Private Sub RenderMap()
          >>> Dim cb As New PSTGI.GIS.Map.I mageUpdateHandl er(AddressOf
          >>> UpdateImage)
          >>> _map.RenderMap( picMap.Height, picMap.Width, cb)
          >>> End Sub
          >>>
          >>> '----CALL-BACK DELEGATE WHEN CALL COMPLETES:
          >>> Private Sub UpdateImage(ByV al arImage As Byte())
          >>> Dim memStream As New System.IO.Memor yStream(arImage )
          >>> picMap.Image = Image.FromStrea m(memStream)
          >>> End Sub
          >>>
          >>>
          >>>
          >>> public class Map{
          >>>
          >>> .......
          >>>
          >>> public delegate void ImageUpdateHand ler(Byte[] arImage);
          >>>
          >>> ....
          >>>
          >>>
          >>> //----RENDER MAP FUNCTION from the _map object:
          >>> public void RenderMap(int HEIGHT, int WIDTH, ImageUpdateHand ler cb){
          >>> map.Height = HEIGHT;
          >>> map.Width = WIDTH;
          >>>
          >>> PSTGI.GIS.Map.I mageRetriever c = new ImageRetriever( cb);
          >>> c.GetImage();
          >>> }
          >>>
          >>> .....
          >>>
          >>>
          >>>
          >>> //class nested within map class for convenience
          >>> public class ImageRetriever{
          >>>
          >>> PSTGI.GIS.Map.I mageUpdateHandl er _cb;
          >>> aims.MapClass _map;
          >>>
          >>> //constructor
          >>> public ImageRetriever( ImageUpdateHand ler cb, aims.MapClass
          >>> map){
          >>> _cb=cb;
          >>> _map=map;
          >>> }
          >>>
          >>>
          >>> //spawns a thread to get the image
          >>> public void GetImage(){
          >>> System.Threadin g.Thread t = new
          >>> System.Threadin g.Thread(new System.Threadin g.ThreadStart(D oIt));
          >>> t.Name="GetImag eThread";
          >>> t.IsBackground= true;
          >>> t.Start();
          >>> }
          >>>
          >>>
          >>> // The funcion the new thread executes
          >>> public void DoIt(){
          >>> Byte[] arImage;
          >>> System.Net.WebC lient wClient = new
          >>> System.Net.WebC lient();
          >>>
          >>> //System.Threadin g.Thread.Sleep( 5000);
          >>>
          >>> _map.Refresh(); // THIS IS THE 5 SECOND FUNCTION CALL
          >>>
          >>> string imgURL = _map.GetImageAs Url();
          >>> arImage = wClient.Downloa dData(imgURL);
          >>>
          >>> object[] args;
          >>> args = new object[]{(object)arImag e};
          >>> _cb.DynamicInvo ke(args); //CALL THE DELEGATE IN THE
          >>> WINFORM SO GUI KNOWS ITS DONE
          >>> }
          >>>
          >>> } //end of ImageRetreiver class
          >>>
          >>> } //end of map class
          >>>
          >>>
          >>>
          >>>[/color]
          >>
          >>[/color]
          >
          >[/color]


          Comment

          • Sunny

            #6
            Re: Confusion: Threading &amp; COM Interop

            Hi all,

            Will you guys be so kind to tell me if this is a possible solution for
            such a problems:

            In my main app I start a new thread, and there I create a new invisible
            form (named COMForm) and make ApplicationStar t on it.

            On COMForm I instaniate the COM object, and then from all other threads
            I use COMForm.Invoke and COMForm.BeginIn voke to pass the calls in the
            thread which have created the COM object?

            Also, if this is a possible solution, is there a need to implement some
            locking in the methods of COMForms, which are been called with Invoke
            and BeginInvoke?. Or the calls will be queued and there is no need of
            additional synchronization ?

            Or this is bad idea?

            Thanks
            Sunny

            Comment

            • Willy Denoyette [MVP]

              #7
              Re: Confusion: Threading &amp; COM Interop

              Inline ***

              Willy.

              "Sunny" <sunny@newsgrou ps.nospam> wrote in message
              news:eDxmWFruEH A.4028@TK2MSFTN GP15.phx.gbl...[color=blue]
              > Hi all,
              >
              > Will you guys be so kind to tell me if this is a possible solution for
              > such a problems:
              >
              > In my main app I start a new thread, and there I create a new invisible
              > form (named COMForm) and make ApplicationStar t on it.
              >[/color]
              *** Good thing, you create a new thread, initialize it as an STA thread and
              let it pump messages.
              [color=blue]
              > On COMForm I instaniate the COM object, and then from all other threads
              > I use COMForm.Invoke and COMForm.BeginIn voke to pass the calls in the
              > thread which have created the COM object?
              >[/color]
              *** This one possibility, but only needed when making asynchronous calls
              (BeginInvoke).
              If you pass a reference to the COM object to another thread and use that
              reference to call a method, the COM interop layer will marshal the call to
              the correct apartment/thread, note that here the COM call is synchronous so
              will block the caller for the duration of the call.
              [color=blue]
              > Also, if this is a possible solution, is there a need to implement some
              > locking in the methods of COMForms, which are been called with Invoke
              > and BeginInvoke?. Or the calls will be queued and there is no need of
              > additional synchronization ?
              >[/color]
              **** No additional synchronization needed, the apartment is STA so only one
              thread can enter it, additional locking wouldn't help anyway.
              [color=blue]
              > Or this is bad idea?
              >[/color]
              [color=blue]
              > Thanks
              > Sunny[/color]


              Comment

              • Sunny

                #8
                Re: Confusion: Threading &amp; COM Interop

                Inline ***

                Sunny

                In article <#0R2HltuEHA.33 76@TK2MSFTNGP12 .phx.gbl>,
                willy.denoyette @pandora.be says...[color=blue]
                > Inline ***
                >
                > Willy.
                >
                > "Sunny" <sunny@newsgrou ps.nospam> wrote in message
                > news:eDxmWFruEH A.4028@TK2MSFTN GP15.phx.gbl...[color=green]
                > > Hi all,
                > >
                > > Will you guys be so kind to tell me if this is a possible solution for
                > > such a problems:
                > >
                > > In my main app I start a new thread, and there I create a new invisible
                > > form (named COMForm) and make ApplicationStar t on it.
                > >[/color]
                > *** Good thing, you create a new thread, initialize it as an STA thread and
                > let it pump messages.
                >[color=green]
                > > On COMForm I instaniate the COM object, and then from all other threads
                > > I use COMForm.Invoke and COMForm.BeginIn voke to pass the calls in the
                > > thread which have created the COM object?
                > >[/color]
                > *** This one possibility, but only needed when making asynchronous calls
                > (BeginInvoke).
                > If you pass a reference to the COM object to another thread and use that
                > reference to call a method, the COM interop layer will marshal the call to
                > the correct apartment/thread, note that here the COM call is synchronous so
                > will block the caller for the duration of the call.[/color]

                I was not exact in my idea here. I create the COM object as private. The
                access to its methods is done through wrapper methods of COMForm, which
                do check for IsInvokeRequire d and decide if they have to use
                Invoke/BeginInvoke. I agree that this way the calling thread will block
                for long duration invocations, but it depends on the intends, so for
                these calls one can use BeginInvoke/EndInvoke to not block calling
                thread.
                [color=blue]
                >[color=green]
                > > Also, if this is a possible solution, is there a need to implement some
                > > locking in the methods of COMForms, which are been called with Invoke
                > > and BeginInvoke?. Or the calls will be queued and there is no need of
                > > additional synchronization ?
                > >[/color]
                > **** No additional synchronization needed, the apartment is STA so only one
                > thread can enter it, additional locking wouldn't help anyway.[/color]

                Ok, then this seems to be a working solution.

                Thanks for confirming this.

                Cheers
                Sunny

                Comment

                • Z D

                  #9
                  Re: Confusion: Threading &amp; COM Interop

                  Hi Nicholas,

                  I was wondering if you ever completed the piece of code you were working on?

                  Thanks again for all your help.

                  -ZD


                  "Nicholas Paldino [.NET/C# MVP]" <mvp@spam.guard .caspershouse.c om> wrote in
                  message news:ufQ52LGuEH A.1472@TK2MSFTN GP10.phx.gbl...[color=blue]
                  >Z D,
                  >
                  > See inline:
                  >[color=green]
                  >> A) I did a test and only instantiated the COM object inside my new thread
                  >> and now things seem to work fine, even when I set my winform's main
                  >> method to STA.
                  >>
                  >> I find this curious. Why would the COM object have an afinity to the
                  >> thread on which it was created? Is this how all .NET objects work? I
                  >> always thought that the thread that made the request to the object is the
                  >> thread on which it is run???[/color]
                  >
                  > COM objects have affinity to the apartment that they are created in.
                  > They can be created in a Single Threaded Apartment (which has only one
                  > thread and one thread only in it), or the Multi Threaded Apartment (which
                  > has many threads in it). Most COM objects are STA objects, and are bound
                  > to the thread that they are created in. The COM subsystem handles the
                  > marshalling of calls across apartment boundaries (or custom proxies,
                  > possibly). However, if you don't create the right proxies to make calls
                  > across the boundaries, you will have adverse effects.
                  >[color=green]
                  >> Am I correct in saying that the original problem was that even though I
                  >> called the method from the COM object on a new thread, it was being
                  >> executed on the thread on which it was created (Winform/GUI thread) and
                  >> that is why the GUI froze?[/color]
                  >
                  > No, it was being called in the thread that you made the call from,
                  > directly to the object in memory (instead of through a proxy) which was
                  > created on another thread. Because the call wasn't set up correctly, this
                  > contributed to the freezing.
                  >[color=green]
                  >> B)
                  >> As for the GUI being updated, I've changed the function the callback
                  >> delegate points to to look like this: (essentially I just check to see if
                  >> an invoke is required. If so, I call the same function using me.invoke so
                  >> that it will be called from the Form's thread. This works like a charm.
                  >>
                  >> '-------Callback delegate points to this function. Called when the image
                  >> is ready.
                  >> Private Sub UpdateImage(ByV al arImage As Byte())
                  >> If Me.InvokeRequir ed() Then
                  >> Dim args As Object() = {arImage}
                  >> Dim dg As New PSTGI.GIS.Map.I mageUpdateHandl er(AddressOf
                  >> UpdateImage)
                  >> Me.Invoke(dg, args)
                  >> Else
                  >> Dim memStream As New System.IO.Memor yStream(arImage )
                  >> picMap.Image = Image.FromStrea m(memStream)
                  >> End If
                  >> End Sub[/color]
                  >
                  > Looks good.
                  >[color=green]
                  >> C)
                  >> Unfortunately, this solution doesn't quite work for me. If I instantiate
                  >> the COM object on the new thread then my main form cant access it. I
                  >> need to interact with it on my main form in order to render other parts
                  >> of my GUI. This COM object also maintains alot of state and is expensive
                  >> to instantiate.[/color]
                  >
                  > In this case, you are going to have to marshal a reference to the COM
                  > object to the new threads. However, this will cause your GUI thread to
                  > freeze up, as the call will be marshaled back to the Main UI thread, and
                  > the long expensive call will still take place.
                  >[color=green]
                  >> Do you have any suggestions on how to architect this? I'm a little lost.
                  >>
                  >> D) Do you know why my original problem worked fine when I set my main
                  >> form to MTA instead of STA? i.e. in my previous post, everything worked
                  >> OK if I set my form to MTA.[/color]
                  >
                  > You could set it to MTA, then what happens is that you will always be
                  > accessing the call through a proxy (through COM), and it would work on all
                  > threads (which default to MTA I believe). However, the problem with this
                  > is that there are other components that you might use (if you use anything
                  > that requires an ActiveX control) which would cause your UI to break.
                  > Generally speaking, placing the MTAThread attribute on your UI thread
                  > handler method is a bad idea.
                  >
                  > What I would do is create the object on another thread, and then
                  > register the object in the global interface table, then I would have that
                  > thread wait on a WaitHandle (so that the thread stays alive, and
                  > therefore, the object), which you then set when you want to dispose of it.
                  >
                  > I'm working on a piece of code that would do this now. I'll post it
                  > when I am done.
                  >
                  >
                  > --
                  > - Nicholas Paldino [.NET/C# MVP]
                  > - mvp@spam.guard. caspershouse.co m
                  >
                  >[color=green]
                  >>
                  >> Thanks so much for all your help I really appreciate it!
                  >>
                  >> -ZD
                  >>
                  >>
                  >>
                  >>
                  >> "Nicholas Paldino [.NET/C# MVP]" <mvp@spam.guard .caspershouse.c om> wrote
                  >> in message news:eNLrSKFuEH A.1308@tk2msftn gp13.phx.gbl...[color=darkred]
                  >>>Z D,
                  >>>
                  >>> See inline:
                  >>>
                  >>>> 1) Create a new class that accepts a callback delegate and the COM
                  >>>> object from the winforms application
                  >>>> 2) This new class will then create a thread, and execute the long
                  >>>> running function on the COM object
                  >>>
                  >>> This is a bad idea. The COM object has thread affinity (most
                  >>> likely), and then you are running it on another thread. If you don't
                  >>> marshal it correctly, it will not work. Rather, pass the type of the
                  >>> com object and create it on the new thread. Before you create it, set
                  >>> the ApartmentState property of the current Thread instance (obtained by
                  >>> calling Thread.CurrentT hread) to ApartmentState. STA (if your COM
                  >>> component is an STA component, or MTA if it is a MTA component, most
                  >>> likely, it is STA).
                  >>>
                  >>> Then make the calls on the COM object.
                  >>>
                  >>>> 3) when it completes, the callback delegate that was passed to the
                  >>>> class is invoked which then changes something in my GUI so I know its
                  >>>> done.
                  >>>
                  >>> As well as passing the type of the COM object and the callback, you
                  >>> should also pass an implementation of ISynchronizeInv oke, which has an
                  >>> Invoke method. The Control class implements this, and you should pass a
                  >>> control from your UI (or the main form, if possible). You would pass
                  >>> the delegate and the parameters to the Invoke method, and the call will
                  >>> be made from the UI in the UI thread (which is causing some of your
                  >>> problems).
                  >>>
                  >>> Hope this helps.
                  >>>
                  >>>
                  >>> --
                  >>> - Nicholas Paldino [.NET/C# MVP]
                  >>> - mvp@spam.guard. caspershouse.co m
                  >>>
                  >>>>
                  >>>>
                  >>>> I thought that this would work perfectly because now the IO bound call
                  >>>> would execute on another thread and would call a delegate which points
                  >>>> to a function in my GUI that would update a textbox in my GUI.
                  >>>>
                  >>>>
                  >>>> Unfortunately, when this new thread calls the COM objects function,
                  >>>> EVERYTHING in my app freezes until the COM objects function call
                  >>>> completes!! I dont understand why my GUI thread freezes since the COM
                  >>>> call is happening on another thread?? In debugger I can see the new
                  >>>> thread that is created yet my main GUI form freezes???
                  >>>>
                  >>>> NOTE: If I comment out the function call to the COM object and replace
                  >>>> it with Thread.Sleep(50 00) then it works as expected and my GUI does
                  >>>> not freeze. So my guess is somehow the COM object takes controll of
                  >>>> all threads and blocks everything until its completed?? I dont
                  >>>> understand! What am I doing wrong?
                  >>>>
                  >>>>
                  >>>> Also: I just discovered if I change my Form from STA to MTA then it
                  >>>> works fine! Why is this? I dont want to run my form as MTA because then
                  >>>> drag/drop OLE wont work.
                  >>>>
                  >>>>
                  >>>>
                  >>>> Any suggestions? I'm sure I'm doing something wrong.
                  >>>>
                  >>>> Here is the relavent code from my VB.NET winforms and my C# classes:
                  >>>>
                  >>>> Thanks in advance for any help, guidance, comments, suggestions, etc!
                  >>>> -ZD
                  >>>>
                  >>>>
                  >>>>
                  >>>> '----FUNCTION CALL FROM THE FORM:
                  >>>> Private Sub RenderMap()
                  >>>> Dim cb As New PSTGI.GIS.Map.I mageUpdateHandl er(AddressOf
                  >>>> UpdateImage)
                  >>>> _map.RenderMap( picMap.Height, picMap.Width, cb)
                  >>>> End Sub
                  >>>>
                  >>>> '----CALL-BACK DELEGATE WHEN CALL COMPLETES:
                  >>>> Private Sub UpdateImage(ByV al arImage As Byte())
                  >>>> Dim memStream As New System.IO.Memor yStream(arImage )
                  >>>> picMap.Image = Image.FromStrea m(memStream)
                  >>>> End Sub
                  >>>>
                  >>>>
                  >>>>
                  >>>> public class Map{
                  >>>>
                  >>>> .......
                  >>>>
                  >>>> public delegate void ImageUpdateHand ler(Byte[] arImage);
                  >>>>
                  >>>> ....
                  >>>>
                  >>>>
                  >>>> //----RENDER MAP FUNCTION from the _map object:
                  >>>> public void RenderMap(int HEIGHT, int WIDTH, ImageUpdateHand ler cb){
                  >>>> map.Height = HEIGHT;
                  >>>> map.Width = WIDTH;
                  >>>>
                  >>>> PSTGI.GIS.Map.I mageRetriever c = new ImageRetriever( cb);
                  >>>> c.GetImage();
                  >>>> }
                  >>>>
                  >>>> .....
                  >>>>
                  >>>>
                  >>>>
                  >>>> //class nested within map class for convenience
                  >>>> public class ImageRetriever{
                  >>>>
                  >>>> PSTGI.GIS.Map.I mageUpdateHandl er _cb;
                  >>>> aims.MapClass _map;
                  >>>>
                  >>>> //constructor
                  >>>> public ImageRetriever( ImageUpdateHand ler cb, aims.MapClass
                  >>>> map){
                  >>>> _cb=cb;
                  >>>> _map=map;
                  >>>> }
                  >>>>
                  >>>>
                  >>>> //spawns a thread to get the image
                  >>>> public void GetImage(){
                  >>>> System.Threadin g.Thread t = new
                  >>>> System.Threadin g.Thread(new System.Threadin g.ThreadStart(D oIt));
                  >>>> t.Name="GetImag eThread";
                  >>>> t.IsBackground= true;
                  >>>> t.Start();
                  >>>> }
                  >>>>
                  >>>>
                  >>>> // The funcion the new thread executes
                  >>>> public void DoIt(){
                  >>>> Byte[] arImage;
                  >>>> System.Net.WebC lient wClient = new
                  >>>> System.Net.WebC lient();
                  >>>>
                  >>>> //System.Threadin g.Thread.Sleep( 5000);
                  >>>>
                  >>>> _map.Refresh(); // THIS IS THE 5 SECOND FUNCTION CALL
                  >>>>
                  >>>> string imgURL = _map.GetImageAs Url();
                  >>>> arImage = wClient.Downloa dData(imgURL);
                  >>>>
                  >>>> object[] args;
                  >>>> args = new object[]{(object)arImag e};
                  >>>> _cb.DynamicInvo ke(args); //CALL THE DELEGATE IN THE
                  >>>> WINFORM SO GUI KNOWS ITS DONE
                  >>>> }
                  >>>>
                  >>>> } //end of ImageRetreiver class
                  >>>>
                  >>>> } //end of map class
                  >>>>
                  >>>>
                  >>>>
                  >>>>
                  >>>
                  >>>[/color]
                  >>
                  >>[/color]
                  >
                  >[/color]


                  Comment

                  Working...