I develop an outlook adding using Visual studio 2013 and Addin express v.7.7.4087.
I wanting to handle the ItemSend() and save the item to drafts and close the inspector automatically, We can do everything apart from closing the inspector, because it is not allowed to close the inspector inside the method of ItemSend(), I have use a windows.forms.timer (close the mailitem inside timer) to do this but ends up with an error where it says “RCW content has been separated from …………” error
Are there any alternative ways of doing this?
In redemption (for outlook), is there a way of doing the same thing?
private void adxOutlookAppEvents1_ItemSend(object sender, DXOlItemSendEventArgs e)
{
Outlook.MailItem mailItem = null;
Outlook.Recipients recipients = null;
mailItem = e.Item as Outlook.MailItem;
try
{
// Some code goes here
if (editButtonClicked || swOffline)
{
e.Cancel = true;
if (swOffline)
{
mailItem.Save();
timer.Start();
}
}
}
catch (Exception ex)
{
}
finally
{
}
}
The timer will work fine as long as you keep in the MailItem object referenced. It looks like your mailItem variable is declared on the local level, where it will be garbage collected.
What code do you use for closing the inspector window? Could you please be more specific?
Are there any alternative ways of doing this?
The Close method of the Inspector class works like a charm.
In redemption (for outlook), is there a way of doing the same thing?
It doesn't provide anything for that. The library is based on the Extended MAPI and don't know anything about the Outlook UI.
Related
What is the importance of using Marshal.ReleaseComObject when interacting with MailItems in Outlook?
I refer to the walkthrough on creating a C# VSTO Outlook AddIn at
https://learn.microsoft.com/en-us/visualstudio/vsto/walkthrough-creating-your-first-vsto-add-in-for-outlook?view=vs-2019
Where they have an example of modifying the exiting selected mail item's subject and body.
void Inspectors_NewInspector(Microsoft.Office.Interop.Outlook.Inspector Inspector)
{
Outlook.MailItem mailItem = Inspector.CurrentItem as Outlook.MailItem;
if (mailItem != null)
{
if (mailItem.EntryID == null)
{
mailItem.Subject = "This text was added by using code";
mailItem.Body = "This text was added by using code";
}
}
}
The example ends without any mention of releasing the mail item object using Marshal.ReleaseComObject.
But in the .Net API Reference at https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.marshal.releasecomobject?view=netframework-4.8, they mentioned:
You should use this method to free the underlying COM object that holds references to resources in a timely manner or when objects must be freed in a specific order.
So apparently there are some consequence if we fail to use Marshal.ReleaseComObject on the MailItem we are referencing?
Is there a specific use case where not using Marshal.ReleaseComObject will lead to problem?
Thanks
If you don't call Marshal.ReleaseComObject, the object will be released as some later undermined point by the Garbage Collector. Normally, it is not a problem - you usually need to be careful and not leave the release to chance when processing a large number of items. In this particular case, you will be fine without Marshal.ReleaseComObject unless you want to make sure that the item is released in case it is updated externally and you don't want the Outlook Object Model to end up with a stale object.
Note that Marshal.ReleaseComObject on the mailItem variable will not be enough - you need to be careful about implicit variables, e.g. when you use a multiple dot notation. Some .Net operators also end up with implicit variables, and as is one of them (as well as is):
void Inspectors_NewInspector(Microsoft.Office.Interop.Outlook.Inspector Inspector)
{
object item = Inspector.CurrentItem;
Outlook.MailItem mailItem = item as Outlook.MailItem;
if (mailItem != null)
{
if (mailItem.EntryID == null)
{
mailItem.Subject = "This text was added by using code";
mailItem.Body = "This text was added by using code";
}
Marshal.ReleaseComObject(mailItem);
}
Marshal.ReleaseComObject(item);
}
I created an adjoining Form Region for Outlook appointment items (Using Visual Studio 2019 for Office 2019) and I have inputs on that form from which I capture the values into UserProperties.
Everything works fine until I get to the point where I have added some recipients, sent the meeting request, open it again, remove a recipient, and then try to send an update.
The problem is that on the close event of the form region, I retrieve all my input fields' values, store them in UserProperties, and then fire the item's .Save() method, which throws an exception.
When this happens, my UserPropery values are not saved, but the appointments body and the recipient list does indeed save.
When the appointment item is being deleted, the save method will also throw an exception which is understandable. I wrapped it in a try..catch for that.
An example of my code:
private void AdditionalAppointmentInfo_FormRegionClosed(object sender, System.EventArgs e)
{
if (customItem) //In the FormRegionShowing Event, I check for UserProps to see if this is an item created by this add-in
{
try
{
item.UserProperties["MyUserProperty"].Value = MyTextBox.Text;
item.Save(); //Throws exception here when a recipient gets removed.
}
catch (System.Exception ex){}
}
else
{
try
{
item.UserProperties.Add("IsCustomItem", OlUserPropertyType.olText).Value="1"; //set a property to indicate that this item was created using this add-in
item.UserProperties.Add("MyUserProperty", OlUserPropertyType.olText).Value = MyTextBox.Text;
}
catch (System.Exception){}
}
}
I can understand an exception when the appointment item gets deleted, but not one when you remove a recipient.
So while UserProperties works for my intended purposes, is it even the right thing to be using if you want a FormRegion controls' values to persist? Or is there some other way to store persistent user data without having to manually trigger the save method?
You must use the OutlookItem property to get an Outlook item.
Microsoft has a Walkthrough for a simple VSTO 2010 Outlook Add-In that perfectly illustrates a problem I am seeing in a more complex Add-In that I support. Here is a link to the Walkthrough:
FirstOutlookAddin Walkthrough
and here is the code from the walkthrough that I have copied into a C# VSTO 2010 Outlook Add-In project:
using Outlook = Microsoft.Office.Interop.Outlook;
using Office = Microsoft.Office.Core;
namespace FirstOutlookAddIn
{
public partial class ThisAddIn
{
private Outlook.Inspectors inspectors;
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
inspectors = this.Application.Inspectors;
inspectors.NewInspector +=
new Microsoft.Office.Interop.Outlook
.InspectorsEvents_NewInspectorEventHandler(Inspectors_NewInspector);
}
private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
{
}
void Inspectors_NewInspector(Microsoft.Office.Interop.Outlook.Inspector Inspector)
{
Outlook.MailItem mailItem = Inspector.CurrentItem as Outlook.MailItem;
if (mailItem != null)
{
if (mailItem.EntryID == null)
{
mailItem.Subject = "Added Text";
mailItem.Body = "Added Text to Body";
}
}
}
#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
}
}
The problem that I have is in using this code in combination with Entrust to send an encrypted email, and then recalling the email message and re-sending it after edit. Once I re-send it (after the recall and edit) it corrupts the message, and when I try to open it I get the following error:
Sorry, we're having trouble opening this item. this could be
temporary, but if you see it again you might want to restart Outlook.
An error occurred in the underlying security system. An internal error
occurred.
I'm almost certain that the problem with this is that one or more of the objects being used locally isn't getting cleaned up automatically by the Garbage Collector, but I'm not sure how to force the Garbage Collector (GC) to clean this up so it will work properly. I've been trying to set the local objects to null, and have found some posts that discuss calling:
GC.Collect();
GC.WaitForPendingFinalizers();
And have been trying that too, but have had no luck so far. Can anyone provide some pointers on how to fix this problem?
You need to call the GC twice if you want to get unused COM objects swiped from the heap. For example:
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
But the better way is to use System.Runtime.InteropServices.Marshal.ReleaseComObject to release an Outlook object when you have finished using it. This is particularly important if your add-in attempts to enumerate more than 256 Outlook items in a collection that is stored on a Microsoft Exchange Server. Then set a variable to Nothing in Visual Basic (null in C#) to release the reference to the object. You can read more about that in the Systematically Releasing Objects article in MSDN.
My C# application needs to work close to Outlook. I've implemented a method that allows to load Outlook if it is not on yet. After this loading method I need to start working with emails, read Inbox folder etc. . . obviously, all these behaviours must be executed when Outlook is on, in particular if the main Outlook windows is ready.
Looking at the OOM I've found the application Startup event, and I think I could use it to be sure the application is ready...but I don't know how to use it.
To share the idea of what I'm trying to do, here there is the code (simplified):
Main:
OutlookProvider p= new OutlookProvider();
p.Connect();
if(p.AppIsOn) {
// TO DO: start working
}
else
throw new Exception("Error; Unable to connect to Outlook.");
OutlookProvider class:
#region Fields
Outlook.Application oApp;
Outlook.MailItem oMail;
bool AppIsOn;
#endregion
OutlookProvider() { AppIsOn= false; }
Connect() {
try {
// try to connect to the possible running Outlook instance
oApp = (Outlook.Application)Marshal.GetActiveObject("Outlook.Application");
AppIsOn= true;
}
catch(Exception exc) {
// Outlook is not running, so I create my own Outlook instance
// here my app is null so an Exception will be thrown
oApp.Startup += new Outlook.ApplicationEvents_11_StartupEventHandler(SetAppIsOn);
oApp= (Outlook.Application)new Outlook.Application();
}
}
void SetAppIsOn() { AppIsOn= true;}
Can I use that event to solve my problem? And if yes how can I implement the Connect() method in order to set my boolean AppIsOn variable?
First of all, you ned to create an instance of the object and only then try to subscribe to the events. You can't set up an event handler when the object is null (not yet initialized). So, the code should look like the following one:
oApp= (Outlook.Application)new Outlook.Application();
oApp.Startup += new Outlook.ApplicationEvents_11_StartupEventHandler(SetAppIsOn);
Anyway, there is no need to handle the Startup event if you automate the host application. Take a look at the C# app automates Outlook (CSAutomateOutlook) sample application which explains how to automate Outlook from C#. The Outlook object model is not asynchronous, so each method or property will take as much time as it needs.
P.S. There is no method to initialize Outlook. The Logon method is only used to log on to a specific profile when Outlook is not already running. If Outlook is not running and you only want to start Outlook with the default profile, do not use the Logon method.
To make sure Outlook is fully initialized, call Namespace.Logon. If Outlook is already running, the call will do nothing.
There is also no reason to call GetActiveObject - Outlook is a singleton, so creating a new object will return the existing object if Outlook is already running.
olApp = new Outlook.Application();
Outlook.Namespace ns = olApp.GetNamespace("MAPI");
ns.Logon();
I'm currently developing an Add-in for MS Outlook 2010 using VS 2013. I am using the NewMailEx event handler to give me the EntryID of new email arriving.
Getting the EntryIDCollection is no issue (and I make sure it's a single EntryID before moving further), but I am unable to use the given ID to find the actual email object (using GetItemFromID()) so that I can access the new email's body. Instead, I just end up with an empty MailItem object after GetItemFromID() is called.
public static Outlook.NameSpace olNameSpace;
...
//EVENT: Item Received (new email arrival)
private static void outLookApp_NewMailEx(string EntryIDCollection)
{
try
{
//THIS part is failing \/ and returning nothing
object item = olNameSpace.GetItemFromID(EntryIDCollection, Type.Missing);
Outlook.MailItem mailItem = item as Outlook.MailItem;
if (mailItem != null)
{
//access email object
}
}
catch (Exception e)
{
MessageBox.Show("Receive mail exception: " + e);
}
}
Do I need to specify the folder for GetItemFromID() even though it's optional?
I'm open to other processes for accessing new email arrivals, but I was just using NewMailEx for the time being to have a simple running example.
Thanks everybody.
Are you sure it is GetItemFromID that returns null? If there is a problem, it will raise an exception rather than return null. Most likely it is the next line (Outlook.MailItem mailItem = item as Outlook.MailItem) that returns null when you cast the returned object to MailItem.
Make sure you are not dealing with ReportItem or MeetingItem objects.
The problem here is the object olNameSpace that is not initialized. Try this:
private void outLookApp_NewMailEx(string EntryIDCollection)
{
olNameSpace = this.Application.GetNamespace("MAPI");
try
{
//THIS part is failing \/ and returning nothing
object item = olNameSpace.GetItemFromID(EntryIDCollection, Type.Missing);
Outlook.MailItem mailItem = item as Outlook.MailItem;
if (mailItem != null)
{
mailItem.Display();
}
}
catch (Exception e)
{
MessageBox.Show("" + e);
}
}
Witty,
What is the actual value passed to the GetItemFromID method?
Do I need to specify the folder for GetItemFromID() even though it's optional?
No, there is no need to specify the second paramter. It is optional.
I'd suggest reading the following series of articles related to handling incoming emails in Outlook:
Handling NewMail, NewMailEx and ItemAdd Outlook events in .NET
Outlook NewMail event unleashed: the challenge (NewMail, NewMailEx, ItemAdd)
Outlook NewMail event: solution options
Outlook NewMail event and Extended MAPI: C# example
Outlook NewMail unleashed: writing a working solution (C# example)
Thanks all for your help.
Ultimately, I couldn't get the GetItemFromID method to work consistently in conjunction with NewMailEx.
However, I do recommend implementing Eugene's recommendation here:
https://www.add-in-express.com/creating-addins-blog/2011/11/10/outlook-newmail-custom-solution/
This consistent scanning of folders works quite well (with 100% dependability in my experimentation). However, if you do experience any latency in using this with Outlook, I recommend increasing the sync delay time and possibly using event handlers such as Folder.Items.ItemAdd and Folders.FolderAdd/FolderChange/FolderRemove to check in between syncs.
My add-in needs to ensure quick processing of new email (for security scanning of links), so it cannot just solely wait in between syncs (in case a link is not scanned before the user reached it), which is why I recommend those other event handlers.
Thanks again,
~B-Witty