|
My problem is that the BackgroundWorker DoWork event is never even raised.
I have:
LoadCubeInBackground = New BackgroundWorker();
LoadCubeInBackground.WorkerReportsProgress = True;
LoadCubeInBackground.WorkerSupportsCancellation = True;
LoadCubeInBackground.DoWork += new DoWorkEventHandler(LoadCube);
LoadCubeInBackground.ProgressChanged += new ProgressChangedEventHandler(LoadCubeInBackground_ProgressChanged);
LoadCubeInBackground.RunWorkerAsync(_connectionString);
But, the first line of my LoadCube function never gets hit.
Right now...because converting it to the Threading.Thread model just doesn't seem like it's going to be easy, I'm looking at just starting the form as a process and having it output a string when it finishes. The whole point of the form is to end up with MDX queries, so I can just join each query into a single string, and then split it in the other program...
There are a few arguments to pass in, but they're all strings, so I can handle it with Environment.GetCommandLineArgs.
|
|
|
|
|
1. is the code shown running on the main thread? BGW does NOT behave the way one expects when created on another thread!
2. what is inside DoWork(), could you show the first few lines?
3. "the first line of my LoadCube function never gets hit." how do you observe that?
4. Environment.GetCommandLineArgs. Beware command line length limitations (could be 2K).
|
|
|
|
|
Luc Pattyn wrote: 1. is the code shown running on the main thread? BGW does NOT behave the way one expects when created on another thread!
it's run from the Form_Load event...so the main thread of the Form...though I don't know if the program that opens the form has it on its main thread or not...
Luc Pattyn wrote: 2. what is inside DoWork(), could you show the first few lines?
ADODB.Connection oCn = New ADODB.Connection();
ADOMD.Catalog oCat = New ADOMD.Catalog();
LoadCubeInBackground.ReportProgress(0, "Opening Data Cube");
ConnectionString myCStringType = e.Argument;
Luc Pattyn wrote: 3. "the first line of my LoadCube function never gets hit." how do you observe that?
I can debug with VS through the program...by that I mean, it opens the program and I can still use breakpoints and such when running my forms. This form is actually called from another form that is called from a button on the main program. So, I set a breakpoint first in the Form_Load and I set a breakpoint at the first ReportProgress in the code shown above. I can step through and see that the BackgroundWorker is created, that the event handlers are added and when I run (F5) at RunWorkerAsync...the breakpoint in the DoWork event handler never gets hit.
Luc Pattyn wrote: 4. Environment.GetCommandLineArgs. Beware command line length limitations (could be 2K).
That's a fair point...I have to pass in the sites that I'm using and there could possibly be hundreds of them...and the name's can be anywhere from 28 characters to 50+. So, I could easily reach that limit. I may just have to pass in a file name with all of the information if I can't get the better way (no command line, no starting process) to work.
I'm going to have to have an install program anyway, because it has to register the toolbar that runs all of this, so I can also install the form's exe...
I really want it to work the way I originally intended...just add the project to the original solution and add it as a new form and let everyone play well together...
|
|
|
|
|
Here is an idea: for ReportProgress to work, the main thread must be idle (the BGW invokes it, forcing it to run on the main thread). Now suppose you have some event handler that is working indefinitely, what it should not do.
Maybe the button handler itself that started it all has not finished yet. You don't have any of the following there, do you (the list is not exhaustive):
- an infinite loop
- Thread.Join
- Thread.Sleep
- network operations
- database operations
- access to some COM object that is already active on another thread while in an STA situation
Experiment: grab your title bar and move the window around. or cover part of your form with another app's window, then uncover it so it repaints immediately.
|
|
|
|
|
The form repaints fine. The whole idea was that I would load the database in the background and send the information to the UI when it was retrieved.
So, the form has a button that adds an item to a listview and then begins to load the other controls. While the controls are waiting for the info to be returned, I have a simple animated gif that displays over each until the information is returned. Right now, the form opens (which is when the bw is supposed to start). I can then click the add button...it adds an item to the listview, enables the rest of the form, and displays the animated gifs. All of it paints fine, and I can move the form around.
Here's the basic progression...
When the program loads, it checks for all registered com items.
It finds my "DataCubeUtilityToolbar" and creates it...which adds a toolbar, along with several buttons and other items.
The button that starts all of this is clicked, and it makes a call to a void within a module that creates a new "SiteSelectionWizardForm" (which is shown modally).
The user goes through the wizard to select the sites, and when finished, hit "Submit", which creates a new "ConnectionForm" (the form in question). That form is shown modally as well.
So, are you suggesting that because I showed them modally, the main program's thread isn't going to start another call until the original button call is completed? Because, I could probably show them as modeless forms...I may have to disable some stuff until the form raises an event...but if that works, I'll do it...
modified on Tuesday, April 13, 2010 6:39 PM
|
|
|
|
|
seems like you are saying somewhere is a button handler that has a form.ShowDialog() inside; this will block its thread as long as the modal dialog is open, and prevent a BGW to report progress (i.e. the progress reports will get queued, and the BGW will continue its job, but the progress reports will not dequeue until the modal form closes).
possible solutions:
- don't use modal dialogs;
- don't report progress while a modal dialog is showing;
- maybe there is some hack that makes it work, I don't know as I never got in that situation.
I suggest you perform a little experiment to confirm, e.g. temporarily replace ShowDialog() by Show().
|
|
|
|
|
I actually did try it...I made all of the forms modeless and the DoWork event still never fired. I was able to step through and see that the original program had exited the button click handler and was checking to see if items should be enabled or not, which tells me that it was allowing calls to elsewhere. The form that I wanted was still displayed, still functioning...except the backgroundworker.
I'm just going to go ahead with using Process.Start and passing in a file with the details in it...
Thanks for the help.
|
|
|
|
|
I understand.
If you ever figure it out, please let us know.
Good luck.
|
|
|
|
|
I figure it's going to have something to do with the initial program that calls it. Something in that program is screwing it up in some major ways.
I ended up trying to run it as an external process. And, it starts to work. It starts the form up, starts loading the database in the background. The events from the loading are firing correctly...and then for no apparent reason, it just stops loading the database. The UI is still functional and still works, except for the parts relying on the parts of the database that were not loaded.
I made a few changes to the code as far as what was output to the StatusLabel so that I could track the progress and it almost seems like a memory issue...as in, the program that called the process set some limits on the memory available to the program. I have 2GB of RAM and the program gets to about 34 MB and then quits the loading. Running the exact same program from a test app runs it fine. The only difference is the program that called it.
I ended up forgoing the whole thing and just loading the database and serializing the object that I created. Doing that and deserializing the object works fine. It just means that I have to implement a user option for updating the object.
The program running it just seems to screw up anything I write for it to do. It's quite frustrating. Anyway, the newest update is supposed to support multi-threading better so maybe with the newer version it will work the way that I want. Until then, a redesign of the process was the only solution.
Thanks for the help.
|
|
|
|
|
It still sounds very mysterious. 34MB isn't really that big, I would suspect something else is going wrong. Could it be you are getting a time-out, maybe on the database connection? Maybe not being the foreground process makes it get less of the CPU cycles (assuming some other process also wants them).
Anyway, thanks for the update, and good luck with it.
|
|
|
|
|
oh...and I appreciate the insights...
|
|
|
|
|
I am having an issue with event 1000 showing up when I start a service I created. It drops in a matter of seconds after starting, and completely ignores exception handling within the app as it does so.
It does not exhibit this behavior on Server 2003, only on 2008. Both are 64-bit implementations.
Any advice would be welcomed. Also, is there a good reference for looking up the exception code listed below? Thank you.
Faulting module name: KERNELBASE.dll, version: 6.1.7600.16385, time stamp: 0x4a5bdfe0
Exception code: 0xebf00baa
Fault offset: 0x000000000000aa7d
Faulting process id: 0xc4c
Faulting application start time: 0x01cadb380757df32
|
|
|
|
|
|
|
Actually, yes. As I recall, it was because I did not have .NET 3.5 enabled on the server, only 2.0. (Unless I am confusing it with a similar issue.)
|
|
|
|
|
Hi,
I'm getting 'Only one usage of each socket address (protocol/network address/port) is normally permitted' exception during invocation of remote object method. I know what causes this problem but I don't know why .
My app registers 2 TcpChannel, ports 9001 and 9002. Ports are used for different remote objects. In such configuration this exception occurs when I'm trying to invoke method of the second object first time. This issue disappears if I use only one TcpChannel with any port number and both remote objects use it.
Thx.
Clarification.
Both remote objects are client and service in the same time i.e. registered as a WellKnownClientType and WellKnownServiceType. Two computers run the same application and talk to each other via remoting. Registration of channels are done only when objects registered as a Service.
modified on Tuesday, April 13, 2010 4:41 PM
|
|
|
|
|
I don't know if this is related but I once had an issue where I was trying to open two TcpChannels and I couldn't because they didn't have unique names. I ended up having to set the name property on the channel like so:
TcpChannel channel;
...
Hashtable properties = new Hashtable();
...
properties.Add("name", "UniqueStringHere");
channel = new TcpChannel(properties, clientProvider, serverProvider);
ChannelServices.RegisterChannel(channel, false);
Good luck!
James
|
|
|
|
|
Does anyone know how to not show weekend dates in NPLOT charts. In Zedgraph one would use something like myPane.XAxis.Type = AxisType.DateAsOrdinal;
costPS.Clear();
CandlePlot cp = new CandlePlot();
cp.DataSource = dt;
cp.AbscissaData = "Date";
cp.OpenData = "Open";
cp.LowData = "Low";
cp.HighData = "High";
cp.CloseData = "Close";
cp.BearishColor = Color.Red;
cp.BullishColor = Color.Green;
cp.Style = CandlePlot.Styles.Filled;
costPS.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
costPS.Add(new Grid());
costPS.Add(cp);
costPS.Title = symbol;
costPS.YAxis1.Label = "Price [$]";
costPS.YAxis1.LabelOffset = 40;
costPS.YAxis1.LabelOffsetAbsolute = true;
costPS.XAxis1.HideTickText = true;
costPS.Padding = 5;
costPS.AddInteraction(new NPlot.Windows.PlotSurface2D.Interactions.HorizontalDrag());
costPS.AddInteraction(new NPlot.Windows.PlotSurface2D.Interactions.VerticalDrag());
costPS.AddInteraction(new NPlot.Windows.PlotSurface2D.Interactions.AxisDrag(false));
costPS.InteractionOccured += new NPlot.Windows.PlotSurface2D.InteractionHandler(costPS_InteractionOccured);
costPS.AddAxesConstraint(new AxesConstraint.AxisPosition(PlotSurface2D.YAxisPosition.Left, 60));
costPS.Refresh();
Hopefully someone has experience with NPlot and can help on the issue. Thanks for any help!
|
|
|
|
|
Part of my application uploads files to an FTP server and reports progress. Currently, the progress is simply calculated by number of files remaining vs. files total in order to generate a percentage and update a progress bar. I want to detail out a little bit more and have a file upload progress bar as well as an overall progress bar.
The entire operation is on a background worker, upload is a function within the process, not on it's own thread. Do I need to employ another background worker in order to update the file progress bar (Which would update at byte chunk intervals) while the main background worker updates the overall progress bar (Which updates on a per file basis)?
Would it be better to calculate the total bytes of all files and then update both progress bars at the same time with the percentage of progress on the overall bar being bytes sent vs. total bytes while the file bar being file bytes sent vs. total file bytes?
I think I answered my own question there. But I would like to know what others think.
The other question is this: I am storing the names of the files to be uploaded in a file queue DB table and accessing it with a data reader. In order to calculate the total size of all files do you think it would be better to store the file size of those files in the table at the time they are inserted and run a simple SUM() operation when I get the data in the first place? Or perhaps write a routine that loops the datareader once and calculates the total file(s) size with an IO operation?
Thank you very much, --EA
|
|
|
|
|
Hi EA,
1.
BackgroundWorker.ReportProgress() takes either an int, or an int and an object of your choice.
So you could pass two ints to the ProgressChanged event, where the second one would get boxed.
Or you could create a class or struct holding whatever you want to pass, and just pass that.
Or you could use one or more class members.
The only thing you must keep in mind is two threads are active here: the BGW making progress, and the GUI thread displaying progress; so you must avoid inconsistent progress data; not by using locks, but in some other way, e.g. by using value types.
An alternative would be this:
pass the progress within a file as the int, and pass the filename as the object;
let the display routine figure out the overall progress, which can be organized to require only a very simple addition and division.
2.
if the file sizes don't change (much), I would consider storing them in the table; getting them from the file system may be too expensive, depends on how many and how far off (network drive, FTP site, ...).
3.
Warning: don't update progress for every tiny amount of data handled, since that might end up slowing it all down. Updating more than say 20 times per second really makes no sense, the human eye won't follow.
Occasionally I update progress by polling rather than pushing it out: just have a timer (a System.Windows.Forms.Timer is the best fit) cause the display to be updated periodically.
|
|
|
|
|
Thank you Luc, you are a well of knowledge. Do you think there is value to creating a nested class vs. an on the fly array?
Example:
protected class myHandler
{
public int fileProgress;
public int overallProgress;
public object Clone();
}
bgWorker.reportProgress(0,myHandler.Clone());
vs.
Int32[] myProgress = new Int32[];
bgWorker.reportProgress(0, myProgress);
I had some ongoing errors with the protected handler class, so right now I am using the on the fly Int array, but if it is just plain wrong...
|
|
|
|
|
If you only have two progress bars, one overall, one for current file, all you need to pass is two percentages. So a simple struct, or an int[2] array would do. However, I would be inclined to make it even simpler:
foreach (string filename in filenames) {
ReportProgress(101, filename);
... some kind of loop {
...
ReportProgress(percentage, filename);
}
ReportProgress(102, filename);
}
and let the ProgressChanged event do the math:
long totalDone;
long overallTotal;
Directory<string filename, long filesize> namesToSizes;
void ProgressChanged(object sender, ProgressChangedEventArgs e) {
int p=e.ProgressPercentage;
string filename=e.UserState;
long filesize=namesToSizes[filename];
if (p==101) {
labelIndividual.Text="now dealing with file "+filename;
} else if (p==102) {
totalDone+=filesize;
} else {
ProgressBarIndividual.Value=p;
ProgressBarTotal.Value=(totalDone+filesize*p/100)*100/overallTotal;
}
}
You have to set up overallTotal and namesToSizes before entering the loop.
Note: While the documentation calls the first parameter "percentProgress", it is not limited to the range [0, 100] so I used two values outside that range to request special actions.
|
|
|
|
|
That is some great information all the way around. Mission accomplished, thanks Luc!
--EA
|
|
|
|
|
Hi all,
I have the following code that I need to return dblRx our of DisplayText in order to use it in another function. Problem I'm having is that if I try to return a double from DisplayText (private double DisplayText), I get the EventHandler error from this.Invoke that I have the wrong data type. Any help would be appreciated.
private void DisplayText(object sender, EventArgs e)
{
RxString = RxString.Remove(0, 3);
double dblRx = double.Parse (RxString);
RxString = dblRx.ToString();
textBox1.Text = RxString;
}
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
RxString = "";
try
{
RxString = serialPort1.ReadTo("\r");
serialPort1.WriteLine("*X01\r");
}
catch (Exception Exception)
{
return;
}
this.Invoke(new EventHandler(DisplayText));
}
|
|
|
|
|
Not sure I understand your question very well, however here are some remarks:
1.
mprice214 wrote: RxString.Remove(0, 3); //This removes the first 4 characters
Not true. At best it removes 3 characters; and if there are fewer, expect an exception.
2.
mprice214 wrote: double.Parse (RxString);
that will fail if the data isn't really a string representation of a double. Don't do this on external data, it will bite you. Either add try-catch or better yet use TryParse().
3.
why does DisplayText() have parameters which make it look like a real event handler, you are not using those parameters, instead you are using some class member. It would seem logical to give it one parameter: the text it should display.
4.
mprice214 wrote: serialPort1.WriteLine("*X01\r");
very strange. You want a carriage return plus whatever the system uses for a newline (maybe "\r\n"). Either you like what the system gives you, or you don't; I would never mix the two.
5.
mprice214 wrote: catch (Exception Exception)
{
return;
}
This is not acceptable; whatever goes wrong, you ignore it. So when it goes wrong, you will have an impossible job to diagnose and fix the problem. And believe me, serial communication always goes wrong sooner or later. You should NOT swallow exceptions, either catch a very specific one and add a comment as to why you really don't care about it, or log it somewhere so it leaves a trail.
6.
mprice214 wrote: this.Invoke(new EventHandler(DisplayText));
That does not work, as the parameter list does not match. First make sure which parameters the method should have, then make sure to provide them, using an overload of Invoke().
|
|
|
|
|