VSTO Outlook 2013 Addin Quit - c#

I have a VSTO addin that displays a a dialog box with buttons yes no and cancel. I want the form to close anytime cancel or the X are clicked. I also want the application to quit when the form is closed. Here is my code:
var frm = new Form1();
DialogResult res = frm.ShowDialog();
if (client != null)
{
if (res == DialogResult.Yes)
{
path = DRIVE_LETTER + ":/Clients/" + client + "/Correspondence/";
}
else if (res == DialogResult.No)
{
path = DRIVE_LETTER + ":/Clients Project History/" + client + "/Correspondence/";
}
else if (res == DialogResult.Cancel)
{
frm.Close();
}
else
{
frm.Close();
}
And then my form closing event handler:
private void Form1_Closing(object sender, CancelEventArgs e)
{
Application.Exit();
}
But it doesn't seem to work. Microsoft.Office.Interop.Outlook.Application doesn't have an Exit method. How can I do the equivalent from within VSTO? I want my application to stop executing completely when those forms are canceled/closed.
Thanks
EDIT: can anyone provide an example of quitting the addin. Or stopping all execution if a certain condition is met, like Pyton's sys.exit(). I don't want outlook to close, just the addin to stop execution. Not even unload, just stop.

If you need to shut down Outlook you may use the Quit method of the Application class. The associated Outlook session will be closed completely; the user will be logged out of the messaging system and any changes to items not already saved will be discarded.
But if you need to shut down the add-in (not the host application) you can:
Disable all event handlers and UI controls. To get the job done you may check out the global boolean variable which can indicate the state of the add-in (allowed to run or not).
The Connect property of the ComAddIn class allows to set the state of the connection for the specified COMAddIn object. The property returns true if the add-in is active; it returns false if the add-in is inactive. An active add-in is registered and connected; an inactive add-in is registered but not currently connected.
Outlook.Application outlook = new Outlook.Application();
if (outlook.Application.COMAddIns.Item("OutlookAddIn").Connect)
{
outlook.Application.COMAddIns.Item("OutlookAddIn").Connect = false;
}
else
{
outlook.Application.COMAddIns.Item("OutlookAddIn").Connect = true;
}

Related

Excel VSTO how to check if the Cancel button has been triggered from the dialog after the WorkbookBeforeClose event is fired?

I am currently developping an Excel VSTO addin and here is the code for WorkbookBeforeClose
private void App_WorkbookBeforeClose(Excel.Workbook Wb, ref bool Cancel)
{
bool isEnabled = false;
setRibbonControlState(ref isEnabled);
}
In this code I disable the ribbon if there is no workbook left opened. But if I press the Cancel button from the dialog after I tried to close Excel, the ribbon gets disabled anyway. But as the WorkbookBeforeClose event passed a Cancel parameter, I don't know how to set that parameter when I press the button, how do I check the dialog prompted for the button that has been triggered.
All cases I have seen so far implement a dialog in the body of the WorkbookBeforeClose handler, but I don't want to implement a custom dialog, I would like to use the one provided by default.
Thanks!
As of my VBA experience this
but I don't want to implement a custom
dialog, I would like to use the one provided by default.
is impossible, because that dialog appears just after the before_close event.
The only (known by me) way to manage this stuff - is to create own SaveChanges dialog, which is, by the way, very simple and not every user will notice the difference. And moreover, it will do the same job as default prompt.
The other thing you should pay attention to - is that there may be at least one invisible workbook. Even if you see such screen:
there is a chance that this.Application.Workbooks.Count will show you 1, instead of 0. This is due to the possibility that user has its own Personal.xlsb workbook, which is invisible, but nevertheless, is loaded with an Excel.Application. So in case you want to disable your ribbon properly - you should consider this as well.
Here is my example of this solution:
public partial class ThisAddIn
{
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
this.Application.WorkbookBeforeClose += ApplicationOnWorkbookBeforeClose;
}
private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
{
}
// Catch the before close event
private void ApplicationOnWorkbookBeforeClose(Excel.Workbook wb, ref bool cancel)
{
if (!wb.Saved)
{
switch (MessageBox.Show(text:$"Do you want to save changes you made to '{this.Application.ActiveWorkbook.Name}'?",
caption:"Microsoft Excel",buttons:MessageBoxButtons.YesNoCancel, icon:MessageBoxIcon.Exclamation))
{
case DialogResult.Cancel: // case want to cancel - break the closing event
{
cancel = true;
return;
}
case DialogResult.Yes: // case user want to save wb - save wb
{
wb.Save();
break;
}
case DialogResult.No: // case user don't want to save wb - mark wb as saved to avoid the application messagebox to appear
{
wb.Saved = true;
break;
}
}
}
if (IsAnyWorkbookOpen())
{
// replace this with your code
MessageBox.Show("Some books will still be open, don't turn off the ribbon");
return;
}
// replace this with your code
MessageBox.Show("All books will be closed");
}
private bool IsAnyWorkbookOpen()
{
// check that remaining amount of open workbooks without the one being closed is greater that 2
if (this.Application.Workbooks.Count - 1 > 2)
{
return true;
}
// IF the count of workbooks is 2 one of them maybe a PERSONAL.xlsb
else if (this.Application.Workbooks.Count == 2)
{
foreach (Excel.Workbook wb in this.Application.Workbooks)
{
if (!wb.Name.Equals(this.Application.ActiveWorkbook.Name))
{
// In case when one of two open workbooks is Personal macro book you may assume that
// there will be no open workbooks for user to work directly
if (wb.Name.Equals("Personal.xlsb".ToUpper()))
{
return false;
}
}
}
// In case when NONE of two open workbooks is a Personal macro book
// there will be at least one open workbook for user to work directly
return true;
}
else
{
return true;
}
}
#region VSTO generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InternalStartup()
{
this.Startup += new System.EventHandler(ThisAddIn_Startup);
this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
}
#endregion
}
And last thing - if you disable your ribbon, but application will still be running - you will have to enable it again on workbook_activate event.
Note I'm just moving from VBA to VSTO - so any comments are highly appreciated.

Overriding standart close button in Unity

I want to create a "Are you sure?" system when user clicks the close button of window. Is it possible to catch FormClosing event in Unity ?
Thanks.
Edit:
void OnApplicationQuit()
{
DialogResult result = MessageBox.Show(
"Are you sure you want to cancel ?",
"Question",
MessageBoxButtons.YesNo,
MessageBoxIcon.Question);
if (result == DialogResult.Yes)
{
Application.Quit();
}
}
I tried this to open a dialog when user clicks (X) button. It works but, looks like it creates a new dialog each frame.
This question needs an updated answer as Application.CancelQuit() is deprecated and MonoBehaviour.OnApplicationQuit() doesn't prevent the application from closing.
To accomplish this exit confirmation method, it's better to use: Application.wantsToQuit.
From the docs:
Unity raises this event when the player application wants to quit.
Add an event handler to this event to receive a notification that application is attempting to quit.
When this event is raised the quit process has started but can be cancelled. This means the player is not guaranteed to quit. For a guaranteed quit event take a look at Application.quitting.
Return true and the quit process will continue. Return false and the quit process will cancel.
Example:
// When the application starts, append a method
// that will run when the user attempts to exit
[RuntimeInitializeOnLoadMethod]
static void RunOnStart() {
Application.wantsToQuit += WantsToQuit;
}
public static bool quitConfirmation = false;
static bool WantsToQuit() {
if(quitConfirmation) {
return true;
} else {
RequestQuitConfirmation();
}
return false;
}
static void RequestQuitConfirmation() {
DialogResult result = MessageBox.Show(
"Are you sure you want to cancel ?",
"Question",
MessageBoxButtons.YesNo,
MessageBoxIcon.Question);
if (result == DialogResult.Yes)
{
quitConfirmation = true;
Application.Quit();
}
}
Note: The return value of this event (Application.wantsToQuit) is ignored when exiting play mode in the editor. IMPORTANT: The return has no effect on iPhone. Application can not prevent termination under iPhone OS.
Have a look at MonoBehaviour.OnApplicationQuit() and Application.CancelQuit()

Outlook addin forward issue

I have an Outlook addin for encryption and decryption that supports Outlook 2010 to 2013.
I am trying encrypt email to decrypt and display.
I am using open mail through mailItem_Open function
wrappedMailItem.Open += new MailItemInspectorOpenDelegate(mailItem_Open);
through this function i just decrypt email and content updated using
mailItem.HTMLBody = decryptString
Then the inspector window open and showing decrypt mail. Its working fine. I close the inspector window
mailItem_close function call
void mailItem_Close(Outlook.MailItem mailItem, ref bool Cancel)
{
try
{
if (mailItem == null)
return;
if (mailItem.Sent != false)
{
var signProperty = GetProperty(mailItem, "App.Decrypted");
// NOTE: Cannot call mailItem.Close from Close event handler
// instead we will start a timer and call it after we
// return. There is a small race condition, but 250
// milliseconds should be enough even on slow machines.
if ((bool)signProperty)
{
var timer = new System.Windows.Forms.Timer { Interval = 250};
timer.Tick += new EventHandler((o, e) =>
{
timer.Stop();
((Outlook._MailItem)mailItem).Close(Outlook.OlInspectorClose.olDiscard);
mailItem = null;
});
Cancel = true;
timer.Start();
}
}
}
catch
{
// Ignore random COM errors
}
Marshal.ReleaseComObject(mailItem);
}
The issue is
But am not closing the inspector window (Showing decrypt message) i just click the forward button its open new inspector window by out look and forward email and close it. Then i close the inspector window ,but the parent mailItem (Ie. inbox mail) showing in decrypt mode . In mailItem_close function i just discard all changes but its not working
This issue is not happening in Reply procedure in same steps, only happens forward case
Please help me
Instead of modifying the message contents on the fly, you'd be better off creating your own form that shows the decrypted data without setting and potentially saving the decrypted data on the original message.

Alert user about the running process during installation

protected override void OnBeforeInstall(IDictionary savedState)
{
base.OnBeforeInstall(savedState);
DialogResult result = DialogResult.None;
if (isExcelIsRunning())
{
result = MessageBox.Show("Excel is still running. Please save your work and close Excel, and then click \"Retry\" button to continue with installation.", "", MessageBoxButtons.RetryCancel, MessageBoxIcon.Warning);
}
while (result == DialogResult.Retry)
{
if (!isExcelIsRunning())
{
break;
}
}
}
//check if excel is currently running
private bool isExcelIsRunning()
{
bool flag = false;
Process[] xlProc = Process.GetProcessesByName("excel");
if (xlProc != null)
flag = true;
return flag;
}
The above is the code I am about to use for my Installer Class.
What I want to achieve here is that I need the installer to check whether or not Excel is currently running during the installation. And if it is running, then there should be a pop-up message at the start of installation to alert user to close off Excel and the installer should pause until there's no Excel instance found in the process list any more.
Back to the code, I don't think that while block is quite right, as it could cause an endless loop after user clicks "Retry" button if in the mean time Excel is still running.
So what'd be the better approach? Would anyone here take a look at the above code and see where I can improve?
I think your code would be something like this:
while(isExcelRunning())
{
var result = MessageBox.Show("Excel is still running. Please save your work and close Excel, and then click \"Retry\" button to continue with installation.", "", MessageBoxButtons.RetryCancel, MessageBoxIcon.Warning);
if (result != DialogResult.Retry)
{
//Handle Cancel
break;
}
}
This will keep displaying the alert in a loop, either until Excel exits, or they press cancel, at which you can do whatever you need to do; then we exit the loop (or you could return entirely out of the method).
The key here is display the alert repetitively, either until Excel is gone or they choose to cancel. If you need to do some code after the loop based on the response; perhaps something like this:
var userHasCancelled = false;
while(!userHasCancelled && isExcelRunning())
{
var result = MessageBox.Show("Excel is still running. Please save your work and close Excel, and then click \"Retry\" button to continue with installation.", "", MessageBoxButtons.RetryCancel, MessageBoxIcon.Warning);
if (result != DialogResult.Retry)
{
userHasCancelled = true;
}
}
if (userHasCancelled)
{
//User cancelled...
}
else
{
//Continue.
}

Form.Close() doesn't close window immediately

I need to be sure that all forms will be closed if I user somehow close the main form. So I decided to hide Close() function and I wrote something like this
public new bool Close()
{
List<Form> formsList = new List<Form>(Application.OpenForms.Count);
foreach (var form in Application.OpenForms)
formsList.Add((Form)form);
formsList.Reverse();
foreach (var form in formsList)
{
if (form.IsDisposed) continue;
Invoke(new GenericEventHandler(() => { form.Close(); }));
}
return Application.OpenForms.Count == 0;
}
So if all forms are closed successfully I return true and thanks to this I know that user can be logged out from application.
However it seems that form.Close() function is not fired immediately. After calling form.Close() formclosed event is not fired immediately as well as collection Application.OpenForms is not modified. The amount of opened forms is noted changed.
What could be a reason?
Why dont you just use
Application.Exit();
If the main form is created in the standard way in WinForms via Application.Run(new MainForm()); then when it closes, the application will exit, closing all other forms. So I don't think you need to worry about manually closing all child forms.
Well, if the user closes the main form, then upon closing it your application will definitely exit, since your form is responsible for keeping the application alive when it was passed to Application.Run().
Now, if you want to close your app when a form is closed, in that form you do this:
protected override void OnFormClosed(FormClosedEventArgs e)
{
base.OnFormClosed(e);
Application.Exit();
}
In general I suggest not to use new keyword to any members of Control and/or Form class. If you need your own method use your own name of it.
In your code you don't really need to use Invoke when closing child forms.
If you need to be sure that all your child Forms are closed before you actually execute logic related to MainForm.Close you may subscribe to MainForm.FormClosing event with handler similar to follwing:
void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
bool allChildFormsClosed = !closeAllChildForms();
if(!allChildFormsClosed)
{
e.Cancel = true;
MessageBox.Show("Failed to close one or more child forms.");
// better add some yes|no dialog to force exit if necessary
}
}
You may implement closeAllChildForms in a similar way as you did above - just: remove the formsList.Reverse(); and the 'Invoke..' - just call Close directly and update: if (this == form || form.IsDisposed) continue;
If for some reason, Application.Exit isn't cutting it for you, you could try this code (put it in the form's FormClosing event):
while (Application.OpenForms.Count > 1)
{
Form form = Application.OpenForms[1];
form.Close();
while (!form.IsDisposed) Application.DoEvents();
}
or if you want it to time out if it can't close all the forms after a while,
while (Application.OpenForms.Count > 1)
{
Form form = Application.OpenForms[1];
form.Close();
DateTime StartTime = DateTime.Now;
while (!form.IsDisposed && (StartTime - DateTime.Now).TotalMilliseconds < 1000)
Application.DoEvents(); //1-second timeout here
if (!form.IsDisposed) //if it's still not closed
{
//send a message to the user that the application can't exit right now
mbox("some forms are still open or whatever");
e.Cancel = true;
return;
}
}

Categories