Outlook 2010 Com addin - NewExplorer never fires - c#

For some reason in my app my FolderSwitch works on the main Explorer that opens with the application but the NewExplorer event never fires, so obviously the FolderSwitch event won't fire on a new Explorer.
I can't work out why the event doesn't fire.
private List<_Outlook.Explorer> ListOfExplorerWindows = new List<_Outlook.Explorer> { };
private _Outlook.Application Application;
public void OnConnection(object Application, Extensibility.ext_ConnectMode ConnectMode, object AddInInst, ref Array custom)
{
this.Application = (_Outlook.Application)Application;
}
public void OnStartupComplete(ref Array custom)
{
_Outlook.Explorer Explorer = this.Application.ActiveExplorer();
Explorer.FolderSwitch += new _Outlook.ExplorerEvents_10_FolderSwitchEventHandler(Explorer_FolderSwitch);
ListOfExplorerWindows.Add(Explorer);
this.Application.Explorers.NewExplorer += new _Outlook.ExplorersEvents_NewExplorerEventHandler(Explorers_NewExplorer);
}
private void Explorers_NewExplorer(_Outlook.Explorer Explorer)
{
Explorer.FolderSwitch += new _Outlook.ExplorerEvents_10_FolderSwitchEventHandler(Explorer_FolderSwitch);
ListOfExplorerWindows.Add(Explorer);
}

For any events you want to keep around when using VSTO, you are required to keep around a class-level member (Explorer, Application, Inspector, CommandBar, etc.) to keep the GC Thread from removing them. This is a resource optimization, but can also be a painful lesson to learn.
See related MSDN Forum post regarding event lifetime or similar SO post.

Related

Outlook: detect when "Send/Receive" has finished after startup

I am developing an outlook plugin in C#.
I want to be able to detect when outlook has finished the "send/receive" option after startup, so that I can then run operations on the received mail items.
What I have tried so far:
Manually calling Application.Session.SendAndReceive() on startup.
This runs fine, but the method returns before the send/receive is complete, which is the opposite of what I want
Overriding Application.NewMail and Application.NewMailEx - neither of these trigger as one might hope at startup (NewMailEx doesn't fire at all, NewMail is unreliable)
Calling NameSpace.SyncObjects.AppFolders.Start(); and registering the SyncObjects.AppFolders.SyncEnd event - this event fires before outlook has finished downloading mail
Iterating through NameSpace.SyncObjects, calling Start(), and registering SyncEnd - this method doesn't fire at all.
What is a solution here which will work depenably?
It seems that there is a hack to detect when syncing has finished; namely to override Application.Reminders.BeforeReminderShow as DWE suggests in this SO answer here
This event (in my testing) always fires after outlook syncing has finished.
Then, in order to make sure the reminder window fires, you add a new reminder at startup, and then hide the reminder again within Reminders_BeforeReminderShow
The code then being something like:
public partial class ThisAddIn
{
private ReminderCollectionEvents_Event reminders; //don't delete, or else the GC will break things
AppointmentItem hiddenReminder;
private void ThisAddIn_Startup(object sender, EventArgs e)
{
//other stuff
hiddenReminder = (AppointmentItem)Application.CreateItem(OlItemType.olAppointmentItem); //add a new silent reminder to trigger Reminders_BeforeReminderShow.This method will be triggered after send/receive is finished
hiddenReminder.Start = DateTime.Now;
hiddenReminder.Subject = "Autogenerated Outlook Plugin Reminder";
hiddenReminder.Save();
reminders = Application.Reminders;
reminders.BeforeReminderShow += Reminders_BeforeReminderShow;
}
private void Reminders_BeforeReminderShow(ref bool Cancel)
{
if (hiddenReminder == null) return;
bool anyVisibleReminders = false;
for (int i = Application.Reminders.Count; i >= 1; i--)
{
if (Application.Reminders[i].Caption == "Autogenerated Outlook Plugin Reminder") //|| Application.Reminders[i].Item == privateReminder
{
Application.Reminders[i].Dismiss();
}
else
{
if (Application.Reminders[i].IsVisible)
{
anyVisibleReminders = true;
}
}
}
Cancel = !anyVisibleReminders;
hiddenReminder?.Delete();
hiddenReminder = null;
//your custom code here
}
}
Yup, this is very kludgy, but that's the nature of working with outlook, and I haven't seen any free alternative which can actually claim to work reliably, whereas this works in all of the use cases I've tried it in. So that's a hit I'm willing to take to get a working solution.

"Invoke or BeginInvoke cannot be called on a control until" on a Label

I've read several questions/answers related to this problem but couldn't find a solution applicable to problem.
I have a form (MainForm) and a button (Upload) on it. When I click on the button (after selecting a file from a ComboBox to be uploaded to the server), it opens another form (UploadBackupForm) and uploads a file to the server. The upload process is controlled in UploadBackupForm and the form looks like this:
This works as long as upload is done once, I mean, the UploadBackupForm is called one time. The second time I click on the Upload button, UploadBackupForm opens and (after uploading some data) it throws an error saying:
System.InvalidOperationException: 'Invoke or BeginInvoke cannot be called on a control until the window handle has been created.'
at this specific line(s):
DurationLabel.Invoke((MethodInvoker)delegate
{
DurationLabel.Text = Count2Duration(count);
});
This has puzzled me because it works when it's done once, and doesn't work at the second time. I have basic knowdledge in C#, so I don't know what's causing this and how to solve it.
MainForm:
private void Upload2ServerButton_OnClick(object sender, EventArgs e)
{
Form UBF = new UploadBackupForm();
UBF.ShowDialog();
}
UploadBackupForm:
public partial class UploadBackupForm : Form
{
public UploadBackupForm()
{
InitializeComponent();
}
public static System.Timers.Timer timer = new System.Timers.Timer();
public static int count = 0;
private void UploadBackup_Load(object sender, EventArgs e)
{
timer.Interval = 1000;
timer.Elapsed += new ElapsedEventHandler(delegate {
count++;
// didn't do any good (this.IsHandleCreated or DurationLabel.IsHandleCreated)
// if (!this.IsHandleCreated)
// {
// this.CreateControl();
// }
DurationLabel.Invoke((MethodInvoker)delegate
{
DurationLabel.Text = Count2Duration(count);
});
});
// upload the archive to the server
new Thread((ThreadStart)delegate
{
FTP.Item[] items = FTP.ListDirectoryDetails(DataIO.FTP.Server, DataIO.FTP.Username, DataIO.FTP.Password, DataIO.FTP.UploadDir);
// here, I upload the file to the server and update the progress bar and the uploaded / total labels
Because the timer variable is static it remains even after the form is closed. It contains a reference to a delegate which holds a reference to the form so the previous instances are kept alive through the lifetime of your application. Also, the single timer posts callbacks to all previous instances along with the current one.
As correctly noted in the comments by Evk, make the timer and count non-static so they are dedicated to each instance of the form.

Word Document.SelectionChange event does not fire

Below is my code (simplified version for readability) from a VSTO-based Word addin.
The problem is that if I have two documents open, such as documentation and a template, my addin helps develop the template and works fine until the template is closed and re-opened in the same Word instance (the documentation file kept Word alive). Once that happens the SelectionChange event is not received, even though the listener is attached (confirmed with debugger).
Is there anything wrong with this code? Any other ways to attach a selection changed event?
void Application_DocumentOpen(Word.Document Doc)
{
// this method gets called as intended
Document vstoDoc = Globals.Factory.GetVstoObject(doc);
vstoDoc.SelectionChange += new Microsoft.Office.Tools.Word.SelectionEventHandler(ThisDocument_SelectionChange);
}
private void Application_DocumentBeforeClose(Word.Document doc, ref bool Cancel)
{
// this one also gets called as intended
Document vstoDoc = Globals.Factory.GetVstoObject(doc);
vstoDoc.SelectionChange -= new Microsoft.Office.Tools.Word.SelectionEventHandler(ThisDocument_SelectionChange);
}
void ThisDocument_SelectionChange(object sender, SelectionEventArgs e)
{
// this doesn't get called if the document is closed and open again within the same Word instance
Log("Selection changed");
}
UPDATE: This seems like VSTO bug.
Attaching to other events works fine, I can use ContentControlOnEnter/Exit:
vstoDoc.SelectionChange += ThisDocument_SelectionChange; // doesn't work
vstoDoc.ContentControlOnEnter += vstoDoc_ContentControlOnEnter; // works
vstoDoc.ContentControlOnExit += vstoDoc_ContentControlOnExit; // works
Why dont you use
Globals.ThisAddIn.Application.WindowSelectionChange +=
new ApplicationEvents4_WindowSelectionChangeEventHandler(Application_WindowSelectionChange);
instead of converting your Microsoft.Office.Interop.Word.Document object to Microsoft.Office.Tools.Word.Document

C# Outlook Plugin- Event Not Firing

I am working on a C# plugin for Outlook that will act when an item is added to the Deleted Items folder. Roughly speaking, the code looks like this. I see the log vent when the method is added, but when I delete an item, the other log event doesn't get fired. What am I missing?
public void OnConnection(object application, Extensibility.ext_ConnectMode connectMode, object addInInst, ref System.Array custom)
{
try
{
Outlook._Application olApp = new Outlook.ApplicationClass();
Outlook._NameSpace olNS = olApp.GetNamespace("MAPI");
Outlook.MAPIFolder deletedFolder = olNS.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderDeletedItems);
deletedFolder.Items.ItemAdd += new Outlook.ItemsEvents_ItemAddEventHandler(DeletedItems_ItemAdd);
log("addDeletedItemEventHandler method added", LogLevel.Debug);
}
catch (Exception e)
{
log("Exception in addDeletedItemEventHandler: " + e.Message, LogLevel.Error);
}
}
private void DeletedItems_ItemAdd(object Item)
{
log("DeletedItems_ItemAdd - Fired", LogLevel.Debug);
}
Dmitry's solution was correct. For anyone else who's looking to solve this problem:
I defined the following at the class level:
Outlook.Items oiDeletedFolderItems;
And in the OnConnection method, I added/ changed:
oiDeletedFolderItems = deletedFolder.Items;
oiDeletedFolderItems.ItemAdd += new Outlook.ItemsEvents_ItemAddEventHandler(DeletedItems_ItemAdd);
The COM object that fires the events (Items) must be alive to fire events. In your case, the compiler creates a temporary variable to hold the result of the call to deletedFolder.Items; as soon as that variable is garbage collected, the events stop firing.
Store Items object in a class variable and hook events on that object.

C# speech recognition (System.Speech.Recognition) issues

I've been working with a bit of speech recognition for a few days with various test programs and it's all worked fine. However i've tried implementing it into my OpenGL project and the function 'Recognized' is now not being called.
Up in the Windows Speech Recognition thing (the thing that says "try saying 'Start Listening'" an awful lot), words that are loaded appear when i say them, so I am assuming that it is correctly detecting words, it's just for some reason not triggering the event.
Here's the code i've been using. All you really need to know (besides what is shown in the code), is that AddCommands is called somewhere else, to add in a few words that i've been testing with and that 'Initiate' is called upon the loading of the form.
public class SpeechControls
{
public static SpeechRecognizer sRecognizer;
private static Dictionary<string, IVoiceControlable> controllers = new Dictionary<string, IVoiceControlable>();
public static void Initiate()
{
sRecognizer = new SpeechRecognizer();
sRecognizer.Enabled = true;
sRecognizer.SpeechRecognized += new EventHandler<SpeechRecognizedEventArgs>(Recognized);
}
private static void Recognized(object obj, SpeechRecognizedEventArgs args)
{
controllers[args.Result.Text].TriggerCommand(args.Result.Text);
}
public static void AddCommands(string[] commands, IVoiceControlable control)
{
foreach (string str in commands)
{
controllers.Add(str, control);
}
sRecognizer.LoadGrammar(new Grammar(new GrammarBuilder(new Choices(commands))));
}
}
Does anyone know why 'Recognized' would not be triggered?
Thanks for any help, much appreciated.
Because OpenGL runs of game loops rather than event listening, the thread is completely taken up by the loop. To start listening for commands a second thread is needed.

Categories

Resources