Email sent from C# OOM stays in Outbox if Outlook is closed until next Outlook start - vba

I'm trying to send emails from a .NET application using Outlook Object Model.
My application displays the Outlook message window so the user can see what we're sending and edit it first. When the user hits the Send button, the Outlook window closes, and the message gets sent. This works perfectly as long as the Outlook application is already running.
If the Outlook application isn't already running, the message gets stuck in the Outbox, and will not send until I start Outlook. When I start Outlook, I can see the message sitting in the Outbox folder for a few seconds, then it gets sent.
I need to show the New Message form to Outlook user to select the recipient(s) and possibly edit the message before sending.
Note: I know that this question was already asked here Email sent with Outlook Object Model stays in Outbox until I start Outlook
and the solution exists, but it is not provided (only the small hint is provided) and unfortunately I cannot ask for clarification / code example because I have not enough "reputation".
I tried to write my own implementation of the hint provided, but the SyncEnd event is fired only when Outlook is already open (just to remind, the question is about the case then Outlook is closed).
My code below. What is wrong?
using Microsoft.Office.Interop.Outlook;
using OutlookApp = Microsoft.Office.Interop.Outlook.Application;
class Mailer
{
AutoResetEvent mailSentEvent = new AutoResetEvent(false);
public void CreateMail()
{
OutlookApp outlookApp = null;
MailItem mailItem = null;
try
{
outlookApp = new OutlookApp();
mailItem = outlookApp.CreateItem(OlItemType.olMailItem);
mailItem.Subject = "Test Message";
mailItem.Body = "This is the message.";
string reportPath = #"C:\temp\aaaaa.pdf";
mailItem.Attachments.Add(reportPath);
mailItem.Display(true);
StartSync(outlookApp);
bool result = mailSentEvent.WaitOne();
}
catch (System.Exception)
{
throw;
}
finally
{
if (mailItem != null) Marshal.ReleaseComObject(mailItem);
if (outlookApp != null) Marshal.ReleaseComObject(outlookApp);
}
}
private static SyncObject _syncObject = null;
private void StartSync(OutlookApp outlookApp)
{
var nameSpace = outlookApp.GetNamespace("MAPI");
_syncObject = nameSpace.SyncObjects[1];
_syncObject.SyncEnd += new Microsoft.Office.Interop.Outlook.SyncObjectEvents_SyncEndEventHandler(OnSyncEnd);
_syncObject.Start();
}
private void OnSyncEnd()
{
mailSentEvent.Set();
}
}

the SyncEnd event is fired only when Outlook is already open
That is not true. The SyncObjects collection contains all Send\Receive groups. You need to iterate over all objects in the collection and call the Start method, for example:
Set sycs = nsp.SyncObjects
For i = 1 To sycs.Count
Set syc = sycs.Item(i)
strPrompt = MsgBox("Do you wish to synchronize " &; syc.Name &;"?", vbYesNo)
If strPrompt = vbYes Then
syc.Start
End If
Next

Related

VSTO Outlook Plugin: Cannot get AppointmentItem in Item_Change event when recurring appointment is dragged and dropped by user

I want to catch the change event on an AppointmentItem. I use Outlook 2017 for tests.
To achieve I use:
I attached the events like this:
public void AttachEvents()
{
_CalendarItems.ItemAdd += Item_Add;
_CalendarItems.ItemChange += Item_Change;
_DeletedItems.ItemAdd += Item_Delete_Add;
The Item_Change method looks like this:
public void Item_Change(Object item)
{
if (item != null && item is Outlook.AppointmentItem)
{
Outlook.AppointmentItem myAppointment = item as Outlook.AppointmentItem;
To test the code I created a recurring appointment series. I double-clicked on appointment in the calendar and entered some title and body and saved.
Now I started my code and inspected the item.
Unfortunately, item points to the series and NOT to the individual appointment when item changed is initiated.
How can I retrieve the actual AppointmentItem when Item_Changed is initiated?
Related Stackoverflow Posting: Outlook Addin: Moving Appointment in Calendar does not reflect new date/time in AppointmentItem (catch Calendar.ItemChange) But still there is no solution to this
More on this topic:
https://www.add-in-express.com/forum/read.php?FID=5&TID=15384
https://social.msdn.microsoft.com/Forums/sqlserver/en-US/4ec55891-fb64-408f-b1cf-4bf05765b866/outlook-get-original-time-of-recurring-exception-item-that-is-opened-with-drag-drop?forum=vsto
Exceptions are not actual appointments - they are stored as embedded message attachments on the master appointments. You get the master appointment, and you would need to access its exceptions to see what changed.
There is a tricky solution to that.
Assumption: The user uses the calendar view to change the item. As the event is thrown by the calendar view this should be true in all cases:
_CalendarItems = calendarFolder.Items;
_CalendarItems.ItemChange += Item_Change;
[...]
now we can use the CalendarView to calculate the selected Startdate and compare it to all Exceptions stored in the RecurrencePattern ...
if (myAppointment.IsRecurring)
{
// in case of recurring appointments at this point we always get
// only a reference to the series master NOT the occurrence
// Assumption: The user clicked on the AppointmentItem in the calendar view
// So we can calculate the selected Start Time from this selection range
// then compare this against all Exceptions in the OccurrencePattern of the recurring pattern
// if we find one AppointmentItem in the Exceptions which has the same DateTime then we found the correct one.
//
Outlook.Application application = new Outlook.Application();
Outlook.Explorer explorer = application.ActiveExplorer();
Outlook.Folder folder = explorer.CurrentFolder as Outlook.Folder;
Outlook.View view = explorer.CurrentView as Outlook.View;
// get the current calendar view
if (view.ViewType == Outlook.OlViewType.olCalendarView)
{
Outlook.CalendarView calView = view as Outlook.CalendarView;
Outlook.RecurrencePattern pattern = myAppointment.GetRecurrencePattern();
for (int i = 1; i <= pattern.Exceptions.Count; i++)
{
Outlook.Exception myException = pattern.Exceptions[i];
Outlook.AppointmentItem exceptionItem = myException.AppointmentItem;
DateTime itemDateStart = exceptionItem.Start;
if (itemDateStart == calView.SelectedStartTime)
{
updateMyPluginMeeting(exceptionItem);
return; // the use may only select on AppointmentItem so we can skip the rest
}
}
}
}
If you know any better solution to this let me know.

How detect when user choose NEW or OPEN mail in Outlook VSTO

I'm programming a VSTO in Outlook 2016 and I would like to enable/disable buttons in a Ribbon, based on the user's action of START A NEW MESSAGE or just OPEN/READ a message.
My problem is HOW detect when the user pressed NEW MAIL or just open a sent/received one message.
Could anyone help me?
Thanks!
This tutorial actually deals with this exact scenario:
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
inspectors = this.Application.Inspectors;
inspectors.NewInspector +=
new Microsoft.Office.Interop.Outlook.InspectorsEvents_NewInspectorEventHandler(Inspectors_NewInspector);
}
Specifically, you attach to this.Appliaction.Inspectors. The tutorial takes the opportunity to modify the Subject and Body properties of the new MailItem:
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";
}
}
}

x-header missing for Outlook MeetingItems when received

I am using the following approach in a VSTO Outlook Addin (using Addin-Express library) to set a custom x-header attribute on mails and other items you can send from Microsoft Outlook, like meetings. It's a security classification which is evaluated by a mail gateway appliance for making sure encryption is activated on outgoing mails, depending on the classification. Inside the organization, it will just be displayed on the receiving side, which is Outlook 2016 desktop client.
Before sending, I set the x-header property like this:
string headerNamespace = "http://schemas.microsoft.com/mapi/string /{00020386-0000-0000-C000-000000000046}/";
public void SetHeader(PropertyAccessor acc, string header, string value)
{
acc.SetProperty(headerNamespace + "x-mycustomheader", value);
}
I always receive the header for mails, but not for Meetings. I know there is an associated appointment, but I tried reading the header from any object I could think of
On the receiving end, I get the current item from the explorer or inspector window and try to retrieve that header. The attribute is not visible in OutlookSpy and it's not inside the transport header. Is it possible that Outlook stored it somewhere else, or has it been removed? This process is working fine for MailItem types.
I stripped some parts like releasing the com objects for better readability. OutlookMeetingItem2 is a wrapper class. I have two alternative methods for reading the header, one is the PropertyAccessor and the other is used here, both can't find the header attribute.
using (OutlookMessageItemWrapper outlookItem = OutlookMessageItemFactory.GetMessageItem(currentItem))
{
object outlookitemMapi = outlookItem.MAPIOBJECT;
classificationString = mapi.GetHeader(outlookItem.MAPIOBJECT, Constants.CLASSIFICATIONHEADER);
if (classificationString == "" && outlookItem is OutlookMeetingItem2)
{
OutlookMeetingItem2 meetingItem = outlookItem as OutlookMeetingItem2;
MeetingItem meeting = meetingItem.Item;
object mapiObject = meeting.MAPIOBJECT;
classificationString = mapi.GetHeader(meeting.MAPIOBJECT, Constants.CLASSIFICATIONHEADER);
if (classificationString == "")
{
AppointmentItem appointment = meeting.GetAssociatedAppointment(false);
if (appointment != null)
{
classificationString = mapi.GetHeader(appointment.MAPIOBJECT, Constants.CLASSIFICATIONHEADER);
}
}
}
}

How to include Voting Button in Outlook Mail in VB.NET

I'm fairly new to VB and I have created an email to be sent to a recipient which is coded in vb from the example; How to: Programmatically Create an E-Mail Item
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
CreateMailItem();
}
private void CreateMailItem()
{
Outlook.MailItem mailItem = (Outlook.MailItem)
this.Application.CreateItem(Outlook.OlItemType.olMailItem);
mailItem.Subject = "This is the subject";
mailItem.To = "someone#example.com";
mailItem.Body = "This is the message.";
mailItem.Importance = Outlook.OlImportance.olImportanceLow;
mailItem.Display(false);
}
Furthermore, I briefly looked at the Outlook MAPI refrence today since it's used in my office but I'm confused on how to proceed.
What I want to include in the email is a voting button e.g Approve/Reject so I can filter the received email from the Recipient based on the subject "Approve: blabla" using the MAPI .
P.S Outlook gives the subject title of the response in either "Approve:blasddd" or "Reject:hjjkkkk" . Please any suggestion will be greatly appreciated.
You need to use the MailItem.VotingOptions property. See https://msdn.microsoft.com/en-us/library/office/ff424466.aspx

Disable send update to all attendees VSTO

I'm using VSTO to develop add-in for Outlook.
When using Send method of MeetingItem (AppointmentItem), how can I disable Send Update to All Attendees popup? It always show when I call Send of existing meeting.
I only found ForceUpdateToAllAttendees property but it make the update send to all attendees, that would be wrong if user don't want to send updates to all attendees.
EDIT:
This is my code
void Application_ItemSend(object item, ref bool Cancel)
{
var form = new SC01(item);
form.Show();
Cancel = true; // prevent mail sending
}
...
in SC01 form:
private void btn_OK_Click(object sender, EventArgs e)
{
var meetingItem = _item As MeetingItem; // _item is private field of SC01
meetingItem.GetAssociatedAppointment(false).Send(); // this Send() will make sending option (to update attendees only or to all attendees
}
sorry I was away some days. I think I have the solution, althuogh I only "speak" vba - but in the end it is all the same...
leave away the line:
Cancel = true; // prevent mail sending
and also the line:
meetingItem.GetAssociatedAppointment(false).Send();
as far as I know the item will not be sent anyway as long as the form is not hidden again.
I hope this works!
Max
just had another idea that should solve your Problem:
void Application_ItemSend(object item, ref bool Cancel)
{
var form = new SC01(item);
form.Show();
'''next line is new and prevents the first popup
item.ForceUpdateToAllAttendees = TRUE
Cancel = true; // prevent mail sending
}
... in SC01 form:
private void btn_OK_Click(object sender, EventArgs e)
{
var meetingItem = _item As MeetingItem; // _item is private field of SC01
'''next line is new => popup Comes now
meetingItem.ForceUpdateToAllAttendees = FALSE
meetingItem.GetAssociatedAppointment(false).Send(); // this Send() will make sending option (to update attendees only or to all attendees
}