c# - Programmatically remove attachments from outlook email - c#

I want to remove all attachments form a mail in outlook. Don't know, what I'm doing wrong. The code does not cause an exception, but the attachments are still available after removing. This is my code:
This gives me an outlook.application object if it is running or is running outlook, if it's not running:
public static OL.Application GetOutlook(out bool StillRunning)
{
OL.Application OLApp = null;
if (System.Diagnostics.Process.GetProcessesByName("OUTLOOK").Count() > 0)
{
StillRunning = true;
return System.Runtime.InteropServices.Marshal.GetActiveObject("Outlook.Application") as Microsoft.Office.Interop.Outlook.Application;
}
else
{
StillRunning = false;
OLApp = new OL.Application();
OL.NameSpace nameSpace = OLApp.GetNamespace("MAPI");
nameSpace.Logon("", "", System.Reflection.Missing.Value, System.Reflection.Missing.Value);
nameSpace = null;
return OLApp;
}
}
This function returns a mail by its EntryID:
public static OL.MailItem GetMailByEntryId(OL.Application OlApp, string MailItemEntryID)
{
OL.NameSpace olNS = null;
object obj = null;
olNS = OlApp.GetNamespace("MAPI");
if (olNS == null) { throw new System.Exception("ERROR: Unable to get Namespace 'MAPI' in Outlook.Application object!"); }
OL.MailItem MI = null;
obj = olNS.GetItemFromID(MailItemEntryID);
if (obj != null && obj is OL.MailItem) { MI = obj as OL.MailItem; }
if (MI == null) { throw new System.Exception("ERROR: Unable to get mail item by ID " + System.Environment.NewLine + MailItemEntryID); }
return MI;
}
Here, I try to remove the attachments of the mail:
public static void RemoveAttachments(string EntryID)
{
bool StillRunning = false;
OL.Application OLApp = GetOutlook(out StillRunning);
OL.MailItem MI = GetMailByEntryId(OLApp, EntryID);
for(int i = 0; i < MI.Attachments.Count; i++) { MI.Attachments.Remove(i); } //Methode Delete() not available...
MI.Save();
if (!StillRunning) { OLApp.Quit(); OLApp = null; System.GC.Collect(); KillOutlook(); }
}
Thank you all for your help...

All collections in OOM (including MailItem.Attachments) are 1 based, not 0. You are also modifying the collection while looping - use a down loop:
Attachments attachments = MI.Attachments;
for(int i = attachments.Count; i >= 1; i--) { Attachments.Remove(i); }

Ahh, got it - You can make it work that way:
foreach(OL.Attachment Att in MI.Attachments){Att.Delete();}

Related

System.Runtime.InteropServices.InvalidComObjectException: COM object that has been separated from its underlying RCW cannot be used

I have an VSTO Outlook Add-in. In the compose windows I have a button which removes all the recipients that satify a condition.
Below the code of the button click event:
private void ClickButton(object sender, System.Windows.RoutedEventArgs e)
{
List<Outlook.Recipient> toList = new List<Outlook.Recipient>();
List<Outlook.Recipient> CCList = new List<Outlook.Recipient>();
List<Outlook.Recipient> BCCList = new List<Outlook.Recipient>();
Outlook.Recipient recipient = null;
Outlook.Recipients recipients = this.MyMailItem?.Recipients;
for (int i = recipients?.Count ?? 1; i > 0; i -= 1)
{
recipient = recipients[i];
if (!this.recipientsList.Contains(recipient))
{
if (recipient.Type == (int)Outlook.OlMailRecipientType.olTo)
{
toList.Add(recipient);
}
else if (recipient.Type == (int)Outlook.OlMailRecipientType.olCC)
{
CCList.Add(recipient);
}
else if (recipient.Type == (int)Outlook.OlMailRecipientType.olBCC)
{
BCCList.Add(recipient);
}
}
Marshall.ReleaseComObject(recipient);
recipient = null;
}
this.MyMailItem.To = null;
this.MyMailItem.CC = null;
this.MyMailItem.BCC = null;
if (toList != null && toList.Count > 0)
{
MyMailItem.To = string.Join(";", this.GetRecipientsAsString(toList));
}
if (CCList != null && CCList.Count > 0)
{
MyMailItem.CC = string.Join(";", this.GetRecipientsAsString(CCList));
}
if (BCCList != null && BCCList.Count > 0)
{
MyMailItem.BCC = string.Join(";", this.GetRecipientsAsString(BCCList));
}
this.recipientsList.Clear();
}
Note that recipientsList is a global variable of type List<Outlook.Recipient>.
private List<string> GetRecipientsAsString(List<Outlook.Recipient> recipientsList)
{
List<string> recList = null;
if (recipientsList?.Count > 0)
{
recList = new List<string>();
foreach (Outlook.Recipient recipient in recipientsList)
{
recList.Add(string.IsNullOrWhiteSpace(recipient.Name) ? recipient.Address : recipient.Name);
}
}
return recList;
}
Sometimes, not always, i am receiving below exception:
COM object that has been separated from its underlying RCW cannot be used.
This is thrown in GetRecipientsAsString method at this line:
recList.Add(string.IsNullOrWhiteSpace(recipient.Name) ? recipient.Address : recipient.Name);
What am I doing wrong?
Keeping Outlook COM objects in the collection is the source of problems. For example, when you set the To, Cc or Bcc properties the Recipients collection is updated, i.e. old recipient instances are destroyed and new ones are created/added. So, your Recipient objects stored in a list are getting obsolete and calling any properties or methods in the GetRecipientsAsString function could lead to exceptions like yours.
Instead, I'd recommend keeping a list of email addresses or names. In that case you can re-create a recipient instance if required by using the CreateRecipient function of the Namespace class.

SyncObjects does not contain definition for "Item" Outlook

Trying to use a function to refresh my outlook mailbox (receive any new mail). I am getting SyncObjects does not contain definition for "Item". on line syncObj = syncObjs.Item(i); What is the issue with my code? This example I found online.
private void AlternativeWay()
{
Microsoft.Office.Interop.Outlook.NameSpace ns = null;
Microsoft.Office.Interop.Outlook.SyncObjects syncObjs = null;
Microsoft.Office.Interop.Outlook.SyncObject syncObj = null;
try
{
//ns = OutlookApp.GetNamespace("MAPI");
ns = mapiNameSpace;
syncObjs = ns.SyncObjects;
for (int i = 1; syncObjs.Count >= i; i++)
{
syncObj = syncObjs.Item(i);
if (syncObj != null)
{
syncObj.Start();
Marshal.ReleaseComObject(syncObj);
}
}
}
catch (Exception ex)
{
//System.Windows.Forms.MessageBox.Show(ex.Message);
}
finally
{
if (syncObjs != null) Marshal.ReleaseComObject(syncObjs);
if (ns != null) Marshal.ReleaseComObject(ns);
}
}
In .net based programming languages you need to use indexers instead:
private void AlternativeWay()
{
Microsoft.Office.Interop.Outlook.NameSpace ns = null;
Microsoft.Office.Interop.Outlook.SyncObjects syncObjs = null;
Microsoft.Office.Interop.Outlook.SyncObject syncObj = null;
try
{
//ns = OutlookApp.GetNamespace("MAPI");
ns = mapiNameSpace;
syncObjs = ns.SyncObjects;
for (int i = 1; syncObjs.Count >= i; i++)
{
syncObj = syncObjs[i];
if (syncObj != null)
{
syncObj.Start();
Marshal.ReleaseComObject(syncObj);
}
}
}
catch (Exception ex)
{
//System.Windows.Forms.MessageBox.Show(ex.Message);
}
finally
{
if (syncObjs != null) Marshal.ReleaseComObject(syncObjs);
if (ns != null) Marshal.ReleaseComObject(ns);
}
}

Convert x500 email address into a smtp address

I am developing an outlook add-in using add-in express. When I get a outbox mail item, I can see my email address under "mailItem.SenderEmailAddress" as in x500 format. Is there a way to convert it as a SMTP email address.
Here is my code :
Outlook.Explorer expl = null;
Outlook.Selection selection = null;
Outlook.MailItem mailItem = null;
Outlook.Recipient recipient = null;
string recipientsEmail = "";
try
{
expl = Globals.ObjOutlook.ActiveExplorer() as Outlook.Explorer; ;
if (expl != null)
{
selection = expl.Selection;
if (selection.Count == 1)
{
if (selection[1] is Outlook.MailItem)
{
mailItem = (selection[1] as Outlook.MailItem);
if (mailItem != null)
{
string senderEmailAddress = mailItem.SenderEmailAddress;
What I have tried and Succeeded :- I know how to convert x500 type to SMTP using a Outlook.Recipient object. There I can get the "addressEntry" of the Recipient and obtain ExhangeUser, then go for the "PrimarySmtpAddress" of ExhangeUser. But I am not quiet sure about how to deal with SenderEmailAddress and convert it as SMTP.
Some interesting experiments :-
Since the property of "Sender" is not available in the current mailitem object, I managed to use reflection to get the property of "Sender". But When I run following code the "propertyInfo" object value is getting null. I can't understand why.
Here is the code
//...
if (mailItem != null)
{
var mailItem2 = GetNewObject(mailItem, "Sender", intArray);
//...
public static object GetNewObject(Outlook.MailItem o, string popertyName, object[] parameters)
{
try
{
PropertyInfo propertyInfo = o.GetType().GetProperty(popertyName); // This object is getting null
return propertyInfo.GetType().GetProperty(popertyName).GetValue(o,parameters) as Outlook.MailItem;
}
catch (MissingMethodException ex)
{
// discard or do something
Debug.DebugMessage(2, "AddinModule : Error in GetNewObject() : " + ex.Message);
return null;
}
}
Please advice me. Thank you.
You can obtain the AddressEntry of the sender using the Sender property like this:
Outlook.AddressEntry senderAddressEntry = mailItem.Sender;
After reading the comment from "Dmitry Streblechenko" I was able to develop a fully working solution for my self. Here is the code
private void adxRibBtnAddEmailAddress_OnClick(object sender, IRibbonControl control, bool pressed)
{
Outlook.MailItem mailItem = null;
Outlook.Recipient recipient = null;
string recipientsEmail = "";
string sendersEmail = "";
try
{
mailItem = OutlookHelper.GetSelectedMailItem();
if (mailItem != null)
{
if (mailItem.SenderEmailType == "EX")
sendersEmail = GetSenderEmailAddress(mailItem);
else
sendersEmail = mailItem.SenderEmailAddress;
if (!string.IsNullOrEmpty(sendersEmail))
{
if (sendersEmail == Globals.OLCurrentUserEmail) // Sent mail
{
recipient = mailItem.Recipients[1];
if (recipient != null)
{
recipientsEmail = OutlookHelper.GetAddress(ref recipient);
if (!string.IsNullOrEmpty(recipientsEmail))
{
// Do Something
}
}
}
else // inbox mail
{
// Do Something
}
}
}
}
catch (Exception ex)
{
Debug.DebugMessage(2, "AddinModule : Error in adxRibBtnsddemailaddress_OnClick() : " + ex.Message);
}
finally
{
if (recipient != null) Marshal.ReleaseComObject(recipient);
if (mailItem != null) Marshal.ReleaseComObject(mailItem);
Cursor.Current = Cursors.Default;
}
}
private string GetSenderEmailAddress(Outlook.MailItem oM)
{
Outlook.PropertyAccessor oPA = null;
Outlook.AddressEntry oSender = null;
Outlook.ExchangeUser oExUser = null;
string SenderID;
string senderEmailAddress;
try
{
// Create an instance of PropertyAccessor
oPA = oM.PropertyAccessor;
// Obtain PidTagSenderEntryId and convert to string
SenderID = oPA.BinaryToString(oPA.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x0C190102"));
// Obtain AddressEntry Object of the sender
oSender = Globals.ObjNS.GetAddressEntryFromID(SenderID);
oExUser = oSender.GetExchangeUser();
senderEmailAddress = oExUser.PrimarySmtpAddress;
return senderEmailAddress;
}
catch (Exception ex)
{
Debug.DebugMessage(2, "AddinModule : Error in adxRibBtnAddGenInteraction_OnClick() : " + ex.Message);
return null;
}
finally
{
if (oExUser != null) Marshal.ReleaseComObject(oExUser);
if (oSender != null) Marshal.ReleaseComObject(oSender);
if (oPA != null) Marshal.ReleaseComObject(oPA);
}
}

C# Outlook AddIn 2010 cannot read UserDefinedProperty

I have defined four custom user properties and i can not access to the in order to get their data, custom properties are present in outlook appointment but i can get to them :
My c# code below :
Outlook.ItemProperties itemProp = appointmentItem.ItemProperties;
foreach (Outlook.ItemProperty userprop in itemProp)
{
if (userprop.IsUserProperty)
{
MessageBox.Show(userprop.Name + "\t" + userprop.Value);
}
}
I have used an api(JWebServices) that creates appointment event into Outlook(2007 my version), in my code below i have created some CustomProperties, called in C# Addin Outlook UserPropertie.
I still can get the value of custom users properties that i have defined from the java side
PropertyName myRdvTypePropertyName = new PropertyName("RdvType", StandardPropertySet.PUBLIC_STRINGS);
ExtendedProperty myRdvTypeExtendedProperty = new ExtendedProperty(myRdvTypePropertyName, event.getTyperdv());
appointment.getExtendedProperties().add(myRdvTypeExtendedProperty);
PropertyName myRdvEmplacementPropertyName = new PropertyName("RdvEmplacement", StandardPropertySet.PUBLIC_STRINGS);
ExtendedProperty myRdvEmplacementExtendedProperty = new ExtendedProperty(myRdvEmplacementPropertyName, event.getLieu());
appointment.getExtendedProperties().add(myRdvEmplacementExtendedProperty);
PropertyName myRdvAdressePropertyName = new PropertyName("RdvAdresse", StandardPropertySet.PUBLIC_STRINGS);
ExtendedProperty myRdvAdresseExtendedProperty = new ExtendedProperty(myRdvAdressePropertyName, event.getEvent_location());
appointment.getExtendedProperties().add(myRdvAdresseExtendedProperty);
To be sure the custom user properties are created, i use Outlook SPY
the screenshot below:
the code below in the FormRegionShowing :
if (appointmentItem.UserProperties["RdvType"] != null)
{
this.TypeRdvComboBox.SelectedItem = appointmentItem.UserProperties["RdvType"].Value;
}
if (appointmentItem.UserProperties["RdvEmplacement"] != null)
{
this.emplacementRdvComboBox.SelectedItem = appointmentItem.UserProperties["RdvEmplacement"].Value;
}
if (appointmentItem.UserProperties["RdvAdresse"] != null)
{
this.adresseTextBox.Text = (string)appointmentItem.UserProperties["RdvAdresse"].Value;
}
Thanks for the inspiration, this works:
Outlook.MailItem mItem = (Outlook.MailItem)item;
string udpName = "";
string udpValueString = "";
Debug.Print(" mItem.UserProperties.Count: " + mItem.UserProperties.Count);
for (int i = 1; i <= mItem.UserProperties.Count; i++) {
udpName = mItem.UserProperties[i].Name;
var udpValue = mItem.UserProperties[i].Value;
udpValueString = udpValue.ToString();
Debug.Print(i + ": " + udpName + ": " + udpValueString);
}
Try using the UserProperties property and UserProperties class instead. Here is what MSDN states:
If you use UserProperties.Find to look for a custom property and the call succeeds, it will return a UserProperty object. If it fails, it will return Null. If you use UserProperties.Find to look for a built-in property, specify False for the Custom parameter. If the call succeeds, it will return the property as a UserProperty object. If the call fails, it will return Null. If you specify True for Custom, the call will not find the built-in property and will return Null.
using System.Runtime.InteropServices;
// ...
private void ShowUserProperties(Outlook.MailItem mail)
{
Outlook.UserProperties mailUserProperties = null;
Outlook.UserProperty mailUserProperty = null;
StringBuilder builder = new StringBuilder();
mailUserProperties = mail.UserProperties;
try
{
for (int i = 1; i < = mailUserProperties.Count; i++)
{
mailUserProperty = mailUserProperties[i];
if (mailUserProperty != null)
{
builder.AppendFormat("Name: {0} \tValue: {1} \n\r",
mailUserProperty.Name, mailUserProperty.Value);
Marshal.ReleaseComObject(mailUserProperty);
mailUserProperty = null;
}
}
if (builder.Length > 0)
{
System.Windows.Forms.MessageBox.Show(builder.ToString(),
"The UserProperties collection");
}
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show(ex.Message);
}
finally
{
if (mailUserProperties != null)
Marshal.ReleaseComObject(mailUserProperties);
}
}
Read more about that in the How To: Get Outlook e-mail item’s custom properties – C# and VB.NET samples article.

Check if COM+ application is already running?

Is it possible in C# (4.0) to get the list of installed Com+ applications on the same box and then retrieve the status (Running/Shut down) of each of them?
I can find methods to start/stop, but not retrieve status.
I think the COM+ Administrative components only lets you query the "static" configuration properties (e.g. Identity, IsEnabled) and doesn't let you query the dynamic properties of COM+ (e.g. PID).
The only way I found to do what you want is using the COMSVCSLib (COM+ Services Type Library).
UPDATE: Based on #Vagaus's comment, we can use either COM+ Administrative components or COM+ Services Type Library!
With quite a bit of help from the article Comonitor - A COM+ Monitor. I've cobbled together some code that uses COM+ Services Type Library:
public static bool IsComPlusApplicationRunning(string appName)
{
int appDataSize = Marshal.SizeOf(typeof(COMSVCSLib.appData));
Type appDataType = typeof(COMSVCSLib.appData);
uint appCount;
IntPtr appDataPtr = IntPtr.Zero;
GCHandle gh = GCHandle.Alloc(appDataPtr, GCHandleType.Pinned);
IntPtr addressOfAppDataPtr = gh.AddrOfPinnedObject();
COMSVCSLib.IGetAppData getAppData = null;
COMSVCSLib.TrackerServer tracker = null;
try
{
tracker = new COMSVCSLib.TrackerServerClass();
getAppData = (COMSVCSLib.IGetAppData)tracker;
getAppData.GetApps(out appCount, addressOfAppDataPtr);
appDataPtr = new IntPtr(Marshal.ReadInt32(addressOfAppDataPtr));
for (int appIndex = 0; appIndex < appCount; appIndex++)
{
COMSVCSLib.appData appData = (COMSVCSLib.appData)Marshal.PtrToStructure(
new IntPtr(appDataPtr.ToInt32() + (appIndex * appDataSize)),
appDataType);
string currentAppName = GetPackageNameByPID(appData.m_dwAppProcessId);
if (string.Compare(currentAppName, appName, StringComparison.OrdinalIgnoreCase) == 0)
{
Console.WriteLine("Application " + appName + " is running with PID " + appData.m_dwAppProcessId);
return true;
}
}
}
finally
{
Marshal.FreeCoTaskMem(appDataPtr);
if (tracker != null)
{
Marshal.ReleaseComObject(tracker);
}
gh.Free();
}
return false;
}
private static string GetPackageNameByPID(uint PID)
{
COMSVCSLib.MtsGrp grpObj = new COMSVCSLib.MtsGrpClass();
try
{
object obj = null;
COMSVCSLib.COMEvents eventObj = null;
for (int i = 0; i < grpObj.Count; i++)
{
try
{
grpObj.Item(i, out obj);
eventObj = (COMSVCSLib.COMEvents)obj;
if (eventObj.GetProcessID() == PID)
{
return eventObj.PackageName;
}
}
finally
{
if (obj != null)
{
Marshal.ReleaseComObject(obj);
}
}
}
}
finally
{
if (grpObj != null)
{
Marshal.ReleaseComObject(grpObj);
}
}
return null;
}
But we can also use the COM+ Administrative components (which seems simpler) to do the same thing:
public static bool IsComPlusApplicationRunning(string appName)
{
COMAdmin.COMAdminCatalog catalog = new COMAdmin.COMAdminCatalogClass();
COMAdmin.ICatalogCollection appCollection = (COMAdmin.ICatalogCollection)catalog.GetCollection("Applications");
appCollection.Populate();
Dictionary<string, string> apps = new Dictionary<string, string>();
COMAdmin.ICatalogObject catalogObject = null;
// Get the names of the applications with their ID and store for later
for (int i = 0; i < appCollection.Count; i++)
{
catalogObject = (COMAdmin.ICatalogObject)appCollection.get_Item(i);
apps.Add(catalogObject.get_Value("ID").ToString(), catalogObject.Name.ToString());
}
appCollection = (COMAdmin.ICatalogCollection)catalog.GetCollection("ApplicationInstances");
appCollection.Populate();
for (int i = 0; i < appCollection.Count; i++)
{
catalogObject = (COMAdmin.ICatalogObject)appCollection.get_Item(i);
if (string.Compare(appName, apps[catalogObject.get_Value("Application").ToString()], StringComparison.OrdinalIgnoreCase) == 0)
{
Console.WriteLine(appName + " is running with PID: " + catalogObject.get_Value("ProcessID").ToString());
return true;
}
}
return false;
}
Have you checked the COM+ administration components? I'd bet that you can find this information using these interfaces.
Best

Categories