|
Thanks, forgot about that.
|
|
|
|
|
I am using the invoke() to call a delegate in order to communicate with another form in a different thread. The code is below.
<br />
<br />
You can see at the bottom (before the stars) where the thread is started. The method below that is being called by the delegate in the code below the stars. Each set of code is in a diff thread.<br />
<br />
public class MainForm : System.Windows.Forms.Form<br />
{<br />
private void search()<br />
{<br />
<br />
Search ArtifactSearch = new ArtifactSearch(this);<br />
<br />
ArtifactSearch.SearchComplete += new Search.ProcessingCompleteDelegate(Search_ProcessingComplete);<br />
<br />
Thread StegSearch = new Thread(new ThreadStart(ArtifactSearch.Start));<br />
<br />
StegSearch.Start();<br />
}<br />
<br />
private void Search_ProcessingComplete()<br />
{<br />
}<br />
<br />
*********************************************************************************************<br />
<br />
public class Search<br />
{<br />
MainForm Parent;<br />
<br />
public event ProcessingCompleteDelegate SearchComplete;<br />
<br />
public Search(MainForm parent)<br />
{<br />
Parent = parent;<br />
}<br />
<br />
public void Start()<br />
{<br />
<br />
Parent.Invoke(SearchComplete,null);<br />
}<br />
}<br />
My problem is that when i invoke the SearchComplete delegate it tells me that "SearchComplete" is not set to an instance of an object. But as you can see it clearly is set to a new instance before the thread is started. How do I fix this. I have used delegates in this manner in other areas of my code and it worked. I'm stumped.
|
|
|
|
|
Not that I'm a threading expert or anything, but I've run into a similar problem before. Is there any place in your code where you are releasing the event? I'm thinking that maybe you should put a lock statement in there somewhere - it seems likely that you are removing the handler for the event before you actually call it (possibly on another thread).
Also, I'm not sure if this is an issue or not, but can you try moving the declaration for the ArtifactSearch object up to the class level. I'm wondering if there might be a scoping issue here that we aren't seeing.
Will
|
|
|
|
|
I'm not familiar with lock statements or releasing events. How do you use the lock statement?
|
|
|
|
|
Actually, after looking it over in light of my other comments, I'm not sure it's needed. Just raise the event and catch it on your form. Then, if InvokeRequired is true, you'll need to call Invoke. I would do this from the form instead of your class, as doing otherwise entangles the two objects. You could have the following, for instance:
public void Search_ProcessingComplete()
{
if(this.InvokeRequired)
{
this.Invoke(Search_ProcessingComplete);
}
else
{
//Do your stuff here that hits the UI.
}
}
On your Search object, your Search method would look like:
public void Start()
{
/// Search would take place here
if(SearchComplete != null) SearchComplete(?);
}
where "?" is the set of parameters you need to pass to invoke the delegate.
|
|
|
|
|
|
this.Invoke(**delegate required here**, args) , what you have is a method
|
|
|
|
|
Right. I should have passed the following in:
this.Invoke(new ProcessingCompleteDelegate(this.Search_ProcessingComplete));
The whole point of the Invoke... methods on the form object is not just to call a delegate, but to actually switch to the thread on which the form is running. Windows Forms are inherently single threaded due to their message loop. This is just a way to get them to run the code in question on the same thread as their message loop, instead of on the background thread. In the above example, I'm calling Invoke to get it to switch over to the UI thread and re-enter the method in that context.
|
|
|
|
|
Use an anonymous method. Here's a code snippet from an MSDN article:
public class SafeLabel : Label
{
delegate void SetString(string text);
delegate string GetString();
override public string Text
{
set
{
if(InvokeRequired)
{
SetString setTextDel = delegate(string text)
{base.Text = text;};
Invoke(setTextDel,new object[]{value});
}
else
base.Text = value;
}
get
{
if(InvokeRequired)
{
GetString getTextDel = delegate(){return base.Text;};
return (string)Invoke(getTextDel,null);
}
else
return base.Text;
}
}
}
Logifusion[^]
|
|
|
|
|
|
the only thing is that i know when invoke is and isn't required so why is this necessary? My issue is that my events do not stay registered and when i call them they are null. I'm lost in what you guys are trying to tell me.
|
|
|
|
|
Are you calling your event using if(SearchComplete != null) SearchComplete(?); or are you using Invoke? Invoke is just the wrong thing to be calling to fire your event.
Logifusion[^]
|
|
|
|
|
You guys will love this. If you simply set the CheckForIllegalCrossThreadCalls to false you can use threads just as you did in VS 2003, which is what I was doing. This was originally written in 2003 and that is why it wasn't working. Now I can manage my own threads without it complaining. What an easy fix.
|
|
|
|
|
From MSDN: When a thread other than the creating thread of a control tries to access one of that control's methods or properties, it often leads to unpredictable results. A common invalid thread activity is a call on the wrong thread that accesses the control's Handle property. Set CheckForIllegalCrossThreadCalls to true to find and diagnose this thread activity more easily.
I surely hope you don't intend to use that in production! I'm also not sure what you mean by "manage my own threads" in VS 2003. I would still get exceptions thrown indicating I was on the wrong thread even in .Net 1.1.
Logifusion[^]
|
|
|
|
|
I only have two threads in the entire application. It is very easy to keep track of what is being accessed by each thread when the number is so small. Thats all I meant. I knew this would pinch a nerve, but my 2003 application has run perfectly since I wrote it. It really is not an issue. This feature was not nearly as strict in 2003 because I ran this exact code and never received a single error.
|
|
|
|
|
Thanks for all of your help. I really appreciate it. I have another major issue however. Could you view my post on listviews. You should see it in the newer posts.
|
|
|
|
|
Did you get this one straightened out?
|
|
|
|
|
well, i reverted to a previous method. I was completely and utterly confused by this conversation.
|
|
|
|
|
This won't work because this.Invoke(Search_ProcessingComplete) is not a valid Invoke statement. That is the wrong parameter for an invoke statement.
|
|
|
|
|
FYI, you can utilize the System.Windows.Forms.WindowsFormsSynchronizationContext object to raise events on the UI thread from your class without having to "entangle" to two objects. You don't even have to entangle your object with Windows forms either; just have it accept any old SynchronizationContext. For Windows Forms apps, you can pass in a WindowsFormsSynchronizationContext and the class then doesn't have to be specific to Windows Forms.
Tech, life, family, faith: Give me a visit.
I'm currently blogging about: Goof around music jam with my brothers (with video)
The apostle Paul, modernly speaking: Epistles of Paul
Judah Himango
|
|
|
|
|
Looks pretty cool. My one complaint being that you're stuck with the SendOrPostCallback delegate which only takes one object. I'm not really sure you can call it raising an event either because it doesn't technically raise any events. But this is definitely a nice new tool that I'll want to experiment with in the future.
Logifusion[^]
|
|
|
|
|
It really addresses a particular problem that encompasses events: SynchronizationContext are meant for doing things (such as calling methods, raising events, etc.) on a particular thread by either Posting (which corresponds to BeginInvoke for Windows Forms) or Sending (which corresponds to Invoke for Windows Forms).
Thus, you *could* make a library that can dispatch events on a UI thread without tying your library to that control, or even to Windows Forms, for that matter. For example:
class MyCrazyLib
{
private SynchronizationContext syncContext;
public event EventHandler WorkProgressed;
public MyCrazyLib(SynchronizationContext eventRaisingSync)
{
this.syncContext = eventRaisingSync;
}
void DoWorkInBackground()
{
ThreadPool.QueueUserWorkItem(MyWorkMethod);
}
void MyWorkMethod(object state)
{
...
this.syncContext.Post(delegate(object state)
{
this.WorkProgressed(someSender, EventArgs.Empty);
});
}
}
There you've got yourself a library that *can* raise events on any thread, UI thread or otherwise. To use this with Windows Forms, you can simply do:
MyCrazyLib lib = new MyCrazyLib(WindowsFormsSynchronizationContext.Current);
lib.WorkProgressed += MyHandler;
lib.DoWorkInBackground();
...
void MyHandler(object sender, EventArgs e)
{
}
Tech, life, family, faith: Give me a visit.
I'm currently blogging about: Goof around music jam with my brothers (with video)
The apostle Paul, modernly speaking: Epistles of Paul
Judah Himango
|
|
|
|
|
Excellent! The name SynchronizationContext gave me pause because it makes me wonder if it's actually going to still let me do my work asynchronously. I can see from your example that it works just fine. Thanks for showing me this!
Logifusion[^]
|
|
|
|
|
SearchComplete is an event, not a delegate. If you want to fire the event, get rid of Parent.Invoke and use if (SearchComplete != null) SearchComplete(this, new EventArgs()); Or whatever you have to pass into your ProcessingCompleteDelegate .
Logifusion[^]
|
|
|
|
|
Whoops. I didn't even see that. Try his suggestion first. You want to raise the event on your class, not on the form. In the handler for the event (on the form), check for InvokeRequired. If it is required, then you'll have to do call this.Invoke to get it back onto your UI thread.
|
|
|
|