UPDATE
Ultimate goal is to send the updated body to the existing recipients of the meeting occurrence.
I'm trying to update HTMLBody of occurrence of existing meeting with number of recipients.
After updated the HTMLBody, recipients count is become zero.
And sending failed with an error saying "message contains no recipients"
Here's the code I've tried.
public static void GetSomething()
{
var entryID = "00000000C97FEB5BB4BE2046B4C77ADEB3C423010700BA6ADFD7533DD244B122D7B9F3B5664000000000010D0000BA6ADFD7533DD244B122D7B9F3B56640000269310F930000";
RDOAppointmentItem appointment = null;
RDORecurrencePattern pattern = null;
RDOMail occurrence = null;
try
{
appointment = rSession.GetMessageFromID(entryID) as RDOAppointmentItem;
if (appointment != null)
{
pattern = appointment.GetRecurrencePattern();
if (pattern != null)
{
occurrence = pattern.GetOccurrence(1);
if (pattern != null) Marshal.ReleaseComObject(pattern);
if (appointment != null) Marshal.ReleaseComObject(appointment);
occurrence.HTMLBody = #"<!DOCTYPE html>\r\n<html>\r\n<head>\r\n<title>Page Title</title>\r\n</head>\r\n<body>\r\n<p>This is a paragraph.</p>\r\n\r\n</body>\r\n</html>";
occurrence.Send();
}
}
}
catch (Exception e)
{
Debug.DebugMessage(2, $"Error in {MethodBase.GetCurrentMethod().Name} | {e.Message}");
}
finally
{
if (occurrence != null) Marshal.ReleaseComObject(occurrence);
}
}
I then tried the approach of adding and removing a recipient which worked but added recipient is not removed from the meeting request sent, although it was removed from the appointment in my calendar.
Here's the code I've tried for that.
public static void GetSomething()
{
var entryID = "00000000C97FEB5BB4BE2046B4C77ADEB3C423010700BA6ADFD7533DD244B122D7B9F3B5664000000000010D0000BA6ADFD7533DD244B122D7B9F3B56640000269310F930000";
RDOAppointmentItem appointment = null;
RDORecurrencePattern pattern = null;
RDOMail occurrence = null;
try
{
appointment = rSession.GetMessageFromID(entryID) as RDOAppointmentItem;
if (appointment != null)
{
pattern = appointment.GetRecurrencePattern();
if (pattern != null)
{
occurrence = pattern.GetOccurrence(1);
if (pattern != null) Marshal.ReleaseComObject(pattern);
if (appointment != null) Marshal.ReleaseComObject(appointment);
RDORecipients oldRecipients = occurrence.Recipients;
var oldRecCount = oldRecipients.Count;
RDORecipient recipient = oldRecipients.AddEx("Test", "test#gmail.com", "SMTP", 1);
if (recipient != null) Marshal.ReleaseComObject(recipient);
if (oldRecipients != null) Marshal.ReleaseComObject(oldRecipients);
occurrence.HTMLBody = #"<!DOCTYPE html>\r\n<html>\r\n<head>\r\n<title>Page Title</title>\r\n</head>\r\n<body>\r\n<p>This is a paragraph.</p>\r\n\r\n</body>\r\n</html>";
RDORecipients newRecipients = occurrence.Recipients;
newRecipients.Remove(oldRecCount + 1);
if (newRecipients != null) Marshal.ReleaseComObject(newRecipients);
occurrence.Send();
}
}
}
catch (Exception e)
{
Debug.DebugMessage(2, $"Error in {MethodBase.GetCurrentMethod().Name} | {e.Message}");
}
finally
{
if (occurrence != null) Marshal.ReleaseComObject(occurrence);
}
}
}
Keep in mind that occurrences do not physically exist - when you ask for an occurrence, Redemption creates a fake appointment object that gets most of its properties (except for the start/end and recurrence) from the master appointment.
When you create an exception (by modifying one of the properties), besides modifying the exception pattern, an appointment is created and added an an embedded message attachment to the master appointment. That appointment stores mostly modified properties. Since the recipients were not modified for the exception, the recipient table is empty.
Related
I am trying to get the sender (From) of an Outlook.MailItem without success. It returns null.
I have tried below.
Attempt 1:
string from = omi.SenderEmailAddress;
Attempt 2:
var exch = omi.Sender.GetExchangeUser(); // it returns null
if (exch != null)
{
string from = exch.PrimarySmtpAddress;
}
Also I have checked omi.SenderEmailType to see what type is, and it returns null.
Note: omi is an Outlook.MailItem object.
Keep in mind that the sender related properties are only populated on the received or already sent messages. If you are accessing these properties on a message being submitted, they won't be present until the message is sent and moved to the Sent Items folder.
You can check out the SendUsingAccount property. If it is not set then you can use the Namespace.CurrentUser property which returns the display name of the currently logged-on user as a Recipient object.
Assuming you have an Outlook.MailItem that is not null, you should be able to follow this process to retrieve the sender's email address, adapted from the Microsoft Documentation.
string PR_SMTP_ADDRESS = #"http://schemas.microsoft.com/mapi/proptag/0x39FE001E";
if (omi == null)
{
throw new ArgumentNullException();
}
if (omi.SenderEmailType == "EX")
{
Outlook.AddressEntry sender = omi.Sender;
if (sender != null)
{
// Now we have an AddressEntry representing the Sender
if (sender.AddressEntryUserType == Outlook.OlAddressEntryUserType.olExchangeUserAddressEntry ||
sender.AddressEntryUserType == Outlook.OlAddressEntryUserType.olExchangeRemoteUserAddressEntry)
{
// Use the ExchangeUser object PrimarySMTPAddress
Outlook.ExchangeUser exchUser = sender.GetExchangeUser();
if (exchUser != null)
{
return exchUser.PrimarySmtpAddress;
}
else
{
return null;
}
}
else
{
return sender.PropertyAccessor.GetProperty(PR_SMTP_ADDRESS) as string;
}
}
else
{
return null;
}
}
else
{
return omi.SenderEmailAddress;
}
I am trying to find a solution to distinguish between an embedded image and attachment in Outlook mail. After doing some research, i found the following code works for most of the case
foreach (Outlook.Attachment attachment in mailItem.Attachments)
{
try
{
var attachmentType = attachment.FileName.Substring(attachment.FileName.LastIndexOf('.'));
if (attachmentType!=null&&attachmentType.Trim().Length>1&&_fileTypeFilter.Contains(attachmentType.Substring(1).ToLower()))
{
prop=attachment.PropertyAccessor;
string conentId = (string)prop.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x3712001E");
if ((attachmentType.Substring(1).ToLower() == "pdf") ||(conentId==null||conentId.Trim().Length==0))
{
//Always allow PDF
// This is an attachement
}
}
}
catch (Exception ex)
{
}
}
The issue is when a mail is send from other mail systems (for eg: hotmail ) then the content id is not null for attachments. This cause the attachments to be ignored .
Another suggestion i tired is to check the property based on following StackFlow don't save embedded
foreach (Outlook.Attachment attachment in mailItem.Attachments)
{
try
{
// var tst = attachment.Type;
var attachmentType = attachment.FileName.Substring(attachment.FileName.LastIndexOf('.'));
if (attachmentType!=null&&attachmentType.Trim().Length>1&&_fileTypeFilter.Contains(attachmentType.Substring(1).ToLower()))
{
prop=attachment.PropertyAccessor;
string conentId = (string)prop.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x3712001E");
var flags = prop.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x37140003");
var asize = attachment.Size;
if ((attachmentType.Substring(1).ToLower() == "pdf") ||
(asize>0&&(flags!=4 &&(int)attachment.Type != 6))) // As per present understanding - If rtF mail attachment comes here - and the embeded image is treated as attachment then Type value is 6 and ignore it
// (conentId==null||conentId.Trim().Length==0))
{
//This is a valid attachment
}
}
}
catch (Exception ex)
{
}
}
But this sometimes includes the image in signature
the most reliable way is to parse the HTML body (MailItem.HTMLBody property) to extract all img tags and check their scr attributes. If its is of the form ""cid:xyz", then "xyz" will be the value of the PR_ATTACH_CONTENT_ID property on the attachment. It can also refer to the graphics file by its file name.
Here is the working solution
var selection = explorer.Selection;
if (selection.Count == 1)
{
object selectedItem = selection[1];
var mailItem = selectedItem as Outlook.MailItem;
if (mailItem == null) return;
foreach (Outlook.Attachment attachment in mailItem.Attachments)
{
bool validAttachment = isAnAttachment(attachment);
}
}
private bool isAnAttachment(Outlook.Attachment attachment)
{
bool isValid = false;
var attachmentType = attachment.FileName.Substring(attachment.FileName.LastIndexOf('.'));
if (attachmentType != null && attachmentType.Trim().Length > 1 && _fileTypeFilter.Contains(attachmentType.Substring(1).ToLower()))
{
Outlook.PropertyAccessor prop = attachment.PropertyAccessor;
var flags = prop.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x37140003");
var asize = attachment.Size;
// As per present understanding - If rtF mail attachment comes here - and the embeded image is treated as attachmet
if ((attachmentType.Substring(1).ToLower() == "pdf") || (asize > 0 && flags != 4 && (int)attachment.Type != 6))
{
isValid = true;
}
}
return isValid;
}
I have in my Outlook 2010-Add-In (c#) many folders. They are in my private post box or in one of my shared post boxes.
Now I am looking for a solution to find out, how to get the right email address (sender / recipient) associated with a dedicated folder. It could be any folder from my private or anyone of my shared post boxes.
I think, maybe I could use the EntryId / StoreId from the folder item to identify the corresponding email address.
I know already, that I could get the email address from any mail item but I'm not looking for this solution.
I like to answer my own questions: I think that I've found a plausible solution.
I do not treat any exceptions inside the function, I do that from outside.
private string GetSMTPAddressByFolderItem(Outlook.MAPIFolder mapiFolder)
{
string PR_MAILBOX_OWNER_ENTRYID = #"http://schemas.microsoft.com/mapi/proptag/0x661B0102";
string PR_SMTP_ADDRESS = #"http://schemas.microsoft.com/mapi/proptag/0x39FE001E";
Outlook.Store store = null;
Outlook.NameSpace ns = null;
Outlook.AddressEntry sender = null;
Outlook._ExchangeUser exchUser = null;
try
{
if (mapiFolder == null)
{
return null;
}
// Get the parent store.
store = mapiFolder.Store;
string storeOwnerEntryId = store.PropertyAccessor.BinaryToString(store.PropertyAccessor.GetProperty(PR_MAILBOX_OWNER_ENTRYID)) as string;
ns = Application.GetNamespace(Constants.OL_NAMESPACE); // i.e. "MAPI"
sender = ns.GetAddressEntryFromID(storeOwnerEntryId);
if (sender != null)
{
if (sender.AddressEntryUserType == Outlook.OlAddressEntryUserType.olExchangeUserAddressEntry ||
sender.AddressEntryUserType == Outlook.OlAddressEntryUserType.olExchangeRemoteUserAddressEntry)
{
exchUser = sender.GetExchangeUser();
if (exchUser != null)
{
return exchUser.PrimarySmtpAddress;
}
else
{
return null;
}
}
else
{
return sender.PropertyAccessor.GetProperty(PR_SMTP_ADDRESS) as string;
}
}
return null;
}
finally
{
if (ns != null)
{
Marshal.ReleaseComObject(ns);
ns = null;
}
if (store != null)
{
Marshal.ReleaseComObject(store);
store = null;
}
if (sender != null)
{
Marshal.ReleaseComObject(sender);
sender = null;
}
if (exchUser != null)
{
Marshal.ReleaseComObject(exchUser);
exchUser = null;
}
}
}
What is the Query results window's global service (interface)? Code below:
var dteService = Package.GetGlobalService(typeof(EnvDTE.DTE)) as EnvDTE.DTE;
if (dteService == null)
{
Debug.WriteLine("");
return;
}
var something=Package.GetGlobalService(typeof(???)) as ???;
EDIT: The goal is, when I press the context menu button, I want the function callback to be able to access the service where the work item is selected (or the results list
Please check this case in MSDN forum for the details how to get it work: https://social.msdn.microsoft.com/Forums/vstudio/en-US/2d158b9c-dec1-4c59-82aa-f1f2312d770b/sdk-packageget-selected-item-from-query-results-list
The following code is quoted from above link for your quick reference:
Document activeDocument = _applicationObject.ActiveDocument;
if (activeDocument != null)
{
DocumentService globalService = (DocumentService)Package.GetGlobalService(typeof(DocumentService));
if (globalService != null)
{
string fullName = activeDocument.FullName;
IWorkItemTrackingDocument document2 = globalService.FindDocument(fullName, null);
if ((document2 != null) && (document2 is IResultsDocument))
{
int[] selectedItemIds = ((IResultsDocument)document2).SelectedItemIds;
}
}
}
var dteService = Package.GetGlobalService(typeof(EnvDTE.DTE)) as EnvDTE.DTE;
if (dteService == null)
{
Debug.WriteLine("");
return;
}
DocumentService documentService = Package.GetGlobalService(typeof(DocumentService)) as DocumentService;
if (documentService == null)
return;
string fullName = dteService.ActiveDocument.FullName;
IWorkItemTrackingDocument activeDocument = documentService.FindDocument(fullName, null);
if (activeDocument == null || !(activeDocument is IResultsDocument))
return;
I got a xml structure as below:
<Users>
<User Code="1" Roles="1,2,3" />
</Users>
I provide a method to search the xml file for retrieving particular user based on code like below
string xpath = "Users/User[#Code="+ Code +"]";
XmlNode user = _xmlDatabase.SelectSingleNode(xpath);
if (user != null)
{
XmlAttributeCollection userMeta = user.Attributes;
if (userMeta != null)
{
int code = int.Parse(Code);
User userInstance = new User(Code, userMeta[1].Value, userMeta[2].Value);
return userInstance;
}
}
i would invoke the method like so
User user = GetUserByCode("1"); & _xmlDatabase is a instance of XmlDocument class. Here is the question,
I get to return null when no matching user is found
Attributes i search for does not exists
It's a fresh file
Hence i modified the method to return "null"only to be complained by compiler that "return statement is missing"
I kind of wanted the end-user to do
User user = GetUserByCode("1");
if(user == null)
Display "No User Found"
please see the comments on below code
if (user != null) // if user == null nothing will return
{
XmlAttributeCollection userMeta = user.Attributes;
if (userMeta != null) // if userMeta == null nothing will return
{
int code = int.Parse(Code);
User userInstance = new User(Code, userMeta[1].Value, userMeta[2].Value);
return userInstance;
}
}
you can solve this as below
public User GetUserByCode(string Code)
{
User userInstance = null;
string xpath = "Users/User[#Code="+ Code +"]";
XmlNode user = _xmlDatabase.SelectSingleNode(xpath);
if (user != null)
{
XmlAttributeCollection userMeta = user.Attributes;
if (userMeta != null)
{
int code = int.Parse(Code);
userInstance = new User(Code, userMeta[1].Value, userMeta[2].Value);
}
}
return userInstance;
}
Above code will return null or userInstance in any case.