I am programming using the WUApiLib library from Microsoft. I have written a simple application that searches for all software updates that are not installed and then downloads and installs them. This part is working perfectly (I used the code found here to help: http://www.nullskull.com/a/1592/install-windows-updates-using-c--wuapi.aspx).
However, I want to make use of the BeginDownload, EndDownload, BeginInstall, EndInstall functionality of the library so that it can report progress back to the interface. The functions in that article are synchronous and the functions I mention are asynchronous.
I am using the first answer on this page as a template:
C# and WUAPI: BeginDownload function
However, my Invoke() function is never getting called and I can't figure out why.
iUpdateDownloader_onProgressChanged progress = new iUpdateDownloader_onProgressChanged(this);
IDownloadJob downloadJob = downloader.BeginDownload(progress, new iUpdateDownloader_onCompleted(this), new iUpdateDownloader_state(this));
public class iUpdateDownloader_onProgressChanged : IDownloadProgressChangedCallback
{
private frmMain form1;
public iUpdateDownloader_onProgressChanged(frmMain mainForm)
{
this.form1 = mainForm;
}
// Implementation of IDownloadProgressChangedCallback interface...
public void Invoke(IDownloadJob downloadJob, IDownloadProgressChangedCallbackArgs e)
{
decimal bDownloaded = ((e.Progress.TotalBytesDownloaded / 1024) / 1024);
decimal bToDownloaded = ((e.Progress.TotalBytesToDownload / 1024) / 1024);
bDownloaded = decimal.Round(bDownloaded, 2);
bToDownloaded = decimal.Round(bToDownloaded, 2);
form1.setDownloadProgressText("Downloading Update: "
+ e.Progress.CurrentUpdateIndex
+ "/"
+ downloadJob.Updates.Count
+ " - "
+ bDownloaded + "Mb"
+ " / "
+ bToDownloaded + "Mb");
}
}
I can put a breakpoint on the first line in the Invoke function and it never reaches there.
Any ideas?
First thing I notice that's wrong: the call to form1.setDownloadProgressText is a call to the UI made from a different, asynchronous thread. You'll want to wrap that in a this.Invoke (if WinForms) or Dispatcher.Invoke (if WPF) to marshall the UI manipulation to the UI thread.
Also, the "this" variable is not available in the list of member variable declarations that occur before the constructor function, so
iUpdateDownloader_onProgressChanged progress = new iUpdateDownloader_onProgressChanged(this);
IDownloadJob downloadJob = downloader.BeginDownload(progress, new
iUpdateDownloader_onCompleted(this), new iUpdateDownloader_state(this));
isn't going to work unless it's wrapped in a constructor or form load event handler, which isn't pictured here.
MY GUESS IS THAT YOUR onState OBJECT IS GETTING INVOKED BEFORE THE ONE SHOWN HERE, AND IT IS ATTEMPTING TO MANIPULATE THE FORM FROM A THREAD OTHER THAN THE UI ONE, AND THIS BLOW-UP IS PREVENTING THE OTHER OBJECTS (INCLUDING THE ONE DEPICTED HERE) FROM EVER BEING INVOKED.
Related
First, I have Windows app with a couple of methods.
One within main Form class:
private void generate_button_Click(object sender, EventArgs e)
{
GenerationThreads nw = new GenerationThreads();
nw.control = testlabel;
// nw.DelegateUpdater();
Thread tr1 = new Thread(new ThreadStart(nw.DelegateUpdater));
tr1.Start();
Thread.Sleep(100);
and two in GenerationThreads class:
public void DelegateUpdater()
{
string envelope = "*Envelope was brought safely.*";
string iterations = "";
for (int x = 1; x <= 3; x++)
{
iterations = iterations + x.ToString() + " iterations done.\n";
PassToLegate(iterations);
Thread.Sleep(400);
}
PassToLegate(iterations + envelope);
}
private void PassToLegate(string text)
{
if (control.InvokeRequired == true)
{
var hat = new Legate(PassToLegate);
string message = text + " (Legate involved)";
control.Invoke(hat, new object[] {message});
}
if (control.InvokeRequired == false)
{
control.Text = text + " (Legate not involved)";
}
}
When DelegateUpdater() is called directly after pressing button, like in commented part - without making a new thread - it just nicely executes three for() spins, displaying them all at once, with an annotation, that Legate wasn't involved at the end - as it's supposed to.
But when a new thread is involved, apart from having said for() display neatly sequenced, I also get both annotations - that the Legate was and wasn't involved.
Why is control.InvokeRequired() acting like true and false at the same time? Why does
if(control.InvokeRequired == false)
{
control.Text = text + " (Legate not involved)";
}
the "text" variable in this particular fragment seem empty? When it's being milled by delegate as "message" - it isn't. And finally, why, after deleting this part of the code completely, the control doesn't show any of the new data at all - even though it was supposed to pass through delegate already? Clearly something about the mechanics still eludes me and I can't put the finger on it. If someone could please explain, what is the real data flow in here.
Thanks!
I also get both annotations - that the Legate was and wasn't involved.
You get both annotations because the code to each is executed.
You first get the " (Legate involved)" annotation because when the PassToLegate() method is first called, the code is executing in the wrong thread. I.e. InvokeRequired returns true. Thus the annotation is appended to the original text value passed to the method.
In that same block of code, you then call control.Invoke(hat, new object[] {message});. And the delegate variable hat has been initialized to new Legate(PassToLegate);. That causes the PassToLegate() method to be called again, but on the UI thread instead. The new message value, i.e. the text value with the " (Legate involved)" annotation appended, is passed as the parameter.
So the method is called a second time, this time on the UI thread. And in that case, the InvokeRequired is now false, and you add the annotation " (Legate not involved)". Since the text parameter passed already had the " (Legate involved)" annotation appended to it, now both annotations are part of the string value assigned to the Text property.
For what it's worth, it is my opinion that the code should never bother with InvokeRequired. It is safe to call Invoke() when you are on the UI thread already (though of course ideally one would never do that), and so the Invoke() method itself has to do the equivalent of checking InvokeRequired anyway. You might as well just always call Invoke() all the time and keep the code simpler.
Even better would be to use higher-level abstractions such as BackgroundWorker, Progress<T>, and async/await (the last one being especially preferable when appropriate). Then you don't need to deal with the Invoke() method at all; the cross-thread invocations are handled automatically for you.
I have a structural problem resulting in an System.ObjectDisposedException with a dll.
More specifically it's a µEye Camera driver that is advised to capture data from a camera. I got two events from this camera that are fired asynchronously:
OnFrameEvent
OnSequenceEvent
The latter tells me that my capture is beeing completed and I can continue in saving my images.
This is the code that does the work:
private void onSequenceEvent(object sender, EventArgs e)
{
uEye.Camera Camera = sender as uEye.Camera;
SequenceCount++;
Camera.Acquisition.Stop();
int s32SeqID;
statusRet = Camera.Memory.Sequence.GetLast(out s32SeqID);
Invoke((MethodInvoker)delegate ()
{
lblStatus.Text = "Save Images...";
this.pbCapture.Value = 0;
});
Rectangle src = new Rectangle();
Rectangle dst = new Rectangle();
src.X = AOI_Size.X;
src.Y = AOI_Size.Y;
src.Width = AOI_Size.Width;
src.Height = AOI_Size.Height;
dst.X = 0; dst.Y = 0; dst.Width = AOI_Size.Width; dst.Height = AOI_Size.Height;
Bitmap bitmap_source = new Bitmap(MySensorInfo.MaxSize.Width, MySensorInfo.MaxSize.Height);;
Bitmap bitmap_destination = new Bitmap(dst.Width, dst.Height);;
Graphics g = Graphics.FromImage(bitmap_destination);
for (int i = 1; i < s32SeqID; i++)
{
Camera.Memory.ToBitmap(i, out bitmap_source);
g.DrawImage(bitmap_source, dst, src, GraphicsUnit.Pixel);
bitmap_destination.Save(PathToSave + i.ToString() + ".bmp");
this?.Invoke((MethodInvoker)delegate ()
{
pbOverallProgress.Value++;
pbCapture.Value++;
});
}
bitmap_source.Dispose();
g.Dispose();
this.CloseForm?.Invoke(1);
}
However - as you may expect the Invoke call will cause the ObjectDisposedException as the "Cancel"-Button is beeing pressed:
this?.Invoke((MethodInvoker)delegate ()
{
pbOverallProgress.Value++;
pbCapture.Value++;
});
Here's the code of the Cancel-Button:
private void btn_Exit_Click(object sender, EventArgs e)
{
if (MessageBox.Show("Do you really want to cancel?", "Abort", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
{
this.CloseForm?.Invoke(0);
}
}
private void UEye_Dialog_Form_CloseForm(int exitCode)
{
this?.Invoke((MethodInvoker)delegate ()
{
if (Camera != null)
{
Camera.EventFrame -= onFrameEvent;
Camera.EventSequence -= onSequenceEvent;
}
Camera?.Acquisition.Stop();
Camera = null;
ReturnCode = exitCode;
this.Close();
});
}
UEye_Dialog_Form_CloseForm(int exitCode) is a event where CloseForm is it's delegate.
I hope this was not too much information :)
This exception only occure if the image is beeing saved, not if I'm waiting for the sequence to be captured.
Sure I could pack the UI Update code within a try-catch-block or check if the forms state is Disposed/Disposing. But for my little programming skills it looks like a structural problem.
Thank you for your help :)
The tricky part is that you're doing multi-threading without synchronization.
Invoke presents one such synchronization point, which is fine. However, as you've found out, it doesn't work after the handle is disposed - this makes perfect sense; Invoke simply sends a window message to the given handle, and when the handle no longer exists there's noöne to deal with the message (not to mention what would Close (which just sends WM_CLOSE etc.) do when the window no longer exists).
Solving this is quite tricky, actually. Even if you check if the form is disposed before trying the Invoke, it might still be disposed between the check and the Invoke itself. locking would allow you to handle the synchronization, but you'd need to lock in DestroyHandle or perhaps an event like Closing - the key is to make sure that whatever signal you send is safely tied to whether Invoke is safe right now. Of course, you don't really want to use Invoke - you need BeginInvoke, otherwise you're guaranteed to have a deadlock when Invoke needs to wait for the UI thread which is currently waiting for the lock to be released. Not good :)
Being prepared for an ObjectDisposedException might be the best solution. But looking through the reference source code, it doesn't look like it's 100% correct either - it's consistent on a single thread, but you're not calling Invoke on the UI thread (obviously).
i want to finish is that : user can dynamic create a tab page , and for each tab page , it will contain a corresponding thread do working .
what i do is :
1 : in main window 's "create tab page" button , add below code :
currentUserControl = new everyTabPage();
TabPage tp = new TabPage();
tp.Name = "a";
tp.Controls.Add(currentUserControl);
currentUserControl.Dock = DockStyle.Fill;
tabControl1.TabPages.Add(tp);
everyTabPage is a user control like this picture
2 : in this userControl 's "start" button , add
Thread tM = new Thread(new ParameterizedThreadStart(ThreadFunc));
tM.IsBackground = true;
tM.Start(comboBox1.Text);
and this is the thread function , for testing , i only change the label 's text.
public void ThreadFunc(object param)
{
string SessionParameter = (string)param;
while (true)
{
this.Invoke((MethodInvoker)delegate()
{
label1.Text = DateTime.Now.ToString() + #"_" + SessionParameter;
});
}
}
3 : after doing these , now program seems work as i wanted .
but when i testing it , i find if i create more than three tabs , program 's speed will become very very slow .
consider now i only change the label 's text , it already so slow , if i realy do some business work , the speed will not acceptable .
can anybody tell me why the speed is so slow , or if my solution is not effective ,thanks .
It is slow because although ThreadFunc runs on a separate thread that Invoke you perform does all the work from all your threads on the UI thread so you are flooding the UI thread with messages and it can not get any work done.
Does your program really need to update the UI in that tight of a loop? A better option may be to use a Timer and have the updates happen on the timer elapsed event instead.
A another quick fix instead of using a timer is put a sleep in the loop.
public void ThreadFunc(object param)
{
string SessionParameter = (string)param;
while (true)
{
this.Invoke((MethodInvoker)delegate()
{
label1.Text = DateTime.Now.ToString() + #"_" + SessionParameter;
});
Thread.Sleep(250);
}
}
Now your UI will update 4 times a second instead of 400,000 times a second.
Do you really Need the
while(true)
I think if you only want to set the Label text then you do not Need it.
while(true)
Needs a lot of CPU time e.g.
In the legacy code I'm working on, there are several file retrieval steps - first this set of data, then that set of data, which update a progress bar with a label displaying which portion of the process is currently active, and the progress bar itself, of course, updates its position. One portion of all this is hanging, though, and via the use of MessageBox.Show()s (I have to do it this way, can't step through it in the debugger), I've narrowed down where the hanging is occurring, but can't figure out why it's occurring.
Warning: the following code is unorthodox and may well warrant such warning signage as "Here be Dragons" or "This way lies madness." Proceed at your own risk/beware of the peril.
MessageBox.Show("Made it just before the pbDialog code");//<-- it hangs after this is displayed
using (pbDialog = new pbDialogs())
{
ProgressBar = new frmProgress( this, true);
ProgressBar.SetProgressLabelText("Vendor/Dept/Expense Data");
typeProgress = (int)ProgressStates.ProgressQRY;
ProgressBar.label1.Text += " (Receiving)";
if( pbDialog != null )
{
pbDialog.ShowDialog( ProgressBar, this );
}
else
{
ProgressBar.ShowDialog();
}
ProgressBar = null;
evt.Set();
}
MessageBox.Show("Made it just after the pbDialog code"); //This is not seen
pbDialog is declared in the same form as this code snippet:
public pbDialogs pbDialog;
pbDialogs is a class in another form (frmProgress.cs):
public class pbDialogs : IDisposable
ProgressBar is an instance of the anonymous class defined in frmProgress.cs (frmProgress, that is to say, which derives from System.Windows.Forms.Form)
public static frmProgress ProgressBar;
typeProgress is a locally defined int:
public static int typeProgress = 0;
evt is the name of the arg passed into the method from which this snippet originates:
private void FetchVendorDepartmentData(ManualResetEvent evt)
ManualResetEvent, as you may know, is a member of System.Threading
Does anybody see anything eyebrow-raising here (besides the general unorthodoxy of it all)?
UPDATE
I added more messages:
MessageBox.Show("Made it just before the pbDialog code");//<-- it hangs after this. TODO: Remove before deploying
using (pbDialog = new pbDialogs())
{
MessageBox.Show("Made it just before ProgressBar = new frmProgress");// TODO: Remove
ProgressBar = new frmProgress( this, true);
MessageBox.Show("Made it just after ProgressBar = new frmProgress");// TODO: Remove
ProgressBar.SetProgressLabelText("Vendor/Dept/Expense Data");
typeProgress = (int)ProgressStates.ProgressQRY;
MessageBox.Show("Made it just after assignment to typeProgress");// TODO: Remove
ProgressBar.label1.Text += " (Receiving)";
if( pbDialog != null )
{
MessageBox.Show("pbDialog was not null");// TODO: Remove
pbDialog.ShowDialog( ProgressBar, this );
}
else
{
MessageBox.Show("pbDialog was null");// TODO: Remove
ProgressBar.ShowDialog();
}
ProgressBar = null;
MessageBox.Show("ProgressBar set to null");// TODO: Remove
evt.Set();
MessageBox.Show("evt.Set called");// TODO: Remove
}
MessageBox.Show("Made it just after the pbDialog code");//TODO: Remove
}
...and the last one I see is, "pbDialog was not null"
UPDATE 2
In accord with the answer from "500 - Internal Server Error," I prepended a line to show the ProgressBar:
ProgressBar.ShowDialog();
if( pbDialog != null ) . . .
...but it makes no diff; in fact, with that I don't even make it as far as without it - I don't see the "pbDialog was not null" message.
Apologies in advance to Billy Blake, but: What the hammer? What the chain? What the anvil? What dread grasp is going on here?
UPDATE 3
So apparently either of these lines cause the hang:
ProgressBar.ShowDialog(); // with this, "pbDialog was not null" is not seen
pbDialog.ShowDialog( ProgressBar, this ); // if make it to here (line above commented out), "ProgressBar set to null" is not seen.
UPDATE 4
The problem may not be in this code after all, as I found another spot in the same class that uses the exact same code, and that portion of the data retrieval completes just fine...
You are specifying that you want ProgressBar as the modal owner of pbDialog here:
pbDialog.ShowDialog( ProgressBar, this );
but it doesn't look like you've actually shown ProgressBar (the owner) yet at this point.
To begin, I'm working on a pretty high level file system where I need to be able to (with very pin point accuracy) identify and process files in a timely manner. With that said, I am working on a system that will be using the FileSystemWatcher. The whole problem with the watcher though is the fact that it tends to have issues when throwing events when there are large files involved.
To remedy this I'm working on an abstract class that can handle the files individualy once they are created by the file system watcher.
The current road block that I am running into is that my out of process validation on the file is throwing an event, I'm just having problems catching it.
public abstract class absFile
{
public delegate void FileAvailable(object sender, FileEventArgs e);
public event FileAvailable OnFileAvailable;
public void isAvailable()
{
// Create a new threaded instance of the AvailableCheck private void
// This method will be run out of process to allow other operations to continue.
Thread toAvailableCheck = new Thread(new ThreadStart(AvailableCheck));
// Once the threaded object is created, start it.
toAvailableCheck.Start();
}
private void AvailableCheck()
{
// Declaring the file stream toThisFile to be used later with the File.Open
FileStream toThisFile;
// Declaring and instantiating the attempt counter for the loop
int tiAttemptNumber = 0;
// Declaring the event args for returning the events that are
// used by this object.
FileEventArgs toFileEventArgs = new FileEventArgs();
do {
try
{
// Attempt to open the file. If this fails the try
// will interrupt the processing and it will be caught.
toThisFile = File.Open(this.FilePath, FileMode.Open);
// If the file.open method does not fail, the isFileAvailable
// property will be set to true by updating the mbIsAvailable
// private boolean.
mbIsAvailable = true;
// populate the file event args to send back
// the number of attempts made at the file and the pause lenght
toFileEventArgs.Attempts = tiAttemptNumber;
toFileEventArgs.Pause = this.AttemptPause / 1000;
// This event is called when the file is complete.
// The client application will be able to handle this event.
OnFileAvailable(this, toFileEventArgs);
// close and dispose of the filestream.
toThisFile.Close();
toThisFile.Dispose();
}
catch (Exception toException)
{
// Since the open failed, add 1 to the counter so that
// it will eventually time out.
tiAttemptNumber++;
// Set the isFileAvailable property to false. This property
// will default as false, but as a part of standard, make sure that
// if the open fails that the flag IS set to false by updating the
// mbIsAvailable private boolean.
mbIsAvailable = false;
// Put the thread to sleep for the ammount of time specified
// by the AttemptPause. This will give the file time to finish
// whatever process it is involved in.
Thread.Sleep(this.AttemptPause);
}
// Continue to make attempts until either the file is marked as available
// or the number of current attempts is the same as or greater than the
// AccessAttempts property.
} while (!this.isFileAvailable && this.AccessAttempts > tiAttemptNumber);
}
this is the code that I am running as you can see in the private void AvailableCheck, OnfileAvailable is the delegate called passing back this and the file event args.
now i have inherited this abstract class, and i need to be able to catch that event.
toWatcher.Created += new FileSystemEventHandler(OnCreated);
is called in the main and farther down the code is the following method
private void OnCreated(object source, FileSystemEventArgs e)
{
lstStatus.Invoke(new MethodInvoker(delegate {
lstStatus.Items.Add(DateTime.Now.ToString("g") + " - " + e.Name + " - File Created Event Detected for: " + e.FullPath);
lstStatus.TopIndex = lstStatus.Items.Count - 1;
tgtFile ThisFile = new tgtFile(e.FullPath);
lstStatus.Items.Add(DateTime.Now.ToString("g") + " - " + e.Name + " - Creating tgtFile Object");
}));
}
The instatiation of the tgtFile object is passed the path which makes its way down the pike to the is available method.
as you can see the chance exists for the OnFileAvailable event to be fired from the tgtFile object.
Also as you can see, the the possibility for multiple tgtFile objects to exist in memory at the same time is there as well based on the threading design of the filesystemwatcher.
in my main application then i want to be able to do something like:
public tgtFile ThisFile;
ThisFile.OnFileAvailable += new EventHandler(OnFileAvailable);
but the EventHandler errors out, and that is where I am stuck.
If it is giving you a compiler error, it's probably because your "OnFileAvailable" method referenced in this line (from the bottom of your post):
ThisFile.OnFileAvailable += new EventHandler(OnFileAvailable);
is not expecting an EventHandler - it's expecting a FileAvailable delegate. Change it to:
ThisFile.OnFileAvailable += new absFile.FileAvailable(OnFileAvailable);
//note that this can also just be ThisFile.OnFileAvailable += OnFileAvailable;
and make sure OnFileAvailable looks like this:
public void OnFileAvailable(object sender, FileEventArgs e)
{
//...
}
First things first... Ensure the event is subscribed, before calling it. Wrap the event call in an IF statement:
if (OnFileAvailable != null)
OnFileAvailable(this, toFileEventArgs);