Lost network connection causes Excel to crash - c#

My Excel AddIn is written in NetOffice, ExcelDNA, C#
It calls web service to get data. It takes a while to fetch a large amount of data.
During the process of data fetch, if network connection is lost, then Excel will hung, shows like "not responding". Now if I try to close Excel, it will ask you to close or debug. I simply close it.
Then when I restart Excel, there is an annoying message box comes up saying
"Excel experienced a serious problem with the 'commodity add-in' add-in. If you have seen this message multiple times, you should disable this add-in and check to see if an update is available. Do you want to disable this add-in?."
I wonder how to handle the situation when connection is lost appropriately? Thanks

Make the web service call asynchronously, if possible. Most WS will provide async versions and non-async versions of the calls that you can make.
If this is not possible, consider executing the web service data fetch within a separate thread.
In both scenarios, you should put some plumbing code in place to kill the job after a certain period, and probably some means to notify the user that not all is well.

"Excel experienced a serious problem with the 'XXX add-in' add-in. If
you have seen this message multiple times, you should disable this
add-in and check to see if an update is available. Do you want to
disable this add-in?."
You get this problem when an unhandled exception occurs. Excel will prompt you to disable the Add-In next start up. This can lead users to posts like this to fix it.
The pain is worse when you have to support clients using Citrix in non-admin environments. To get around the problem of Excel wanting to diable the add-In you have to add a Global Exception handler so the exception isn't referred back to Excel to avoid prompting users to disable the Add-In.
public YouAddInCtrl()
{
InitializeComponent();
// Add the event handler for handling UI thread exceptions to the event.
System.Windows.Forms.Application.ThreadException += ApplicationThreadException;
// Add the event handler for handling non-UI thread exceptions to the event.
AppDomain.CurrentDomain.UnhandledException += ApplicationUnhandledException;
}
private void ApplicationThreadException(object sender, ThreadExceptionEventArgs e)
{
addInManager.TopLevelExceptionHandler(e.Exception);
}
private void ApplicationUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
addInManager.TopLevelExceptionHandler((Exception)e.ExceptionObject);
}
// Any exceptions returned to Excel will cause the Addin to be disabled
// So we must swallow them here.
internal void TopLevelExceptionHandler(Exception ex)
{
var e = new NotificationEventArgs(NotificationEventArgs.NotificationEnum.TopLevelException);
if (NotifyEventTopLevelException != null)
{
if (NotifyEventTopLevelException(ex,e))
{
System.Diagnostics.Process.Start("mailto:Support#XYZ.com%3e?subject=XYZ%202%20PROD%20Environment%20Problem&body=Hi,%0A%0AIssue:%0A%0ASteps%20to%20Reproduce:");
}
}
LogExceptions(ex);
}
I would also suggest that you run the WebService request on a different thread, eg:
BackgroundWorker1.WorkerReportsProgress = true;
BackgroundWorker1.WorkerSupportsCancellation = true;
BackgroundWorker1.DoWork += DoWorkExecuteQuery;
BackgroundWorker1.RunWorkerCompleted += RunWorkerCompletedExecuteQuery;
private bool QueryData()
{
var thinkProgBar = new ThinkingProgressBar();
thinkProgBar.ShowCancelLink(true);
thinkProgBar.SetThinkingBar(true);
BackgroundWorker1.RunWorkerAsync(thinkProgBar);
thinkProgBar.ShowDialog();
if (thinkProgBar.Tag != null && thinkProgBar.Tag.ToString() == "Cancelled")
{
CancelGetDataByFilters();
thinkProgBar.SetThinkingBar(false);
return false;
}
thinkProgBar.SetThinkingBar(false);
return true;
}
private void DoWorkExecuteQuery(object sender, DoWorkEventArgs e)
{
dtQueryData = null;
e.Result = e.Argument;
((ThinkingProgressBar)e.Result).SetThinkingBar(true);
dtQueryData = WEBSERVICE.GetData(); //CALL YOUR WEBSERVICE HERE
}
private void RunWorkerCompletedExecuteQuery(object sender, RunWorkerCompletedEventArgs e)
{
var dlg = e.Result as ThinkingProgressBar;
if (dlg != null) {
((ThinkingProgressBar)e.Result).SetThinkingBar(false);
dlg.Close();
}
}
Here is the ThinkingProgress bar:
public partial class ThinkingProgressBar : Form
{
private System.DateTime startTime = DateTime.Now;
public ThinkingProgressBar()
{
InitializeComponent();
}
private void lblClose_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
this.Tag = "Cancelled";
this.Hide();
}
public void ShowCancelLink(bool show)
{
lblClose.Visible = show;
}
public void SetThinkingBar(bool on)
{
if (on)
{
lblTime.Text = "0:00:00";
startTime = DateTime.Now;
timer1.Enabled = true;
timer1.Start();
}
else
{
timer1.Enabled = false;
timer1.Stop();
}
}
private void timer1_Tick(object sender, EventArgs e)
{
var diff = new TimeSpan();
diff = DateTime.Now.Subtract(startTime);
lblTime.Text = diff.Hours + ":" + diff.Minutes.ToString("00") + ":" + diff.Seconds.ToString("00");
lblTime.Invalidate();
}
}

Related

How to make a program wait for user to type in a textbox?

When I run a WinForm program to a line, I would like to check if a textbox already has user input, if not, I will ask user to type in the textbox and wait till user types in some input, before running the next line of the code. I was wondering how to do the wait?
The program has to wait for the information required as input for the next line of code.
Thanks.
Waiting for something to happen in the GUI (using a timer, loops from other threads, etc...) is a massive waste of resources. Almost all functional programming languages have Events including C#
From Wikipedia:
event-driven programming is a programming paradigm in which the flow
of the program is determined by events such as user actions (mouse
clicks, key presses), sensor outputs, or message passing from other
programs or threads. Event-driven programming is the dominant paradigm
used in graphical user interfaces and other applications (e.g.,
JavaScript web applications) that are centered on performing certain
actions in response to user input. This is also true of programming
for device drivers (e.g., P in USB device driver stacks).
You can do it like this with the help of Control.TextChanged event inherited by the Textbox control:
private void Form1_Load(object sender, EventArgs e)
{
ValidateGUI();
}
private const int MIN_CHARS_TO_DO_SOMETHING = 8;
private const string NOT_VALID = "Oh No There is No User Input )-:";
private const string VALID = "Great We Can Do Something (-:";
private void textBox1_TextChanged(object sender, EventArgs e)
{
ValidateGUI();
}
private void ValidateGUI()
{
if (textBox1.Text.Length < MIN_CHARS_TO_DO_SOMETHING)
{
lblMessege.Text = NOT_VALID;
}
else
{
lblMessege.Text = VALID;
// Execute some code..
//...
//...
}
}
I'm going to assume you have a valid reason for waiting instead of monitoring input.
You just need to use background workers and then you need to set the DoWork event to wait for a specified amount of time, and the RunWorkerCompleted event to run your code checking if input is being made.
Here's an example assuming a label and a textbox are on the form already. Alternatively you can just add the background worker as a form element instead of creating it in code:
public Form1()
{
InitializeComponent();
waitForInput();
}
private void waitForInput()
{
BackgroundWorker waiter = new BackgroundWorker();
waiter.WorkerSupportsCancellation = true;
waiter.WorkerReportsProgress = true;
waiter.DoWork += wait10Seconds;
waiter.RunWorkerCompleted += doneWaiting;
waiter.RunWorkerAsync();
}
private void wait10Seconds(object sender, DoWorkEventArgs e)
{
System.Threading.Thread.Sleep(10000);
}
private void doneWaiting(object sender, RunWorkerCompletedEventArgs e)
{
if(textBox1.Text == "")
{
label1.Text = "Why haven't you typed anything?";
}
}
If I understand what you're getting at, a synchronization object like SemaphoreSlim might be a good fit for something like this. This declaration sets the initial count to 0 so the semaphore will block. The initSync method pauses halfway through and will await the textbox Enter key to release the semaphoreSlim before executing the next line. However, the UI thread is not blocked during the waiting period.
SemaphoreSlim _waitForText = new SemaphoreSlim(0, maxCount: 1);
private async Task initAsync()
{
richTextBox1.AppendText(
$"The async method that populates this RichTextBox waits indefinitely for input.");
richTextBox1.AppendText($"{Environment.NewLine}>");;
richTextBox1.SelectionColor = Color.Red;
// "check if a textbox already has user input"
if (string.IsNullOrWhiteSpace(textBox1.Text))
{
// "ask user to type"
textBox1.Text = "Enter ID";
await _waitForText.WaitAsync();
}
else onUserInputOK();
// The MOCK login has completed. Enable the app.
richTextBox1.Enabled = true;
richTextBox1.AppendText(
$"{Environment.NewLine}Now this method will complete, and you'll see a message box in 5 seconds");
}
Example
public MainForm() => InitializeComponent();
protected override async void OnLoad(EventArgs e)
{
base.OnLoad(e);
// Disable UI response until "logged in"
richTextBox1.Enabled = false;
// Subscribe to TextChanged event
textBox1.KeyDown += detectUserInput;
await initAsync();
await Task.Delay(TimeSpan.FromSeconds(5));
MessageBox.Show("All done");
}
private void detectUserInput(object sender, KeyEventArgs e)
{
if (e.KeyData == Keys.Return)
{
e.Handled = e.SuppressKeyPress = true;
if (!string.IsNullOrWhiteSpace(textBox1.Text))
{
onUserInputOK();
}
}
}
private void onUserInputOK()
{
richTextBox1.AppendText($"{textBox1.Text}");
Text = textBox1.Text; // Start echo to title bar
richTextBox1.SelectionColor = Color.DarkGreen;
// Remove this listener. Install normal runtime hook.
textBox1.KeyDown -= detectUserInput;
textBox1.TextChanged += normalTextboxListener;
_waitForText.Release();
}
private void normalTextboxListener(object sender, EventArgs e) => Text = textBox1.Text;

How can I exit while loop with "ESC" at any time/ C#

I have the following code in C#:
while (wechsel)
{
ProfDpDrv.MDPReadSlaveData(SlaveAddress, resetdiag, out dpData);
newStatus = dpData.m_InputData[i];
if (newStatus == 1)
{
int debug = e.RowIndex;
Dgv_Data_List.Rows[e.RowIndex].Cells["Adresse"].Style.BackColor = Color.Green; //
Dgv_Data_List.Refresh();
wechsel = false;
}
}
Here I want to EXIT the loop with the ESC if wechsel still at true. I have no console by the way.
Due to the fact, that we don't know where this code is running (WinForms, WPF, ASP, etc) except it is not a console it is hard to give you a concrete help.
A general advice to solve this issue, would be to create a CancellationTokenSource and give the source.Token to the long running method. This method can within the loop regulary check, if a cancellation was requested by checking token.IsCancellationRequested and then simply do whatever needed to leave a consistent state and exit the method.
And back to the beginning of my answer, it depends on the used interface you have to hook onto something to register a keypress and then calling source.Cancel().
I assumed that you are using WPF or WinForms.
Just subscribe to KetDown method on host window to capture all keys being pressed and then check if it's ESC and handle that appropriately, see code below:
public partial class MainWindow : Window
{
private bool wechsel = true;
public MainWindow()
{
InitializeComponent();
this.KeyDown += MainWindow_KeyDown;
}
private void MainWindow_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key != Key.Escape) return;
wechsel = false;
txtInfo.Text = "interrupted by ESC";
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
int i = 0;
while (wechsel)
{
txtInfo.Text = i++.ToString();
await Task.Delay(500);
}
}
}

Check multiple checkbox.checked state inside a do while cycle

I asked in a previous question how to "Threading 2 forms to use simultaneously C#".
I realize now that I was not explicit enough and was asking the wrong question.
Here is my scenario:
I have some data, that I receive from a local server, that I need to write to a file.
This data is being sent at a constant time rate that I cant control.
What I would like to do is to have one winform for the initial setup of the tcp stream and then click on a button to start reading the tcp stream and write it to a file, and at the same time launch another winform with multiple check-boxes that I need to check the checked state and add that info simultaneously to the same file.
This processing is to be stopped when a different button is pressed, closing the stream, the file and the second winform. (this button location is not specifically mandatory to any of the winforms).
Because of this cancel button (and before I tried to implement the 2nd form) I used a background worker to be able to asynchronously cancel the do while loop used to read the stream and write the file.
private void bRecord_Click(object sender, EventArgs e)
{
System.IO.StreamWriter file = new System.IO.StreamWriter(AppDomain.CurrentDomain.BaseDirectory + DateTime.Now.ToString("yyyy-dd-M--HH-mm-ss") + ".xml", true);
data_feed = client.GetStream();
data_write = new StreamWriter(data_feed);
data_write.Write("<SEND_DATA/>\r\n");
data_write.Flush();
exit_state = false;
string behavior = null;
//code to launch form2 with the checkboxes
//...
worker = new BackgroundWorker();
worker.WorkerSupportsCancellation = true;
worker.DoWork += new DoWorkEventHandler((state, args) =>
{
do
{
int var = data_feed.ReadByte();
if (var != -1)
{
data_in += (char)var;
if (data_in.IndexOf("\r\n") != -1)
{
//code to check the checkboxes state in form2
//if (form2.checkBox1.Checked) behavior = form2.checkBox1.Text;
//if (form2.checkBoxn.Checked) behavior = form2.checkBoxn.Text;
file.WriteLine(data_in + behavior);
data_in = "";
}
}
}
while (exit_state == false);
});
worker.RunWorkerAsync();
}
private void bStop_Click(object sender, EventArgs e)
{
exit_state = true;
worker.CancelAsync();
}
I hope I've been clearer now.
I not experienced in event programming and just started in C# so please try to provide some simple examples in the answers if possible.
At first would it be enough to use one Winform? Disable all checkboxes, click a button which enables the checkboxes and start reading the tcpstream? If you need two Forms for other reasons let me know, but i think this isn't needed from what i can see in your question.
Then i would suggest you to use the Task Library from .Net. This is the "modern" way to handle multithreading. BackgroundWorker is kind of old school. If you just able to run on .Net 2.0 you have to use BackgroundWorker, but don't seem to be the case (example follows).
Further if you want to cancel a BackgroundWorker operation this isn't only call CancelAsync();. You also need to handle the e.Cancelled flag.
backgroundWorker.WorkerSupportsCancellation = true;
private void CancelBW()
{
backgroundWorker.CancelAsync();
}
private void backgroundWorker_DoWork += ((sender, args)
{
//Handle the cancellation (in your case do this in your loop for sure)
if (e.Cancelled) //Flag is true if someone call backgroundWorker.CancelAsync();
return;
//Do your stuff.
});
There is no common way to directly cancel the backgroundWorker
operation. You always need to handle this.
Now let's change your code to the modern TAP-Pattern and make some stuff you want to have.
private void MyForm : Form
{
private CancellationTokenSource ct;
public MyForm()
{
InitializeComponent();
checkbox1.Enable = false;
//Disable all checkboxes here.
ct = new CancellationTokenSource();
}
//Event if someone click your start button
private void buttonStart_Click(object sender, EventArgs e)
{
//Enable all checkboxes here
//This will be called if we get some progress from tcp
var progress = new Progress<string>(value =>
{
//check the behaviour of the checkboxes and write to file
file.WriteLine(value + behavior);
});
Task.Factory.StartNew(() => ListenToTcp(ct, progress as IProgress<string)); //starts the tcp listening async
}
//Event if someone click your stop button
private void buttonStop_Click(object sender, EventArgs e)
{
ct.Cancel();
//Disable all checkboxes (better make a method for this :D)
}
private void ListenToTcp(CancellationToken ct, IProgess<string> progress)
{
do
{
if (ct.IsCancellationRequested)
return;
int temp = data_feed.ReadByte(); //replaced var => temp because var is keyword
if (temp != -1)
{
data_in += (char)temp;
if (data_in.IndexOf("\r\n") != -1)
{
if (progress != null)
progress.Report(data_in); //Report the tcp-data to form thread
data_in = string.empty;
}
}
while (exit_state == false);
}
}
This snippet should do the trick. I don't test it so some syntax error maybe occur :P, but the principle will work.
The most important part is that you are not allowed to access gui
components in another thread then gui thread. You tried to access the
checkboxes within your BackgroundWorker DoWork which is no possible
and throw an exception.
So I use a Progress-Object to reuse the data we get in the Tcp-Stream, back to the Main-Thread. There we can access the checkboxes, build our string and write it to the file. More about BackgroundWorker vs. Task and the Progress behaviour you can find here.
Let me know if you have any further questions.

Form freezes when opened for second time

I am developing a Windows Forms application that access a WCF service. I ran into a great problem that I can't predict the reason of it. Even the Visual Studio debugger not showing any exception in the Output view. The scenario is like this, I have a custom user control that has a linkLabel on it. Whenever the link label is clicked, a form is opened and a class object is passed to it. The class definition of this object resides on WCF service on a remote server. Now the problem is that when I click the linkLabel, the form opens perfectly loading each of its component according to the class object passed to it. But when I close this form and click that linkLabel again, the form opens but immediately freezes after loading some elements. I tried many code variations. Edited many part of code that I think can affect. But none of them showed the difference. Since, I don't know where actually is the code has error, I am posting the linkLabel click code and functions that are called after it is clicked.
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
Enabled = false;
string temp = Title.Text;
Title.Text = "Opening...";
System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ThreadStart(openTopic));
t.Start();
Title.Text = temp;
Enabled = true;
}
void createTopicWindow()
{
TopicViewer t = new TopicViewer(t);
Invoke(new Action(() => t.Show()));
}
private void openTopic()
{
Invoke(new Action(() => createTopicWindow()));
}
The above is the edited code, since I was getting Cross thread exception before.
Following is the code of constructor of the form that is called when clicked the linkLabel:
try
{
InitializeComponent();
this.t = topic;
if (IsHandleCreated == false)
CreateHandle();
System.Threading.Thread th = new System.Threading.Thread(new System.Threading.ThreadStart(loadTopic));
th.Start();
Common.openedTopics.Add(this);
AddComment addComment1 = new AddComment();
addComment1.Topic = t;
addComment1.Dock = DockStyle.Fill;
panel5.Controls.Add(addComment1);
backgroundWorker1.RunWorkerAsync();
}
catch (Exception)
{ }
void loadTopic()
{
Invoke(new Action(()=>tHead = new TopicHeader()));
Global.SetControlPropertyThreadSafe(tHead,"Topic", t);
Global.SetControlPropertyThreadSafe(tHead,"Dock", DockStyle.Fill);
Invoke(new Action(()=>panel1.Controls.Add(tHead)));
Global.SetControlPropertyThreadSafe(this,"Text", t.Title + " - Topic Viewer");
if (t.Description.Trim().Length > 0)
{
Global.SetControlPropertyThreadSafe(webBrowser1, "DocumentText", t.Description);
}
else
{
Invoke(new Action(() => tabControl1.TabPages[0].Dispose()));
}
Global.SetControlPropertyThreadSafe(tabPage2, "Text", "Comments (" + client.getComCount(t.TopicID) + ") ");
}
TopicHeader is another small user control.
Please anyone tell me the solution to this?
If you are using .Net 4.5, then using async/await would be easiest solution. That way, you don't need any Invokes
async private void Form1_Load(object sender, EventArgs e)
{
string s = await Task<string>.Factory.StartNew(LongRunningTask,
TaskCreationOptions.LongRunning);
this.Text = s;
}
string LongRunningTask()
{
Thread.Sleep(10000);
return "------";
}
I can't give a direct answer to you question, but this may give a hold on.
public void Form_Load()
{
// do some stuff on the gui-thread
// i need to do something that takes a long time:
ThreadPool.QueueUserWorkItem((state) =>
{
// i'll execute it on the ThreadPool
// Long running code....
// update results in mainform on gui thread.
Invoke(new Action( delegate
{
// because the invoke will execute this on the gui-thread, you'll able to update controls.
// update my gui controls.
DataGrid.Source = myReceiveDataThing;
}));
}
}
You might expand the code, to check if the form is still valid.

C# - Concurrent I/O operations exception

I have a refresh button to update news in my WP7 application. When I double or triple click on the refresh button I am getting an error
"WebClient does not support concurrent I/O operations" .
I think thats because It is sending the request triple times and making it crash. Here is my Click code.
private void NewsRefresh_Click(object sender, RoutedEventArgs e)
{
var vm = this.DataContext as MainPageViewModel;
if (vm != null)
{
vm.UpdateNews();
}
}
How can I turn it as "if It is busy cancel the process".
WebClient isn't very flexible but if you really want to use it you can make use of the IsBusy property and then cancel ongoing operation. Then, once it's cancelled you can restart it. There is important problem with synchronization. The operation which consists of checking IsBusy and invoking CancelAsync isn't atomic. Luckily DownloadStringCompleted is dispatched to the UI thread so you don't need to bother about synchronization. The snippet below shows how can you achieve it. For simplicity it's Windows Forms.
public partial class Form1 : Form
{
WebClient _WebClient;
bool _UpdateNews;
public Form1()
{
InitializeComponent();
_WebClient = new WebClient();
_WebClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(_WebClient_DownloadStringCompleted);
_UpdateNews = false;
}
void _WebClient_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
if (_UpdateNews)
{
_UpdateNews = false;
UpdateNews();
}
else if (e.Error != null)
{
// Report error
}
else
{
MessageBox.Show(e.Result);
}
}
private void button1_Click(object sender, EventArgs e)
{
if (_WebClient.IsBusy)
{
_WebClient.CancelAsync();
_UpdateNews = true;
}
else
{
UpdateNews();
}
}
private void UpdateNews()
{
_WebClient.DownloadStringAsync(new Uri("http://stackoverflow.com/questions/7084948/c-concurrent-i-o-operations-exception"));
}
}
The 'easy' way (though not bullet proof):
private void NewsRefresh_Click(object sender, RoutedEventArgs e)
{
try
{
NewsRefresh.Enabled = false;
var vm = this.DataContext as MainPageViewModel;
if (vm != null)
{
vm.UpdateNews();
}
}
finally
{
NewsRefresh.Enabled = true;
}
}
The more difficult approach would require more details on what exactly a MainPageViewModel is, and what UpdateNews() does. Basically you need to store a state value wherever you are storing the WebClient instance. Before using the WebClient you need check to see if you are already using it. The issue comes when multiple threads may operate on a single instance, or if you multiple operations (other than UpdateNews). When multiple threads are involved the easiest thing is to surround the usage of the WebClient with a Mutex.
Of course the other option is to not reuse the WebClient instance, rather create a new one for each new request.
UPDATE
Well, well, using DownloadStringAsync is certainly going to make things fun. The above code Disabling the UI will not work unless you move the re-enabling code. It would be easiest to go with my last suggestion and just create a new instance of WebClient. I'm not real fond of WebClient myself and prefer using WebRequest.Create.

Categories