I'm trying to use the attachments included in calendar items pulled progmatically.
I have a list of chosen calendar subject lines from a previous dialog box, and while the subject is transferring properly, the body isn't working well (another question altogether) but the attachments aren't working whatsoever.
Here's my foreach loop where the attachments are being placed into an Attachments array for use later:
string[] subjects = new string[dialog.chosen.Count];
string[] bodies = new string[dialog.chosen.Count];
Attachments[] attach = new Attachments[dialog.chosen.Count];
foreach (Outlook.AppointmentItem appt in rangeAppts)
{
foreach (string text in dialog.chosen)
{
if (text == appt.Subject)
{
subjects[i] = appt.Subject;
bodies[i] = Convert.ToString(appt.Body);
attach[i] = appt.Attachments;
i = i + 1;
}
}
}
And then here's where I actually call the method:
sendEmailTemplate(bodies[i], subject, to, "", attachment: attach[i]);
And then the method itself:
public void sendEmailTemplate(string body, string subject, string to, string cc , Attachments attachment = null)
{
Microsoft.Office.Interop.Outlook.Application oApp = new Microsoft.Office.Interop.Outlook.Application();
Microsoft.Office.Interop.Outlook._MailItem oMailItem = (Microsoft.Office.Interop.Outlook._MailItem)oApp.CreateItem(Microsoft.Office.Interop.Outlook.OlItemType.olMailItem);
oMailItem.HTMLBody = body;
oMailItem.Subject = subject;
try
{
oMailItem.Attachments.Add(attachment);
}
catch {}
oMailItem.To = to;
oMailItem.CC = cc;
oMailItem.Display(false);
oMailItem.Application.ActiveInspector().WindowState = Microsoft.Office.Interop.Outlook.OlWindowState.olNormalWindow;
}
I've tried several things, however when I actually go to send the e-mail, I end up getting:
Exception: Member not found. HRESULT: 0x80020003
And then I haven't been able to get anything else to work. The try/catch loop on the method is to prevent the above exception as I was getting that exception regardless of whether or not an attachment was present, and now attachments just aren't being added.
I'm using Interop that comes with Office along with C#. Winforms if that makes a difference.
MailItem.Attachments takes either a string (fully qualified file name), or another Outlook item (MailItem, ContactItem, etc.).
You cannot pass Attachments object as an argument. If you need to copy the attachments, loop through all attachments in the Attachments collection, call Attachment.SaveAsFile for each attachment, pass the file name to MailItem.Attachments.Add, delete thee temporary file.
Related
Hello and thank you in advance for any help you might be able to offer!
I am looking to create a C# add-in for Outlook that will take new emails that are being written and remove the contents of the To: and CC: fields if either exceeds a count of 10 recipients, then display a message box if either has exceeded 10. I've got some code already but I haven't worked with C# in about 7 years, so I'm very rusty and I feel like I might have done something wrong here. I'd greatly appreciate any insight on how to accomplish my goal.
The code:
private bool CheckRecipients(Outlook.MailItem mail)
{
bool retValue = false;
Outlook.Recipients recipients = null;
Outlook.Recipient recipientTo = null;
Outlook.Recipient recipientCC = null;
try
{
recipientTo = mail.recipientTo;
recipientCC = mail.recipientCC;
while(recipientTo.Count > 10)
{
recipientTo.Remove(1);
MessageBox.Show("You have added more than 10 recipients in the To: field. Please limit the To: field to 10 recipients.");
}
while(recipientCC.Count > 10)
{
recipientCC.Remove(1);
MessageBox.Show("You have added more than 10 recipients in the CC: field. Please limit the CC: field to 10 recipients.");
}
retValue = "something here";
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show(ex.Message);
}
finally
{
if (recipientCC != null) Marshal.ReleaseComObject(recipientCC);
if (recipientTo != null) Marshal.ReleaseComObject(recipientTo);
if (recipients != null) Marshal.ReleaseComObject(recipients);
}
return retValue;
}
After writing the code above, I ran the code as an add-in. Nothing happened.
There is no recipientTo and recipientCC properties in the Outlook object model:
recipientTo = mail.recipientTo;
recipientCC = mail.recipientCC;
Most probably the Recipients property is meant. It returns a Recipients collection that represents all the recipients for the Outlook item. Use Recipients(index) where index is the name or index number to return a single Recipient object. The name can be a string representing the display name, the alias, or the full SMTP email address of the recipient.
The Type property of a new Recipient object is set to the default for the associated AppointmentItem, JournalItem, MailItem, or TaskItem object and must be reset to indicate another recipient type. In case of MailItem recipient - one of the following OlMailRecipientType constants: olBCC, olCC, olOriginator, or olTo.
So, you can iterate over all recipients in the code where you could check the Type property and count recipients in each category if required.
You may find the article which I wrote for the technical blog helpful, see How To: Fill TO,CC and BCC fields in Outlook programmatically.
Another approach is to use a string based properties like To, Cc or Bcc that returns or sets a semicolon-delimited string list of display names for the recipients of the Outlook item. These properties contain the display names only, not the actual email addresses.
I am not sure if you can even compile your code - MailItem object does not implement recipientTo or recipientCC properties. It only exposes MailItem.Recipients collection. You can build your own To/CC lists by looping through the MailItem.Recipients collection and checking the Recipient.Type property (olTo/olCC/olBCC) for each recipient entry.
I am currently making an Outlook Add In and I successfully got the attachments from an email.
Here is the attachment:
And here is the method:
public static List<AttachmentResponse> GetAttachments(
AttachmentSampleServiceRequest request)
{
// Create an ExchangeService object, set the credentials and the EWS URL.
var service = new ExchangeService
{
Credentials = new OAuthCredentials(request.AttachmentToken),
Url = new Uri(request.EwsUrl)
};
var result = new List<AttachmentResponse>();
var getAttachmentsResponse =
service.GetAttachments(request.Attachments.Select(attachment => attachment.id).ToArray(),
null,
new PropertySet(BasePropertySet.FirstClassProperties,
ItemSchema.MimeContent));
var getAttachmentResponsesFiltered = getAttachmentsResponse.Where(file =>
file.Result == ServiceResult.Success && file.Attachment.ContentType == "application/pdf");
{
result.AddRange(from attachmentResponse in getAttachmentResponsesFiltered
let fileAttachment = attachmentResponse.Attachment as FileAttachment
let base64String =
Convert.ToBase64String(fileAttachment?.Content, 0, (int) fileAttachment?.Content.Length)
select new AttachmentResponse
{
Id = attachmentResponse.Attachment.Id, Name = attachmentResponse.Attachment.Name,
Type = attachmentResponse.Attachment.ContentType, FileBase64 = base64String
});
}
return result;
}
The Open XML SDK which you could use on the server-side doesn't provide anything for generating PDF files. You may try to use something like iText or iTextSharp or any other third-party components designed for the server-side execution.
In case of COM add-ins there is no need to use EWS at all. Use the Attachments property of Outlook items to deal with attached files and items. The MailItem.Attachments property returns an Attachments object that represents all the attachments for the specified item. You can access attachments in the collection by using the either the index number of the object, or a value used to match the default property of an object in the collection. For example, here is a sample VBA code which illustrates how you could use the Outlook object model from your add-in:
Sub SaveAttachment()
Dim myInspector As Outlook.Inspector
Dim myItem As Outlook.MailItem
Dim myAttachments As Outlook.Attachments
Set myInspector = Application.ActiveInspector
If Not TypeName(myInspector) = "Nothing" Then
If TypeName(myInspector.CurrentItem) = "MailItem" Then
Set myItem = myInspector.CurrentItem
Set myAttachments = myItem.Attachments
'Prompt the user for confirmation
Dim strPrompt As String
strPrompt = "Are you sure you want to save the first attachment in the current item to the Documents folder? If a file with the same name already exists in the destination folder, it will be overwritten with this copy of the file."
If MsgBox(strPrompt, vbYesNo + vbQuestion) = vbYes Then
myAttachments.Item(1).SaveAsFile Environ("HOMEPATH") & "\My Documents\" & myAttachments.Item(1).DisplayName
End If
Else
MsgBox "The item is of the wrong type."
End If
End If
End Sub
The following code is ment to create an e-mail and attach the one or multiple attachemnts. So far the code does just that with success, but it also creates the equal amount of e-mails with the right amount of attachments in each of them. I.e. with three attachments it creates three e-mails all with the three attachments in each of them.
private void SendMail(List<string> paths)
{
DateTime defDt = DateTime.Now.AddMinutes(3);
Microsoft.Office.Interop.Outlook.Application app = new Microsoft.Office.Interop.Outlook.Application();
Microsoft.Office.Interop.Outlook.MailItem mailItem = app.CreateItem(Microsoft.Office.Interop.Outlook.OlItemType.olMailItem);
mailItem.BodyFormat = Microsoft.Office.Interop.Outlook.OlBodyFormat.olFormatHTML;
mailItem.GetInspector.Activate();
var signature = mailItem.HTMLBody;
mailItem.HTMLBody = signature;
foreach (string u in paths)
{
mailItem.Attachments.Add(u);
}
mailItem.DeferredDeliveryTime = defDt;
mailItem.Display(mailItem);
}
Why does it behave like this? There are no foreach loop besides the one for adding the multiple attachments. When firing with one attachment the error doesn't show (probably since it i creating just one e-mail with one attachment as excpected). Any thoughts from this great community?
The code was called in a foreach loop earlier in the code. The code presented works as expected.
Hello I recently got into development around EWS. One of the issue came up to me is that a client ask me to import emails into database and he wants to detect the duplicate based on InternetMessageID this way he doesn't have to import the duplicate emails and my code came up to this point.
private static string GetInternetMessageID(Microsoft.Exchange.WebServices.Data.Item email)
{
EmailMessage emailMsg = email as EmailMessage;
string returnId = string.Empty;
if ((emailMsg != null)) {
try {
emailMsg.Load();
//loads additional info, without calling this ToRecipients (and more) is empty
} catch (ArgumentException ex) {
//retry
email.Load();
}
returnId = emailMsg.InternetMessageId;
} else {
//what to do?
}
return returnId;
}
I can handle regular emails, but for special exchange objects such as contact, Calendar, Posts etc it does not work because it could not cast it to an EmailMessage object.
And I know you can extract the internetMessageId from those objects. Because the client used to have another software that extract this ID for them, maybe the property is not called internetMessageID, I think I probally have to extract it from the internetMessageHeader. However when ever I try to get it from the item object it just throws me an error. How do I get the internet messageID from these "Special" exchange items?
PS i am aware of item.id.UniqueID however that is not what I want as this id changes if I move items from folder to another folder in exchange
Only objects that have been sent via the Transport service will have an InternetMessageId so things like Contacts and Tasks because they aren't messages and have never been routed via the Transport service will never have an Internet MessageId. You probably want to look at using a few properties to do this InternetMessageId can be useful for messages PidTagSearchKey https://msdn.microsoft.com/en-us/library/office/cc815908.aspx is one that can be used (if you good this there are various examples of using this property).
If your going to use it in Code don't use the method your using to load the property on each item this is very inefficient as it will make a separate call for each object. Because these I'd's are under 256 Kb just retrieve then when using FindItems. eg
ExtendedPropertyDefinition PidTagSearchKey = new ExtendedPropertyDefinition(0x300B, MapiPropertyType.Binary);
ExtendedPropertyDefinition PidTagInternetMessageId = new ExtendedPropertyDefinition(0x1035, MapiPropertyType.String);
PropertySet psPropSet = new PropertySet(BasePropertySet.IdOnly);
psPropSet.Add(PidTagSearchKey);
psPropSet.Add(PidTagInternetMessageId);
ItemView ItemVeiwSet = new ItemView(1000);
ItemVeiwSet.PropertySet = psPropSet;
FindItemsResults<Item> fiRess = null;
do
{
fiRess = service.FindItems(WellKnownFolderName.Inbox, ItemVeiwSet);
foreach (Item itItem in fiRess)
{
Object SearchKeyVal = null;
if (itItem.TryGetProperty(PidTagSearchKey, out SearchKeyVal))
{
Console.WriteLine(BitConverter.ToString((Byte[])SearchKeyVal));
}
Object InternetMessageIdVal = null;
if (itItem.TryGetProperty(PidTagInternetMessageId, out InternetMessageIdVal))
{
Console.WriteLine(InternetMessageIdVal);
}
}
ItemVeiwSet.Offset += fiRess.Items.Count;
} while (fiRess.MoreAvailable);
If you need larger properties like the Body using the LoadPropertiesForItems Method https://blogs.msdn.microsoft.com/exchangedev/2010/03/16/loading-properties-for-multiple-items-with-one-call-to-exchange-web-services/
I am trying to make a function that fetches the body of an email with a given search criteria. When I fetch items from the mailbox and try to get the body, it only writes part of the body. I want ALL text from the body instead. How can I do this? This is what I have so far:
Outlook.Application myApp = new Outlook.Application();
const string PR_HAS_ATTACH = "http://schemas.microsoft.com/mapi/proptag/0x0E1B000B";
// Obtain Inbox
Outlook.Folder folder = myApp.Session.GetDefaultFolder(OlDefaultFolders.olFolderInbox) as Microsoft.Office.Interop.Outlook.Folder;
Outlook.Table table = folder.GetTable(Microsoft.Office.Interop.Outlook.OlTableContents.olUserItems);
// Remove default columns
table.Columns.RemoveAll();
// Add using built-in name
table.Columns.Add("Subject");
table.Columns.Add("ReceivedTime");
table.Sort("ReceivedTime", Microsoft.Office.Interop.Outlook.OlSortOrder.olDescending);
// Add using namespace
// Date received
table.Columns.Add("urn:schemas:httpmail:textdescription");
while (!table.EndOfTable)
{
Outlook.Row row = table.GetNextRow();
if (row["Subject"].ToString().ToLower().Contains(subject.Text.ToLower()) && row["ReceivedTime"].ToString().Contains(cellCreationDate))
{
body.Text = row["urn:schemas:httpmail:textdescription"].ToString();
}
}
You can't use Outlook.Table and then urn:shcemas:httpmail.textdescription to get the entire body. textdescription only returns the first 255 characters of the body as noted here http://msdn.microsoft.com/en-us/library/office/ff861580.aspx.
here is an alternative.
// change this in your code
body.Text = row["urn:schemas:httpmail:textdescription"].ToString();
// To this
Microsoft.Office.Interop.Outlook.MailItem mailItem =
myApp.Session.GetItemFromID(row["EntryID"]);
body.Text = mailItem.Body;