none
C# Outlook AppointmentItem - update the RTFBody with regular text RRS feed

  • Question

  • Hey,

    I want to update the body of the sent email - I want to add some text to the end of the body of the email. it is easy to add the text if I update the AppointmentItem.Body - but sometimes, the body is formatted (RTF) and I don't want to loose the formatting - what is the best way to add text to the end of the RTFBody?

    for example, I want to add the string: Bla Bla Bla Bla

    How can I do it?

    and what if I want to find and replace a text in the RTFBody?

    Thanks,

    Thursday, January 2, 2014 11:44 AM

Answers

  • Thanks a lot Dmitry -

    I fixed the issue in this way:

    byte[] bBody = item.RTFBody as byte[];
    				
    // search for the last '}' in the body
    byte b = Convert.ToByte('}');
    int iIndex = Array.LastIndexOf(bBody, b);
    
    if (iIndex > 0)
    {
    	byte[] bFirstPart = bBody.Take(iIndex).ToArray<byte>();
    	byte[] bLastPart = bBody.Skip(iIndex).ToArray<byte>();
    	
    	string str = "THIS IS THE TEST STRING." + Environment.NewLine + " IT SHOULD BE ADDED TO END OF THE MEETING BODY.";
    	str = PlainTextToRtf(str);
    	byte[] bStr = System.Text.Encoding.UTF8.GetBytes(str);
    	
    	var list = new List<byte>();
    	list.AddRange(bFirstPart);
    	list.AddRange(bStr);
    	list.AddRange(bLastPart);
    
    	item.RTFBody = list.ToArray();
    }

    And it works very well in Outlook :-) - but somehow in the Outlook.com or on the mobile phones that images are not shown. But at least, in the MS Outlook 2010 they are shown but in Outlook 2013 they are not visible (there is a dot there). But here, all of us have Outlook 2010 so we can see the images.

    Thanks everyone :).


    Seif.

    • Proposed as answer by Seif Ibrahim Tuesday, January 7, 2014 8:49 AM
    • Marked as answer by iseif Wednesday, January 8, 2014 12:58 PM
    Tuesday, January 7, 2014 7:38 AM

All replies

  • Hello iseif,

    First of all, I would recommend you to take a look at the Chapter 17: Working with Item Bodies in MSDN. It describes possible ways for modifying the Body property of Outlook items.

    The most easiest way is to use the WordEditor property of the Inspector class. The WordEditor property is only valid if the IsWordMail method returns True and the EditorType property is olEditorWord. The returned Word Document object provides access to most of the Word object model.

    Word.Document olDoc = item.GetInspector.WordEditor as Word.Document;

    Note, the inspector with a mail item should be available and visible to a user.

    Also, as a workaround, you can use the PropertyAcessor property of the AppointmentItem class. It returns an instance of the corresponding class which can be used for accessing the PR_RTF_COMPRESSED property. Here is the DASL property tag which can be passed to the GetProperty method "http://schemas.microsoft.com/mapi/proptag/0x10090102".

     


    Thursday, January 2, 2014 12:06 PM
  • Thanks a lot - Now I'm trying to do the following but it does not success (while debuging I can enter the if scope but the text is not updated:

    Microsoft.Office.Interop.Word.Document olDoc = item.GetInspector.WordEditor as Microsoft.Office.Interop.Word.Document;
    
    if (olDoc != null)
    {
    	object start = olDoc.Content.End - 1;
    	object end = olDoc.Content.End;
    
    	Microsoft.Office.Interop.Word.Range rng = olDoc.Range(ref start, ref end);
    	rng.Text = "TESTTTTTTT";
    }

    How can I insert the data to the end of the body?
    Thursday, January 2, 2014 1:10 PM
  • Hi iseif,

    Please take a look at the How to automate Microsoft Word to create a new document by using Visual C# article. It provides a lot of sample code.

    Thursday, January 2, 2014 1:31 PM
  • Thanks but the same as above - everything seems working and I can see in the debug that the paragraph has been created and updated but it does not appear in the email that has been sent.

    i tried to call the MeetingItem.Save() method but the same the body was not updated.

    Thursday, January 2, 2014 1:52 PM
  • I tried to solve the issue with RTF byte arrays as following:

    string str = "THIS IS TEST" + Environment.NewLine + "THIS IS THE END OF THE BODY";
    byte[] b2 = System.Text.Encoding.UTF8.GetBytes(str);
    var z = new byte[item.RTFBody.Length + b2.Length];
    item.RTFBody.CopyTo(z, 0);
    b2.CopyTo(z, item.RTFBody.Length);
    
    item.RTFBody = z;

    it works but the problem is the new lines are omitted - it seems that the GetBytes ignores the Environmet.NewLine.

    Any idea how I can use the NewLines here?

    Thursday, January 2, 2014 2:41 PM
  • Hi iseif,

    For example, I have tested the following code on my machine with Outlook 2013 installed:

    object oEndOfDoc = "\\endofdoc"; /* \endofdoc is a predefined bookmark */ 
    
    Outlook.Inspector insp = OutlookApp.ActiveInspector();
    Word.Document doc = insp.WordEditor as Word.Document;
                 
    Word.Paragraph oPara2;
    object oRng = doc.Bookmarks.get_Item(ref oEndOfDoc).Range;
    oPara2 = doc.Content.Paragraphs.Add(ref oRng);
    oPara2.Range.Text = "Heading 2";
    oPara2.Format.SpaceAfter = 6;
    oPara2.Range.InsertParagraphAfter();

    In the inspector window I can see the body updated. It looks like you need to call the Save method of the AppointmentItem class.

    Finally, I would recommend releasing underlying COM objects instantly. You can read more a bout this in the Systematically Releasing Objects article in MSDN.

    Thursday, January 2, 2014 3:37 PM
  • iseif,

    You can try to use the "\r\n\" or <br> tag.

    Thursday, January 2, 2014 3:41 PM
  • Are you modifying a meeting request in the Application.ItemSend event handler? How are you retrieving the item to be modified?

    Also keep in mind that two RTF strings cannot be simply concatenated - they must be merged.


    Dmitry Streblechenko (MVP)
    http://www.dimastr.com/redemption
    Redemption - what the Outlook
    Object Model should have been
    Version 5.5 is now available!

    Friday, January 3, 2014 4:42 PM
  • Hey Dmitry..

    yes, I want to add the text in the ItemSend mathod - I'm updating the MeetingItem because I want to add some text to the meeting request. Therefore I think that updating the WordEditor is not working.. While merging two Byte arrays is working but it doesn't support the new line "\r\n".. I don't

    know why.

    I have an idea, what if I create a RichTextBox in the run time and copy the RTFBody to it and then add the text I want to add to the body and in the end update the RTFBody with the content of this text box? Do you think it will work for me? I still didn't try it but I just thought about it..


    Seif.


    Friday, January 3, 2014 8:07 PM
  • Hey Eugene,

    I'm updating the MeetingItem in the ItemSend method and I think it doesn't work for me because of that. I tried the Byte arrays issue but somehow it doesn't support the new line chars.

    I have an idea, what if I create a RichTextBox in the run time and copy the RTFBody to it and then add the text I want to add to the body and in the end update the RTFBody with the content of this text box? Do you think it will work for me? I still didn't try it but I just thought about it..


    Seif.

    Friday, January 3, 2014 8:10 PM
  • GetInspector will not work for you in the ItemSend event - you will get the inspector for the parent appointment, but it is not being sent - the meeting item is.

    When you use RTFBody, you cannot concatenate two RTF strings - open an RTF file in Notepad. At the very least you will need to remove the last "}" from the original RTF body, add your RTF string, then add the closing "}".

    \r\n does not work because RTF uses "\par " as a new line tag.


    Dmitry Streblechenko (MVP)
    http://www.dimastr.com/redemption
    Redemption - what the Outlook
    Object Model should have been
    Version 5.5 is now available!

    Friday, January 3, 2014 8:59 PM
  • You can give it a try. But I am not an RTF guru.
    Friday, January 3, 2014 9:02 PM
  • Hey,

      I solved the issue by using a RichTextBox in this way:

    string str = "THIS IS THE TEST STRING." + Environment.NewLine + " IT SHOULD BE ADDED TO END OF THE MEETING BODY.";
    
    RichTextBox rtb = new RichTextBox();
    rtb.Rtf = System.Text.Encoding.UTF8.GetString(item.RTFBody); ;
    rtb.Select(rtb.TextLength, 0);
    rtb.SelectedRtf = PlainTextToRtf(str);;
    
    item.RTFBody = System.Text.Encoding.UTF8.GetBytes(rtb.Rtf);
    

    I used the following method I found in the internet that convert a plain text to basic RTF:

    private string PlainTextToRtf(string plainText)
    {
    	if (string.IsNullOrEmpty(plainText))
    		return "";
    
    	string escapedPlainText = plainText.Replace(@"\", @"\\").Replace("{", @"\{").Replace("}", @"\}");
    	escapedPlainText = EncodeCharacters(escapedPlainText);
    
    	string rtf = @"{\rtf1\ansi\ansicpg1250\deff0{\fonttbl\f0\fswiss Helvetica;}\f0\pard ";
    	rtf += escapedPlainText.Replace(Environment.NewLine, "\\par\r\n ");
    	rtf += " }";
    	return rtf;
    }

    Now, everything is Okay :-) - but, I got another issue - if the meeting request has an image in the body - it got lost :-( - I receive a meeting request with image attached but it is a blank image (only 1KB).

    Any idea about this issue?


    Seif.

    Sunday, January 5, 2014 8:59 AM
  • Hey,

      I solved the issue by using a RichTextBox in this way:

    string str = "THIS IS THE TEST STRING." + Environment.NewLine + " IT SHOULD BE ADDED TO END OF THE MEETING BODY.";
    
    RichTextBox rtb = new RichTextBox();
    rtb.Rtf = System.Text.Encoding.UTF8.GetString(item.RTFBody); ;
    rtb.Select(rtb.TextLength, 0);
    rtb.SelectedRtf = PlainTextToRtf(str);;
    
    item.RTFBody = System.Text.Encoding.UTF8.GetBytes(rtb.Rtf);
    

    I used the following method I found in the internet that convert a plain text to basic RTF:

    private string PlainTextToRtf(string plainText)
    {
    	if (string.IsNullOrEmpty(plainText))
    		return "";
    
    	string escapedPlainText = plainText.Replace(@"\", @"\\").Replace("{", @"\{").Replace("}", @"\}");
    	escapedPlainText = EncodeCharacters(escapedPlainText);
    
    	string rtf = @"{\rtf1\ansi\ansicpg1250\deff0{\fonttbl\f0\fswiss Helvetica;}\f0\pard ";
    	rtf += escapedPlainText.Replace(Environment.NewLine, "\\par\r\n ");
    	rtf += " }";
    	return rtf;
    }

    Now, everything is Okay :-) - but, I got another issue - if the meeting request has an image in the body - it got lost :-( - I receive a meeting request with image attached but it is a blank image (only 1KB).

    Any idea about this issue?


    Seif.

    Sunday, January 5, 2014 8:59 AM
  • Good news, Seif!

    It looks like you need to update a reference in the RTF markup to the attached image. As a rule such attachments are hidden and provides IDs for referencing to them from the body. If you modify the RTF markup manually, you need to keep the reference in an unmodified state (do not remove it).



    Sunday, January 5, 2014 4:24 PM
  • Outlook stores embedded RTF objects as attachments. RTF body has placeholders that are replaced by the actual OLE objects when the message is shown.

    Even if you manage to replace the placeholders with the actual OLE object data, Windows RTF edit  control will wipe out these objects when the data is streamed out.


    Dmitry Streblechenko (MVP)
    http://www.dimastr.com/redemption
    Redemption - what the Outlook
    Object Model should have been
    Version 5.5 is now available!

    Sunday, January 5, 2014 4:47 PM
  • So - how can I solve this issue? Any idea? How can I save those object before updating the RTFBody? so I will add them back after I update the RTFBody?

    Any idea?


    Seif.

    Monday, January 6, 2014 6:14 AM
  • As I suggested before, try to insert your RTF text before the last "}".

    Dmitry Streblechenko (MVP)
    http://www.dimastr.com/redemption
    Redemption - what the Outlook
    Object Model should have been
    Version 5.5 is now available!

    Monday, January 6, 2014 6:33 AM
  • Thanks a lot Dmitry,

    I did it - and now it seems much better - I don't see that the images are now 'attached' they are in the body but the problem is that they have been changed into a small black square box. I'm using the following code:

    string str = "THIS IS THE TEST STRING." + Environment.NewLine + " IT SHOULD BE ADDED TO END OF THE MEETING BODY.";
    str = PlainTextToRtf(str);
    
    string strOriginalRTF = System.Text.Encoding.UTF8.GetString(item.RTFBody);
    int i = strOriginalRTF.LastIndexOf('}');
    strOriginalRTF = strOriginalRTF.Insert(i - 1, str);
    
    item.RTFBody = System.Text.Encoding.UTF8.GetBytes(strOriginalRTF);

    And i've updated the PlainTextToRtf to:

    private string PlainTextToRtf(string plainText)
    {
    	if (string.IsNullOrEmpty(plainText))
    		return "";
    
    	string escapedPlainText = plainText.Replace(@"\", @"\\").Replace("{", @"\{").Replace("}", @"\}");
    	escapedPlainText = EncodeCharacters(escapedPlainText);
    
    	string rtf = @"{\fonttbl\f0\fswiss Helvetica;}\f0\pard ";
    	rtf += escapedPlainText.Replace(Environment.NewLine, "\\par\r\n ");
    	return rtf;
    }

    I think the problem is in the conversion from Bytes array into String and the conversion from string into bytes array - I think the images references are lost there.

    BTW - I tried to convert the RTFBody byte array to string and then converting it back to byte array and update the RTFBody without adding new content to check if the images remains (to check if the concatinating string is the problem) and found that the images are lost. so the problem is when converting the RTFBody to string and converting it back to byte array.

    How do I get the 'accurate' RTFBody from the MeetingItem? or shall I try to use the RTFBody of the related AppointmentItem? maybe the images are still didn't 'moved' to the MeetingItem object?


    Seif.


    Monday, January 6, 2014 7:17 AM
  • Hi Seif,

    I have noticed that you use UTF8 for encoding. I am not sure that you chose the right class. Did you try to use another classes for encoding?

    For example, MSDN provides the following sample code on the RTFBody property page:

    Outlook.AppointmentItem appt = Application.ActiveInspector().CurrentItem as Outlook.AppointmentItem; 
     
    byte[] byteArray = appt.RTFBody as byte[]; 
     
    System.Text.Encoding encoding = new System.Text.ASCIIEncoding(); 
     
    string RTF = encoding.GetString(byteArray); 
     
    Debug.WriteLine(RTF); 
    

    Monday, January 6, 2014 11:41 AM
  • Hi Seif,

    I have noticed that you use UTF8 for encoding. I am not sure that you chose the right class. Did you try to use another classes for encoding?

    For example, MSDN provides the following sample code on the RTFBody property page:

    Outlook.AppointmentItem appt = Application.ActiveInspector().CurrentItem as Outlook.AppointmentItem; 
     
    byte[] byteArray = appt.RTFBody as byte[]; 
     
    System.Text.Encoding encoding = new System.Text.ASCIIEncoding(); 
     
    string RTF = encoding.GetString(byteArray); 
     
    Debug.WriteLine(RTF); 


    The same - it doesn't help. I wonder why there is no HTMLBody in the MeetingItem - if it has such a property then it will be very easy to fix this issue.

    Seif.

    Monday, January 6, 2014 1:24 PM
  • You are right, the Outlook Object Model doesn't provide the HTMLBody property for the AppointmentItem class. But you can try to use the PropertyAccessor for setting the PR_HTML property provided by Extended MAPI. The DASL property tag is http://schemas.microsoft.com/mapi/proptag/0x10130102.
    Monday, January 6, 2014 1:38 PM
  • You should never treat RTF data as a string - it is a byte array in OOM for a reason.

    Dmitry Streblechenko (MVP)
    http://www.dimastr.com/redemption
    Redemption - what the Outlook
    Object Model should have been
    Version 5.5 is now available!

    Monday, January 6, 2014 9:31 PM
  • Outlook will never use PR_HTML for appointments/contacts/tasks. It only works with RTF.

    Dmitry Streblechenko (MVP)
    http://www.dimastr.com/redemption
    Redemption - what the Outlook
    Object Model should have been
    Version 5.5 is now available!

    Monday, January 6, 2014 9:31 PM
  • Thanks a lot Dmitry -

    I fixed the issue in this way:

    byte[] bBody = item.RTFBody as byte[];
    				
    // search for the last '}' in the body
    byte b = Convert.ToByte('}');
    int iIndex = Array.LastIndexOf(bBody, b);
    
    if (iIndex > 0)
    {
    	byte[] bFirstPart = bBody.Take(iIndex).ToArray<byte>();
    	byte[] bLastPart = bBody.Skip(iIndex).ToArray<byte>();
    	
    	string str = "THIS IS THE TEST STRING." + Environment.NewLine + " IT SHOULD BE ADDED TO END OF THE MEETING BODY.";
    	str = PlainTextToRtf(str);
    	byte[] bStr = System.Text.Encoding.UTF8.GetBytes(str);
    	
    	var list = new List<byte>();
    	list.AddRange(bFirstPart);
    	list.AddRange(bStr);
    	list.AddRange(bLastPart);
    
    	item.RTFBody = list.ToArray();
    }

    And it works very well in Outlook :-) - but somehow in the Outlook.com or on the mobile phones that images are not shown. But at least, in the MS Outlook 2010 they are shown but in Outlook 2013 they are not visible (there is a dot there). But here, all of us have Outlook 2010 so we can see the images.

    Thanks everyone :).


    Seif.

    • Proposed as answer by Seif Ibrahim Tuesday, January 7, 2014 8:49 AM
    • Marked as answer by iseif Wednesday, January 8, 2014 12:58 PM
    Tuesday, January 7, 2014 7:38 AM