Z
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(5000) 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.ImageUpdateHandler(AddressOf
UpdateImage)
_map.RenderMap(picMap.Height, picMap.Width, cb)
End Sub
'----CALL-BACK DELEGATE WHEN CALL COMPLETES:
Private Sub UpdateImage(ByVal arImage As Byte())
Dim memStream As New System.IO.MemoryStream(arImage)
picMap.Image = Image.FromStream(memStream)
End Sub
public class Map{
.......
public delegate void ImageUpdateHandler(Byte[] arImage);
....
//----RENDER MAP FUNCTION from the _map object:
public void RenderMap(int HEIGHT, int WIDTH, ImageUpdateHandler cb){
map.Height = HEIGHT;
map.Width = WIDTH;
PSTGI.GIS.Map.ImageRetriever c = new ImageRetriever(cb);
c.GetImage();
}
.....
//class nested within map class for convenience
public class ImageRetriever{
PSTGI.GIS.Map.ImageUpdateHandler _cb;
aims.MapClass _map;
//constructor
public ImageRetriever( ImageUpdateHandler cb, aims.MapClass
map){
_cb=cb;
_map=map;
}
//spawns a thread to get the image
public void GetImage(){
System.Threading.Thread t = new System.Threading.Thread(new
System.Threading.ThreadStart(DoIt));
t.Name="GetImageThread";
t.IsBackground=true;
t.Start();
}
// The funcion the new thread executes
public void DoIt(){
Byte[] arImage;
System.Net.WebClient wClient = new System.Net.WebClient();
//System.Threading.Thread.Sleep(5000);
_map.Refresh(); // THIS IS THE 5 SECOND FUNCTION CALL
string imgURL = _map.GetImageAsUrl();
arImage = wClient.DownloadData(imgURL);
object[] args;
args = new object[]{(object)arImage};
_cb.DynamicInvoke(args); //CALL THE DELEGATE IN THE WINFORM
SO GUI KNOWS ITS DONE
}
} //end of ImageRetreiver class
} //end of map class
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(5000) 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.ImageUpdateHandler(AddressOf
UpdateImage)
_map.RenderMap(picMap.Height, picMap.Width, cb)
End Sub
'----CALL-BACK DELEGATE WHEN CALL COMPLETES:
Private Sub UpdateImage(ByVal arImage As Byte())
Dim memStream As New System.IO.MemoryStream(arImage)
picMap.Image = Image.FromStream(memStream)
End Sub
public class Map{
.......
public delegate void ImageUpdateHandler(Byte[] arImage);
....
//----RENDER MAP FUNCTION from the _map object:
public void RenderMap(int HEIGHT, int WIDTH, ImageUpdateHandler cb){
map.Height = HEIGHT;
map.Width = WIDTH;
PSTGI.GIS.Map.ImageRetriever c = new ImageRetriever(cb);
c.GetImage();
}
.....
//class nested within map class for convenience
public class ImageRetriever{
PSTGI.GIS.Map.ImageUpdateHandler _cb;
aims.MapClass _map;
//constructor
public ImageRetriever( ImageUpdateHandler cb, aims.MapClass
map){
_cb=cb;
_map=map;
}
//spawns a thread to get the image
public void GetImage(){
System.Threading.Thread t = new System.Threading.Thread(new
System.Threading.ThreadStart(DoIt));
t.Name="GetImageThread";
t.IsBackground=true;
t.Start();
}
// The funcion the new thread executes
public void DoIt(){
Byte[] arImage;
System.Net.WebClient wClient = new System.Net.WebClient();
//System.Threading.Thread.Sleep(5000);
_map.Refresh(); // THIS IS THE 5 SECOND FUNCTION CALL
string imgURL = _map.GetImageAsUrl();
arImage = wClient.DownloadData(imgURL);
object[] args;
args = new object[]{(object)arImage};
_cb.DynamicInvoke(args); //CALL THE DELEGATE IN THE WINFORM
SO GUI KNOWS ITS DONE
}
} //end of ImageRetreiver class
} //end of map class