473,605 Members | 2,448 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Confusion: Threading & COM Interop

Z D
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


Nov 16 '05 #1
8 8185
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]
- mv*@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

Nov 16 '05 #2
Z D
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]" <mv*@spam.guard .caspershouse.c om> wrote in
message news:eN******** ******@tk2msftn gp13.phx.gbl...
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]
- mv*@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


Nov 16 '05 #3
Z D,

See inline:
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???
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.
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?
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.
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
Looks good.
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.
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.
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.
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]
- mv*@spam.guard. caspershouse.co m


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

-ZD


"Nicholas Paldino [.NET/C# MVP]" <mv*@spam.guard .caspershouse.c om> wrote
in message news:eN******** ******@tk2msftn gp13.phx.gbl...
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]
- mv*@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



Nov 16 '05 #4
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.
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.
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
D 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" <no****@nospam. com> wrote in message
news:%2******** ********@tk2msf tngp13.phx.gbl. .. 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]" <mv*@spam.guard .caspershouse.c om> wrote
in message news:eN******** ******@tk2msftn gp13.phx.gbl...
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]
- mv*@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



Nov 16 '05 #5
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
Nov 16 '05 #6
Inline ***

Willy.

"Sunny" <su***@newsgrou ps.nospam> wrote in message
news:eD******** ******@TK2MSFTN GP15.phx.gbl...
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.
*** Good thing, you create a new thread, initialize it as an STA thread and
let it pump messages.
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?
*** 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.
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 ?
**** No additional synchronization needed, the apartment is STA so only one
thread can enter it, additional locking wouldn't help anyway.
Or this is bad idea?
Thanks
Sunny

Nov 16 '05 #7
Inline ***

Sunny

In article <#0************ **@TK2MSFTNGP12 .phx.gbl>,
wi************* @pandora.be says...
Inline ***

Willy.

"Sunny" <su***@newsgrou ps.nospam> wrote in message
news:eD******** ******@TK2MSFTN GP15.phx.gbl...
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.
*** Good thing, you create a new thread, initialize it as an STA thread and
let it pump messages.
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?

*** 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.


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.
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 ?

**** No additional synchronization needed, the apartment is STA so only one
thread can enter it, additional locking wouldn't help anyway.


Ok, then this seems to be a working solution.

Thanks for confirming this.

Cheers
Sunny
Nov 16 '05 #8
Z D
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]" <mv*@spam.guard .caspershouse.c om> wrote in
message news:uf******** ******@TK2MSFTN GP10.phx.gbl...
Z D,

See inline:
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???


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.
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?


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.
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


Looks good.
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.


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.
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.


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]
- mv*@spam.guard. caspershouse.co m


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

-ZD


"Nicholas Paldino [.NET/C# MVP]" <mv*@spam.guard .caspershouse.c om> wrote
in message news:eN******** ******@tk2msftn gp13.phx.gbl...
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]
- mv*@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




Nov 16 '05 #9

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

0
2345
by: roy | last post by:
I try to call com written in VB 6.0. When I use VS.net Studio to do the debuging, some time it works fine, some time I got the following message: Server Error in '/GISOnlineReservation' Application. ----------------------------------------------------------- --------------------- Configuration Error Description: An error occurred during the processing of a
3
406
by: Avi Kadosh | last post by:
hi all I am new to Csharp I tried the following code using System; using System.Threading;
8
1181
by: Z D | last post by:
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
8
2438
by: NewUser | last post by:
Hello, I'm a new user to Visual Basic.net and I would appreciate any help regarding a problem I have. I have searched the posts in this newsgroup and the VB library for threading topics, but I get confused with the code that is displayed. Anyway, I have a System.Timers.Timer object in a form and its interval is 50 ms. I want to create a thread around the Timer_Elapsed event (ie. myThread = new System.Threading.Thread(AddressOf...
2
2596
by: 1944USA | last post by:
I am re-architecting a C# application written as a multithreaded Windows Service and trying to squeeze every bit of performance out of it. 1) Does the thread that an object is instantiated on have any impact on its performnce? Example: if I instantiate object "X" on thread "A" pass a reference of "X" to Thread "B" and then have "B" run "X" (Exclusively). Does
14
2914
by: Christian Kaiser | last post by:
We have a component that has no window. Well, no window in managed code - it uses a DLL which itself uses a window, and this is our problem! When the garbage collector runs and removes our component (created dynamically by, say, a button click, and then not referenced any more), the GC runs in a different thread, which prohibits the DLL to destroy its window, resulting in a GPF when the WndProc of that window is called - the code is gone...
11
2180
by: Steve Smith | last post by:
I have written a winforms application that launches approximately 150 threads with Thread.ThreadStart() Each thread uses CDO 1.21 to logon to a different Exchange mailbox and send/receive a number of mail messages, reporting back to the UI thread through the use of a Queue object. When all messages that are expected have been received, each thread sends a final update to the UI and the method should exit, which should terminate the...
3
1700
by: archana | last post by:
Hi all, I have one confusion regarding threading in windows service which is developed in c#. What i am doing is on 'onstart' event i am starting one thread. In thread procedure i am processing some URL's asynchronously. Say in thread procedure for validating url through webrequest i am
6
2858
by: George Sakkis | last post by:
I'm baffled with a situation that involves: 1) an instance of some class that defines __del__, 2) a thread which is created, started and referenced by that instance, and 3) a weakref proxy to the instance that is passed to the thread instead of 'self', to prevent a cyclic reference. This probably sounds like gibberish so here's a simplified example: ==========================================
0
8004
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main usage, and What is the difference between ONU and Router. Let’s take a closer look ! Part I. Meaning of...
1
8071
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows Update option using the Control Panel or Settings app; it automatically checks for updates and installs any it finds, whether you like it or not. For most users, this new feature is actually very convenient. If you want to control the update process,...
0
6743
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
1
5886
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules. He will explain when you may want to use classes instead of User Defined Types (UDT). For example, to manage the data in unbound forms. Adolph will...
0
3912
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
0
3958
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
1
2438
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system
1
1541
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.
0
1271
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating effective websites that not only look great but also perform exceptionally well. In this comprehensive...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.