System.InvalidCastException when using Webbrowser and Background worker class C# - c#

public void bgw_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < idscount; i++)
{
if (bgw.CancellationPending == true)
{
e.Cancel = true;
break;
}
else
{
bgw.ReportProgress(i + 1);
webBrowser1.Document.GetElementById("txtIDCardNo").SetAttribute("Value", "\"" + value + "\"");
webBrowser1.Document.GetElementById("btnIDCard").InvokeMember("click");
webBrowser1.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(WaitElement);
System.Threading.Thread.Sleep(200);
}
}
}
I end up in "An exception of type 'System.InvalidCastException' occurred in System.Windows.Forms.dll but was not handled in user code". I am not able to understand, If I run same code without background worker class it works but inside background worker class it doesnt. Please help me out.

Related

How do I bypass code in the RunWorkerCompleted event if an error occurs?

I wrote a program that creates an XML file from the contents of an Excel spreadsheet. The program works and I am now making it more robust by adding error checking. For example, if the Excel spreadsheet does not contain a required XML tag, the program displays an error and returns from the main program. When an error occurs, the RunWorkerCompleted() event fires, and code executes that shouldn't execute because an error occurred. My question is how to determine if an error occurred so I can bypass certain code in the RunWorkerCompleted() event.
I tried calling bgw.CancelAsync() in the main program where the error is detected, but RunWorkerCompletedEventArgs e.Cancelled is false so it doesn't work.
Here are some excerpts from my code. The progress bar is started in the click event of a button. It works, but I had to declare cellsProcessed, rowCount, and colCount as global because I couldn't figure out how to pass them to bgw_DoWork().
private void button_create_Click(object sender, EventArgs e)
{
// Define the event that fires when the progress bar finishes
bgw.RunWorkerCompleted += bgw_Complete;
rowCount = xmlRange.Rows.Count; // Declared as global
colCount = xmlRange.Columns.Count; // Declared as global
// Start the progress bar thread
if (!bgw.IsBusy)
{
bgw.RunWorkerAsync();
}
// Read the header in row 1 and return an error if it isn't valid
for (colIdx = 1; colIdx <= colCount; colIdx++)
{
if ((xmlRange.Cells[1, colIdx] != null) && (xmlRange.Cells[1, colIdx].Value2 != null))
{
cellContents = xmlRange.Cells[1, colIdx].Value2.ToString();
switch (colIdx)
{
case 1:
if (cellContents != "Tag")
{
error = true;
errText = "Cell A1 must contain 'Tag'";
}
break;
case 2:
if (cellContents != "Type")
{
error = true;
errText = "Cell B1 must contain 'Type'";
}
break;
if (error)
{
bgw.CancelAsync();
MessageBox.Show(errText, "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
// Close Excel resources
xmlWorkbook.Close();
xmlSpreadsheet.Quit();
Marshal.ReleaseComObject(xmlRange);
Marshal.ReleaseComObject(xmlWorksheet);
Marshal.ReleaseComObject(xmlWorkbook);
Marshal.ReleaseComObject(xmlSpreadsheet);
GC.Collect();
return;
}
private void bgw_DoWork(object sender, DoWorkEventArgs e)
{
if (bgw.CancellationPending)
{
e.Cancel = true;
}
else
{
label_pctComplete.Text = "Completed " + progressBar.Value.ToString() + "%";
bgw.ReportProgress((100 * ++cellsProcessed) / (rowCount * colCount));
}
}
private void bgw_Complete(object sender, RunWorkerCompletedEventArgs e)
{
if (!e.Cancelled)
{
label_pctComplete.Visible = false;
progressBar.Visible = false;
// Inform the user that the XML file was created
label_created.Visible = true;
label_created.Text = "Done!...Created " + textBox_outputFile.Text;
// Enable the button so the user can view the XML Limit File
button_openLimitFile.Enabled = true;
}
}
private void bgwProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar.Value = e.ProgressPercentage;
}
Thank you for any help.

Return from BackgroundWorker.DoWork throws TargetInvocationException

I am unable to get the reason for this Exception:
private void bwWorker_DoWork(object sender, DoWorkEventArgs e)
{
if (Main.bolDebugMode)
MessageBox.Show("Function DoWork is called");
if (Main.ftpsync(Main.strUsername407, Main.strPassword407, sender as BackgroundWorker) == 0)
e.Result = e.Result + "No error in " + Main.strUsername407;
else
{
if (Main.bolDebugMode)
MessageBox.Show("Errors in " + Main.strUsername407);
e.Cancel = true;
e.Result = e.Result + "Errors in " + Main.strUsername407;
if (Main.bolDebugMode)
MessageBox.Show("Errors marked");
try
{
MessageBox.Show("Next step throws exception");
return;
}
catch (Exception error)
{
if (error.ToString() != null)
MessageBox.Show(error.InnerException.Message);
}
}
}
It throws this exception:An unhandled exception of type 'System.Reflection.TargetInvocationException' occurred in mscorlib.dll
Additional information: Exception has been thrown by the target of an invocation.
The target (to my limited understanding) is the backgroundworker's RunWorkerCompleted function:
private void bwWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (Main.bolDebugMode)
MessageBox.Show("DoWork Completed. Break: " + e.Cancelled + " Error: " + e.Error + " Result: " + e.Result);
// First, handle the case where an exception was thrown.
if (e.Error != null)
{
lStatus.Text = e.Error.Message;
}
else if (e.Cancelled)
lStatus.Text = "Cancelled: " + e.Result.ToString();
}
else
{
lStatus.Text = "Done! " + e.Result;
Thread.Sleep(Convert.ToInt16(Main.strGlobalWaitTime));
pbProgress.Value = 0;
lStatus.Text = "";
}
if (Main.bolDebugMode)
MessageBox.Show("Analysis completed");
// Enable the Start button.
btnStart.Enabled = true;
// Disable the Cancel button.
btnCancel.Enabled = false;
}
public class Main
{
#region Variables
// Variables - FTP Settings
// Reading tons of variables from a appconfig file like so:
private static string StrGlobalWaitTime = ConfigurationManager.AppSettings["program_globalWaitTime"];
private static bool BolDeleteRemoteFiles = Convert.ToBoolean(ConfigurationManager.AppSettings["program_deleteremotefiles"]);
// Configuring the variables to receive and write
public static string strGlobalWaitTime
{
get { return StrGlobalWaitTime; }
set { StrGlobalWaitTime = value; }
}
#endregion
#region Main function
public static int ftpsync(string strUsername, string strPassword, BackgroundWorker bwWorker)
{
if (Directory.EnumerateFiles(strWorkDirectory, "*.pdf").Any())
{
bwWorker.ReportProgress(0, "Files left! Upload not complete");
Thread.Sleep(Convert.ToInt16(Main.strGlobalWaitTime));
return 1;
}
However, it doesn't even reach the first debugging message box. Thus it must be happening between the return and the beginning of the function. Is he background worker not handing over directly to the RunWorkerCompleted function? Can anyone tell me what I am missing or doing wrong?
This is my first question. I will try to provide as much information as possible. Google searches for the most obvious queries have been done.
The thing to look for whenever you encounter a TargetInvocationException is the InnerException property, which will have the "real" exception.
From the look of it, it will most likely have something to do with trying to access the GUI from inside the worker's thread. Remember that when using the DoWork handler, the code executes in a different thread from the main UI's. Therefore, calls to GUI components must be either done via Invoke calls or avoided all together.

Background Worker is called twice

I have a problem with background worker, it gets called twice thus, increasing the time of execution for my long routine, I created background worker manually so, there is no chance for the DoWork to be initialized within the initializeComponent() method, any help is appreciated.
here is my code:
// constructor
public TeacherScheduleForm(Therapist therapist)
{
this.therapist = therapist;
InitializeComponent();
bw = new BackgroundWorker();
bw.WorkerSupportsCancellation = true;
bw.WorkerReportsProgress = true;
bw.DoWork += bw_DoWork;
bw.ProgressChanged += bw_ProgressChanged;
bw.RunWorkerCompleted += bw_RunWorkerCompleted;
load = new LoadingForm();
}
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
load.AppendProgress(e.ProgressPercentage);
// load.AppendText(e.ProgressPercentage.ToString() + "%");
Console.Write("Progress: " + e.ProgressPercentage);
// MessageBox.Show("Progress : " + e.ProgressPercentage);
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if ((e.Cancelled == true))
{
MessageBox.Show("Cancelled");
}
else if (!(e.Error == null))
{
MessageBox.Show("Error : " + e.Error);
}
else
{
updateUI();
load.Close();
Console.Write( "Done!");
}
}
// do work of background worker
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
for (int i = 1; (i <= 2); i++)
{
if ((worker.CancellationPending == true))
{
e.Cancel = true;
break;
}
else
{
// Perform a time consuming operation and report progress.
Console.Write("Before Doing work");
setup(therapist.therapistID + "", schoolYear);// the time consuming operation
Console.Write("Doing work");
//System.Threading.Thread.Sleep(100);
worker.ReportProgress((i*5));
}
}
}
The background worker is called when the user selects the school year through a combo box which is in this code below:
private void comboBoxSchoolYear_SelectedIndexChanged(object sender, EventArgs e)
{
//load = new LoadingForm();
schoolYear = int.Parse(comboBoxSchoolYear.SelectedValue + "");
try{
if (!bw.IsBusy)
{
bw.RunWorkerAsync();
load.ShowDialog();
}
else
{
bw.CancelAsync();
}
}
catch(Exception ex)
{
Console.Write("Error : " + ex.Message);
}
}
You are loading the form after creating the event-handler. Thats the only point I can think off doing the trouble. Try to load the form first and then create the handler.
Reason: At InitializeComponent(); the IndexChanged normally will fire up because the control is set at this point with its index. I havnt noticed this behaviour on FormLoad till now. But as I cant see any other problem in here its worth a try.
IF this doesnt solves it, you should also take care if TeacherScheduleForm is being called twice.
Something handy for debugging-purposes:
MessageBox.Show((new StackTrace().GetFrame(0).GetMethod().Name));
Paste this into your event/method or whatever. It will popup a messagebox with the method-name which called your current method. In this case (from comments) it would've raised 2 messageBoxes saying TeacherScheduleForm for both.
I've saved this to my code-snippets.

Cross thread operation not valid when use backgroundworker in c#

private bool ImportData()
{
bool result = false;
try
{
intdevid = int.Parse(cmbDeviceName.SelectedValue.ToString());
FetchDevicedata(intdevid);
//FTPTCompletedBatchTransfer();
FetchMaxReportId();
GetFTPFile(strDeviceIP, strDeviceUsername, strDevicePwd, strDevicePath + "//RunningBatch//RunningBatch.db", "RunningBatch.db"); // Copy RunningBatch.db to Debug Folder from Remote
LoadRunningData(); // Get Running Data in dataset from running.db
if (DecodeBatchData_R() == false)
{
MessageBox.Show("Running Batch Data Not Found");
}// save in batch master and row data table
GetFTPFile(strDeviceIP, strDeviceUsername, strDevicePwd, strDevicePath + "//CompletedBatch//CompletedBatch.db", "CompletedBatch.db");
LoadCompletedData();
if (DecodeBatchData() == false)
{
MessageBox.Show("Completed Batch Data not found");
}
result = true;
}
catch (Exception ex)\\here error:Cross-thread operation not valid: Control 'cmbDeviceName' accessed from a thread other than the thread it was created on.
{
clsLogs.LogError("Error: " + ex.Message + this.Name + " || ImportData");
result = false;
}
return result;
}
private void btnimport_Click(object sender, EventArgs e)
{
//////////////////copy checkweigher .db to database folder
dsCheckRptId = new DataSet();
///////////////////////////////////////////////////////////
if (cmbDeviceName.Text.ToString().Trim() == "--Select--")
{
MessageBox.Show("Please Select Proper Device");
cmbDeviceName.Focus();
return;
}
var deviceId = (int)cmbDeviceName.SelectedValue;
bgw.RunWorkerAsync(deviceId);
progressBar1.Visible = true;
label2.Visible = true;
}
void bgw_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 1; i <= 100; i++)
{
var deviceId = (int)e.Argument;
e.Result = ImportData();
System.Threading.Thread.Sleep(100);
bgw.ReportProgress(i);
}
}
void bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
label2.Text = String.Format("Progress: {0} %", e.ProgressPercentage);
}
void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
var result = (bool)e.Result;
if (cmbDeviceName.SelectedValue != null && cmbDeviceName.SelectedValue.ToString().Trim() != "0" && cmbDeviceName.SelectedValue.ToString().Trim() != "System.Data.DataRowView" && cmbDeviceName.SelectedValue.ToString().Trim() != "")
if (result)
{
MessageBox.Show("Data Import Completed Successfully for " + strDevicename);
clsLogs.LogEvent(3, "Data Import Completed Successfully for " + strDevicename);
}
else
{
MessageBox.Show("Data Import Fail For " + strDevicename);
clsLogs.LogEvent(3, "Data Import Fail for " + strDevicename);
}
progressBar1.Visible = false;
label2.Visible = false;
}
;When I run this background worker coding, there's an error stating "Cross-thread operation not valid: Control 'cmbDeviceName' accessed from a thread other than the thread it was created on. ."
How do I solve this problem guys?
WinForms controls are not thread safe, thus cross-thread operations on controls are not valid. You can access controls only from thread which created those controls. In your code you are accessing cmbDeviceName combobox from background thread. Best option to solve this is passing intdevid as RunWorkerAsync argument:
// executed on main thread
var deviceId = (int)cmbDeviceName.SelectedValue;
backgroundWorker.RunWorkerAsync(deviceId);
And get this argument in your DoWork handler:
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
// executed on background thread
var deviceId = (int)e.Argument;
// ...
}
Suggested reading: Safe, Simple Multithreading in Windows Forms

TargetInvocationException was unhandled at the end of DoWork Backgroundworker Method

Here's DoWork:
private void uploadWorker_DoWork(object sender, DoWorkEventArgs e)
{
uploadWorker.ReportProgress(20);
int tiffError = 0;
finalFiles = Directory.GetFiles(AppVars.FinalPolicyImagesFolder);
foreach (string file in finalFiles)
{
if (!file.EndsWith(".tiff"))
{
tiffError = 1;
break;
}
}
uploadWorker.ReportProgress(50);
if (tiffError == 1)
{
MessageBox.Show("There are files in this folder that are not .tiff. Please ensure only .tiff files are in this folder.", "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else
{
if (finalFiles.Length == 0)
{
MessageBox.Show("There are no TIFF files to be uploaded. Please generate files first.", "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
pbUpload.Value = 0;
EnableAllButtons();
}
else
{
double count = finalFiles.Length;
int current = 0;
int pbValue = 0;
uploadWorker.ReportProgress(70);
foreach (string file in finalFiles)
{
current = current + 2;
if (file.Contains(".tiff") == true)
{
PolicyNumber = Path.GetFileName(file).Split('_')[0];
basePolicyNumber = PolicyNumber.Remove(PolicyNumber.Length - 2);
basePolicyNumber = basePolicyNumber + "00";
finalPolicyName = Path.GetFileName(file);
PolicyUUID = Transporter.GetPolicyUUID(AppVars.pxCentralRootURL, basePolicyNumber);
if (PolicyUUID == "")
{
MessageBox.Show("The InsightPolicyID for the policy you are trying to upload does not exist in ixLibrary. Please ensure the policy number is correct. If you are sure it should be in ixLibray, please contact IT.", "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else
{
ixLibrarySourceFileURL = AppVars.ixLibraryPolicyAttachmentsURL + finalPolicyName;
UploadToixLibraryErrorCode = Transporter.UploadFileToixLibrary(AppVars.ixLibraryPolicyAttachmentsURL, file);
if (UploadToixLibraryErrorCode != 0)
{
MessageBox.Show("There was an error uploading the file to ixLibrary. Please contact IT about this problem.", "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else
{
GeneratePayLoadErrorCode = Transformer.GeneratePayLoad(ixLibrarySourceFileURL, finalPolicyName);
if (GeneratePayLoadErrorCode != 0)
{
MessageBox.Show("There was an error generating the XML for pxCentral. Please contact IT about this problem.", "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else
{
pxCentralPOSTErrorCode = Transporter.pxCentralPOST(AppVars.pxCentralRootURL + PolicyUUID, AppVars.pxCentralXMLPayloadFilePath);
pbValue = Convert.ToInt32(((current / count) * 30) + 70);
uploadWorker.ReportProgress(pbValue);
}
}
}
}
}
}
}
}
As soon as it hits the last }, I get the TargetInvocationException was unhandled error here (see comment in code):
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
bool createdNew = false;
Mutex mutex = new Mutex(true, "MyApplicationMutex", out createdNew);
if (createdNew == true)
{
//error happens here
Application.Run(new frmMain());
}
else
{
MessageBox.Show("The application is already running.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
}
}
I'm not sure why this started happening all of the sudden. Does anyone know why?
Finally, here's RunWorkerCompleted:
private void uploadWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error == null)
{
DeleteFinalFiles(finalFiles);
MessageBox.Show("Upload process complete.", "Complete!", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
EnableAllButtons();
}
You are calling EnableAllButtons in your DoWork handler. this, presumably, changes the Enabled state of buttons on the form. that is not legal from any other thread than the UI thread. You should make the call to EnableAllButtons in your ProgressChanged event handler or in your RunWorkerCompleted event handler. You're also calling ProgressBar.Value in the DoWork with the code pbUpload.Value = 0.
Also, you should call MessageBox.Show from your UI thread (i.e. in ProgressChanged or RunworkerCompleted handler) so that the MessageBox can be associated with your forms message pump propertly. You should pass a form object to MessageBox.Show to associate the message box with the form so you can't bring the form to the foreground while the message box is shown. e.g.:
MessageBox.Show(this,
"There are files in this folder that are not .tiff. Please ensure only .tiff files are in this folder.",
"Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
In WinForms, you must only access a control on the thread that created the control. Your DoWork event handler is not running on the thread that created the form (which, of course, is the point). Therefore, you must not access any of the controls on the form in the DoWork handler. Doing so can create unpredictable results.
I had exactly the same problem with TargetInvocation Exception raised after the completion of the background process. Inside the backgroundWorker ProgressChanges Event I have references to controls as shown below`private void m_oWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// This function fires on the UI thread so it's safe to edit
// the UI control directly, no funny business with Control.Invoke :)
CurrentState state =
(CurrentState)e.UserState;
txtProgress.Text = state.CrawlStatus;
lblStatus2.Text = state.sStatus;
txtItemsStored.Text = state.TotItems.ToString() + " items";
txtLastRunTime.Text = state.MostRecentGatherDate.ToString();
AppNameKey.SetValue("LastCrawlTime", txtLastRunTime.Text);
}`
The DoWork event reads from a control
private void m_oWorker_DoWork(object sender, DoWorkEventArgs e)
{
DateTime LastCrawlTime;
try
{
LastCrawlTime = Convert.ToDateTime(txtLastRunTime.Text);
if (lblStatus2.Text != "Status: Running" || (!cmdRunNow.Enabled && cmdStopRun.Enabled)) // run is not currently running or cmdRunNow clicked
{
//lblStatus2.Text = "Status: Running";
GetUpdated(LastCrawlTime,e);
}
}
catch (Exception Ex)
{
MessageBox.Show(Ex.Message);
}
}
The RunWorkedrCompleted Event writes to a control:
void m_oWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
lblStatus2.Text = "Status: Stopped";
cmdStopRun.Enabled = false;
}
// Check to see if an error occurred in the background process.
else if (e.Error != null)
{
lblStatus2.Text = "Fatal Error while processing.";
}
else
{
// Everything completed normally.
//CurrentState state = (CurrentState)e.UserState;
lblStatus2.Text = "Status: Finished";
}
}
None of these caused problems. What did cause the problem was the attempt to reference e.UserState in the RunWorker_Completed event (commented out above)
I figured out the issue.
The progress bar was exceeding its maximum allowed (of 100).
The problem was that in the code, I was incrementing the progress bar as such:
current = current + 2;
I replaced it with:
current++;
The reason why I was incrementing by 2 was simply for testing purposes.

Categories

Resources