How can I solve my Close/Dispose Unhandled Exception? - c#

I'm developing a Windows Forms App, a connector between a callCenter and a CRM and I'm clogged with an unhandled exception which I can't understand or either solve.
My App has:
MainForm with some controls and a FlowControll Pannel which may or may not show a collection of SearchForms - OK
SearchForm - basically a DataGridView with clickable cells - depending on the column index, the click will perform different operations - OK
DialForm - loads as consequence of clicking a cell on my SearchForm and allows to i) Cancel (closes the form OK); ii) dial a number -PROBLEM - very frustrating
When I click to dial, the app correctly makes the phoneCall trough my callCenter, but the very next line of code (Dispose() upon the DialForm) generates an unhandled exception of type 'Safe Handle has been closed', reportedly with DangerousAddRef(Boolean& success).
The relevant methods:
///
/// DialForm Method - upon clicking «Dial Number» Button
///
private void dialButton_Click(object sender, EventArgs e)
{
//
// Piece of code to manage closing with DialFormCloseEventArgs
//
DialFormCloseEventArgs args = new DialFormCloseEventArgs();
args.toClose = this;
EventHandler<DialFormCloseEventArgs> eh = father.Search_CloseDialForm;
this.BeginInvoke(eh, new object[] { sender, args });
}
///
/// SearchForm Method - EventHandler to close DialForm and make call
///
public void Search_CloseDialForm(object sender, DialFormCloseEventArgs e)
{
string numberToDial = e.toClose.numberToDial.Text;
e.toClose.Dispose();
this.rePositionMainForm();
using (ConectorCTI.ConectorCTI ctiws = new ConectorCTI.ConectorCTI())
{
ctiws.Timeout = 180000;
// Synchronous Call
//ctiws.Dial(this.userLoginName, numberToDial, "");
// Assynchronous Call
ctiws.DialAsync(this.userLoginName, numberToDial, "");
}
}
I also show my App with textual descriptions so you can actually «see it»

Probably need to wait for the async task to complete before disposing of the object. Something like
using (ConectorCTI.ConectorCTI ctiws = new ConectorCTI.ConectorCTI())
{
var task = ctiws.DialAsync(this.userLoginName, numberToDial, "");
await task;
}

Related

Load popup form cross thread

I'm having trouble manipulating forms when from another thread.
I've overcome the issue by loading the form at runtime, showing it then hiding it. This means the form is created on the right thread and can be manipulated using invokes.
This is not the right way to do it. I have 3 problems that come from using this method
I can't spawn another popup box I have to use the one I created at runtime
The forms flash briefly on load - now that I have 3 forms its pretty obvious what I'm doing.
I have to use a variable bool to hold if the popup is open or not.
If anyone could point me in the right direction It would be much appreciated. Currently my code looks like:
On Main form Load:
CallerIDfrm = new frmCallerID();
CallerIDfrm.Show();
CallerIDfrm.Hide();
to manipulate the popup Im using
delegate void StringArgReturningVoidDelegate1(string CallerIDnum, string CallerIDname, string ContactID);
private void CallerID(string CallerIDnum, string CallerIDname, string ContactID)
{
if (CallerIDfrm.InvokeRequired)
{
StringArgReturningVoidDelegate1 d = new StringArgReturningVoidDelegate1(CallerID);
CallerIDfrm.Invoke(d, new object[] { CallerIDnum, CallerIDname, ContactID });
}
else
{
if (ContactID != null || ContactID != "0")
{
CallerIDfrm.ContactID = ContactID;
}
CallerIDfrm.Mainfrm = this;
CallerIDfrm.TopLevel = true;
CallerIDfrm.TopMost = true;
CallerIDfrm.lblCallerIDname.Text = CallerIDname;
CallerIDfrm.lblCallerIDnum.Text = CallerIDnum;
CallerIDfrm.Show();
CallerIDOpen = true;
}
}
To Hide the popup until required again im using:
delegate void StringArgReturningVoidDelegate2();
private void CallerIDClose()
{
if (CallerIDfrm.InvokeRequired)
{
StringArgReturningVoidDelegate2 d = new StringArgReturningVoidDelegate2(CallerIDClose);
CallerIDfrm.Invoke(d, new object[] { });
}
else
{
try
{
CallerIDfrm.Hide();
CallerIDOpen = false;
}
catch
{
}
}
}
I've tried otherways but the Popup loads as if it is not responding and I loose access to the popup.
Ultimately I'd like to be able to spawn multiple popups and have the ability to close them from the Main Form.
What I gather from your question: You have an caller api/lib/class and you like to show CallerId on a popup form when a call is received. Have a look at Events and Event Driven programming.
The following codes has not been tested, I wrote it from top of my head. Might not compile, they are here to show an example:
Create an CallReceived event in api/lib class as follows:
public event EventHandler<CallReceivedEventArgs> CallReceived;
protected void OnCallReceived(EventArgs e)
{
var handler = CallReceived;
if (handler != null)
handler(this, e);
// Note: For C# 6.0 and later, above statements can be simplified to
// CallReceived?.Invoke(this, e);
}
Note: If you don't have access to this api/lib code, create a Gateway class and put your event in there along with mechanism to trigger it.
Also create a CallReceivedEventArgs, this will be used to transfer event data:
public class CallReceivedEventArgs : EventArgs
{
public string CallerIDnum {get; set;}
public string CallerIDname {get; set;}
public string ContactID {get; set;}
}
Now, in your api/lib class raise this event whenever a call is received:
// a call received, replace dummy values with actual values
OnCallReceived(new CallReceivedEventArgs() { CallerIDnum="5554443322", CallerIDname="SOME_NAME", ContactID="SOME_CONTACT" });
Finally in your GUI form, register to said event and process accordingly.
// inside your main form class
private CallerAPI callerApi = new CallerAPI();
// somewhere inside you main form class, register to event
// from your use case, I would place it inside Main Form's constructor
callerApi.CallReceived += callerApi_Callreceived;
// receive event
void callerApi_Callreceived(object sender, CallReceivedEventArgs e)
{
var callerIDnum = e.CallerIDnum;
// etc.
// show callerId form with details
// you need to change frmCallerID's constructor accordingly
CallerIDfrm = new frmCallerID(e.CallerIDnum, CallerIDname, ContantID);
// to be able to track opened popups, you can store them inside a list
// private List<Form> openPopupList = new List<Form>();
//
// alternatively, you can assign CallerIDnum to form's name property
// and store these inside a List<string> instead of List<Form>
openPopupList.add(CallerIDfrm);
CallerIDfrm.Show();
}
Don't forget to unregister from event.
callerApi.CallReceived -= callerApi_Callreceived;
To wrap it up:
I can't spawn another popup box I have to use the one I created at runtime
You can create and show multiple frmCallerID, independent from each other.
The forms flash briefly on load - now that I have 3 forms its pretty obvious what I'm doing.
Since new approach creates CallerID forms based on events, you won't see these form flashing. It'll open whenever a CallReceived event is received.
I have to use a variable bool to hold if the popup is open or not.
A better approach would be: Register to forms FormClosed event, and remove from openPopupList accordingly.
frmCallerID.FormClosed += frmCallerID_FormClosed;
void frmCallerID_FormClosed(object sender, EventArgs e)
{
// remove form from openPopupList
frmCallerID closedPopup = (frmCallerID) sender;
openPopupList.remove(closedPopup);
}

Trouble attaching an event to a custom task pane. VSTO, Excel 2016

Background
I'm currently working on an application in VSTO2015 and Excel 2016. The application manages a number of CustomTaskPanes in different windows. I am trying to get // some code to fire when the task pane is opened or closed. In order to handle the various windows, I've implemented a structure very similar to this;
CustomTaskPane in Excel doesn't appear in new Workbooks
ThisAddIn.cs contains the following class;
public class TaskPaneManager
{
static Dictionary<string, Microsoft.Office.Tools.CustomTaskPane> _createdPanes = new Dictionary<string, Microsoft.Office.Tools.CustomTaskPane>();
/// <summary>
/// Gets the taskpane by name (if exists for current excel window then returns existing instance, otherwise uses taskPaneCreatorFunc to create one).
/// </summary>
/// <param name="taskPaneId">Some string to identify the taskpane</param>
/// <param name="taskPaneTitle">Display title of the taskpane</param>
/// <param name="taskPaneCreatorFunc">The function that will construct the taskpane if one does not already exist in the current Excel window.</param>
public static Microsoft.Office.Tools.CustomTaskPane GetTaskPane(string taskPaneId, string taskPaneTitle, Func<UserControl> taskPaneCreatorFunc)
{
string key = string.Format("{0}({1})", taskPaneId, Globals.ThisAddIn.Application.Hwnd);
string title = taskPaneId;
string windowId = Globals.ThisAddIn.Application.Hwnd.ToString();
if (!_createdPanes.ContainsKey(key))
{
var customTaskPane = taskPaneCreatorFunc();
var pane = Globals.ThisAddIn.CustomTaskPanes.Add(customTaskPane, taskPaneTitle);
_createdPanes[key] = pane;
//
// Set the task pane width as set in the App.Config
//
pane.Width = Convert.ToInt32(ConfigurationManager.AppSettings["TaskPaneWidth"]);
}
return _createdPanes[key];
}
....
My calls from Ribbon.cs;
private void btnUploadWizard_Click(object sender, RibbonControlEventArgs e)
{
// Check for configuration sheet first.
string title = "Upload Wizard";
TaskPaneManager.isConfigurationCreated();
var UploadWizardTaskpane = TaskPaneManager.GetTaskPane(title, title, () => new TaskPaneUploadWizard());
UploadWizardTaskpane.Visible = !UploadWizardTaskpane.Visible;
}
The Problem: Event Handlers
I'm having difficulty getting an event handler to fire. I can't tell what I'm doing wrong. In the TaskPaneDesigner I am attaching the event using this.VisibleChanged += new System.EventHandler(this.TaskPaneUploadWizard_VisibleChanged);, and then defining it in my TaskPaneUploadWizard class as follows;
public partial class TaskPaneUploadWizard : UserControl
{
...
public TaskPaneUploadWizard()
{
InitializeComponent();
}
private void TaskPaneUploadWizard_VisibleChanged(object sender, EventArgs e)
{
// Some code
}
My thoughts
It seems to me as though I am either attaching the eventHandler to something other than the CustomTaskPane object, or I am attaching it before the CustomTaskPane is created.
Help!
To detect if the task pane was opened or closed, you have to attach to the VisibleChanged event of pane.
So the simplest solution would be to add just one line of code to the GetTaskPane method:
var pane = Globals.ThisAddIn.CustomTaskPanes.Add(customTaskPane, taskPaneTitle);
// This is the new line to be added.
pane.VisibleChanged += (s, e) => customTaskPane.Visible = pane.Visible;
_createdPanes[key] = pane;
Now the visibility of the whole task pane will be passed on to its content and // some code should be executed.
Alternatively, if you don't want to manually set customTaskPane.Visible for whatever reason, you could as well execute your code directly in this new event handler:
pane.VisibleChanged += (s, e) => { /* some code */ };
But personally I would rather recommend the first approach because it seems to fit a bit better into your existing code.
var UploadWizardTaskpane = TaskPaneManager.GetTaskPane(title, title, () => new TaskPaneUploadWizard());
This is creating a CustomTaskPane not a TaskPaneUploadWizard object
It seems like this.VisibleChanged += new System.EventHandler(this.TaskPaneUploadWizard_VisibleChanged); is acting on TaskPaneUploadWizard which is only the guest of CustomTaskPane
Note that The visibility of CustomTaskPane doesn't affect TaskPaneUploadWizard
My suggestion
You remove VisibleChanged from the designer, you will add it manually in your TaskPaneUploadWizard
public partial class TaskPaneUploadWizard : UserControl
{
//{---}
CustomTaskPane _HostPane;
public CustomTaskPane HostPane
{
get => _HostPane;
set
{
if(_HostPane == value)
return;
_HostPane?.VisibleChanged -= TaskPaneUploadWizard_VisibleChanged;
_HostPane = value;
_HostPane?.VisibleChanged += TaskPaneUploadWizard_VisibleChanged;
}
}
//{---}
private void TaskPaneUploadWizard_VisibleChanged(object sender, EventArgs e)
{
// Some code
}
//{---}
Then in GetTaskPane you say
//{---}
var pane = Globals.ThisAddIn.CustomTaskPanes.Add(customTaskPane, taskPaneTitle);
(customTaskPane as TaskPaneUploadWizard).HostPane = pane;
//{---}

Delaying between two consecutive calls

Using Silverlight 5, RIA services 1.0 and Telerik controls I have a dialog with a button that when clicked goes and runs some service code.
The issue is when I double click or triple click it real fast, it keeps calling the service thus getting this error:
System.InvalidOperationException:
System.InvalidOperationException: A SubmitChanges operation is already in progress on this DomainContext.
I was wondering if this is a common error and any work around for it?
Here is the .NET source code that it goes to that causes it to crash:
public virtual SubmitOperation SubmitChanges(Action<SubmitOperation> callback, object userState)
{
if (this.IsSubmitting)
throw new InvalidOperationException(Resource.DomainContext_SubmitAlreadyInProgress);
This is not an error. A submitChange operation is asynchronous so you have to detect that it is completed before doing something else.
One solution could be to block the user from clicking on the button before the operation is completed.
Since you are using a Telerik controls, you can use a busy indicator.
private void btnUserAction_Click(object sender, RoutedEventArgs e)
{
myBusyIndicator.IsBusy = true;
// DO your stuff
var submitOperation = myContext.SubmitChanges();
submitOperation.Completed += (s, e) =>
{
// It is completed, now the user can click on the button again
myBusyIndicator.IsBusy = false;
}
}
EDIT : The busy indicator should be defined in your Xaml, like this :
<Telerik:RadBusyIndicator x:Name="myBusyIndicator">
<Grid x:Name="LayoutRoot" >
<Button Name="btnUserAction" Click="btnUserAction_Click" />
</Grid>
</Telerik:RadBusyIndicator>
I had this same problem. So I created a property on my ViewModel and bound it to my IsEnabled property of my button. I set this to false when the save starts and to true when it is done.
<Button Content="Save" IsEnabled="{Binding FinishedDataTransfer}" ...
bool _finishedDataTransfer = false;
public bool FinishedDataTransfer
{
get { return _finishedDataTransfer; }
set
{
_finishedDataTransfer = value;
RaisePropertyChanged("FinishedDataTransfer");
}
}
The advantage of doing it this way instead of a busy indicator is that the user can still do thing with the page while the save is running.
We had same problem, and had to change from sending a command to the viewmodel to having a bit of code behind for button click ...
/// <summary>
/// Workaround for handling double-click from the Map Results button - trying to prevent running the query twice and adding duplicate layers into the Layer Control
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void MapResultsButton_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (e.ClickCount == 1)
this.ViewModel.MapResultsCommand.Execute(null);
}
As you can see, this uses a ClickCount property which is part of the MouseButtonEventArgs
Once the command has reached the viewmodel we can then worry about busy indicators and other ways of disabling the button.

ontextchanged async sql stored proc call

I have a winform application and on the main form I placed a textbox.
What I want is that on the OnTextChanged event I need to query sql (stored proc) table in order to bring a list of matches with the character typed. For example if I type letter A then automaticly I should go and search in the database for names starting with letter "A", if I then type letter "L" then I should go and search for names starting with "AL" and so on.
The thing is that If I the user type fast then It should cancel any async process that is in progress and keep only the task for the last letter typed.
Any clue on how to achieve these?
The thing is that If I the user type fast then It should cancel any async process
It shouldn't need to cancel anything - the trick is not to start anything until you think the user has stopped typing.
I wrote something a bit similar, whereby when the user typed I had a thread start a new method, the method waited for a (configurable) time before executing. Another keystroke would call the method again and reset the wait period - effectively if someone continued to type the task wouldn't execute; as soon as they stopped it would (after a 200 ms pause for example).
I would implement something similar if I were you, as it avoids going to SQL when you don't have to
updated - by way of a simple example, a new project with a new form and default text box should allow the following code to function for your needs (as a very simple starting point)
public partial class Form1 : Form
{
private bool _waiting;
private bool _keyPressed;
private const int TypingDelay = 220; // ms used for delay between keystrokes to initiate search
public Form1()
{
InitializeComponent();
}
private void WaitWhileUserTyping()
{
var keepWaiting = true;
while (keepWaiting)
{
_keyPressed = false;
Thread.Sleep(TypingDelay);
keepWaiting = _keyPressed;
}
Invoke((MethodInvoker)(ExecuteSearch));
_waiting = false;
}
private void ExecuteSearch()
{
Thread.Sleep(200); // do lookup
// show search results...
MessageBox.Show("Search complete");
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
if (_waiting)
{
_keyPressed = true;
return;
}
_waiting = true;
// kick off a thread to do the search if nothing happens after x ms
ThreadPool.QueueUserWorkItem(_ => WaitWhileUserTyping());
}
}

Why would commenting out a line of code change whether a *previous* line of code is executed?

With this code:
using (pbDialog = new pbDialogs())
{
ProgressBar = new frmProgress(this, false);
ProgressBar.SetProgressLabelText("Inventory Data");
MessageBox.Show("Set progress label text to Inventory data");
typeProgress = (int) ProgressStates.ProgressQRY;
ProgressBar.label1.Text += " (Receiving)";
if (pbDialog != null)
{
MessageBox.Show("pbDialog is not null");
//pbDialog.ShowDialog(ProgressBar, this);
}
else
{
MessageBox.Show("pbDialog IS null");
ProgressBar.ShowDialog();
}
ProgressBar = null;
MessageBox.Show("Made it to compressDB()");
compressDB();
. . .
}
I see "Set progress label text to Inventory data"
then "pbDialog is not null"
then "Made it to compressDB()"
Nothing too odd there; but if I uncomment the line that is commented above, I see only "pbDialog is not null"
It is hanging for some reason as a result to the call to ShowDialog(); what is really strange, though, is that this prevents "Set progress label text to Inventory data" from displaying. Why would that be the case?
Note: I think the "pb" in the code stands for "peanut brittle" or some such; I'm pretty sure about the "brittle" part, anyway.
UPDATE
Yeah, the use of ShowDialog() with pbDialog is one of scads of examples that the original coder was practicing job security by obscurity - but then he [un]fortunately skedaddled, leaving in his wake a cesspool of spaghetti/eggshell code with no comments, misleading names and every sort of bizarre and convoluted, counterintuitive practice imaginable in the witches brew he purportedly considered a masterpiece of elegant design and clever-clever tricks.
pbDialog is an instance of a class (pbDialogs). Just to give you a taste of how macabre and convoluted and tangled it all is, here is that class:
public class pbDialogs : IDisposable
{
private static Form m_top;
public pbDialogs()
{
} // pbDialogs Constructor
public static void Activate( Form form )
{
form.Capture = true;
IntPtr hwnd = OpenNETCF.Win32.Win32Window.GetCapture();
form.Capture = false;
OpenNETCF.Win32.Win32Window.SetForegroundWindow( hwnd );
} // Activate
/// <summary>
/// This method makes ShowDialog work the way I want, I think.
/// </summary>
/// <remarks>
/// Here is what it does:
/// 1. Sets the caption of the new window to the same as the caller's.
/// 2. Clears the caption of the parent so it won't show up in the task list.
/// 3. When the ShowDialog call returns, brings the previous window
/// back to the foreground.
/// </remarks>
/// <param name="dialog"></param>
/// <param name="parent"></param>
public void ShowDialog( Form dialog, Control parent )
{
Control top = parent.TopLevelControl;
string caption = top.Text;
dialog.Text = caption;
top.Text = "--pending--"; // Don't show parent in task list
dialog.Activated += new EventHandler( form_Activated );
dialog.Closed += new EventHandler( form_Closed );
m_top = dialog; // New top-most form
dialog.ShowDialog();
m_top = (Form)top; // The top dialog just changed
dialog.Activated -= new EventHandler( form_Activated );
dialog.Closed -= new EventHandler( form_Closed );
top.Text = caption; // Make visible in task list again
Activate( (Form)top ); // And make it the active window
} // ShowDialog
/// <summary>
/// If one of our other windows, such as the main window,
/// receives an activate event, it will activate the current
/// top-most window instead.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private static void form_Activated( object sender, EventArgs e )
{
if( (m_top != null) && !(sender == m_top) ) // Is this the top-most window?
Activate( m_top ); // No, activate the top dialog
} // form_Activated
private static void form_Closed( object sender, EventArgs e )
{
m_top = null; // When you close the top dialog, it's not top anymore
} // form_Closed
#region IDisposable Members
public void Dispose()
{
// TODO: Add pbDialogs.Dispose implementation
}
#endregion
} // class pbDialogs
There is also a "related" ProgressBar -- a form which shares a file with pbDialogs, and whose instance variable is defined in the file that contains the code above thusly:
public static frmProgress ProgressBar;
This is definitely "whack-a-mole" code - if I make one small, seemingly innocuous change, all Dallas breaks loose in what even a semi-sane person would consider a completely unrelated part of the code.
This may be an indication of just how squirrelly this code/project is: I will make a new build after commenting out a couple of lines, and the size of the file will change from 400KB to 408, or from 412 to 408, etc. It's not normal behavior for an .exe to change that much in size (in a relative sense) with such a small change, is it?
UPATE 2
This, in frmProgress (which has both "public class frmProgress : System.Windows.Forms.Form" and "public class pbDialogs : IDisposable") scares me:
using System.Windows.Forms;
using OpenNETCF.Windows.Forms;
The second (OpenNETCF) is grayed out, indicating it's not really used, but it may be that it was previously used, and somehow that "Windows.Forms" code inadvertently got switched to "System" code, and that may be contributing to its current groundsquirellyness.
ShowDialog is generally a blocking call. The code will not continue past this until the dialog is closed.

Categories