Creating a copy of MailItem and then deleting creates a new mail in the same folder - outlook-addin

I created a copy of a mail in my Inbox folder by calling MailItem.copy() function and then I deleted the newly created mail item.
Following is sample code:
IDispatch * lDispatch;
_MailItemPtr lReadModeMailItem;
HRESULT lReturn = mMailItem->Copy(&lDispatch);
if (lReturn != S_OK)
{
return;
}
lReturn = lDispatch->QueryInterface(IID__MailItem, (LPVOID*)&lReadModeMailItem);
if (lReturn != S_OK || lReadModeMailItem == NULL)
{
return;
}
lReadModeMailItem->Close(olDiscard);
HRESULT lMyRet = lReadModeMailItem->Delete();
if (lMyRet != S_OK)
{
return;
}
lDispatch->Release();
After I am done executing this code, 2 new mail items are created in my Inbox folder.
Then, if I move to any other folder and come back to Inbox or restart Outlook, then 1 new mail item remains.
Why does 1 extra mail item remain in Inbox, even when I called MailItem.Delete() function?
How can I permanently delete the mail item created using MailItem.Copy() ?

That's the lack of IMAP accounts in Outlook.
You may consider using the following workarounds to get rid of such issues:
Programmatically switch folders in Outlook by setting the Explorer.CurrentFolder property which allows setting a Folder object that represents the current folder displayed in the explorer.
Use the SyncObject.Start method which begins synchronizing a user's folders using the specified Send\Receive group.

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.

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

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

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);
}
}
}
}

Microsoft.Office.Interop.Outlook with dlls?

I have a problem with the dlls I guess.
Microsoft.Office.Interop.Outlook.Application oApp = new Microsoft.Office.Interop.Outlook.Application();
// Get the MAPI namespace.
Microsoft.Office.Interop.Outlook.NameSpace oNS = oApp.GetNamespace("mapi");
// Log on by using the default profile or existing session (no dialog box).
oNS.Logon(Missing.Value, Missing.Value, false, true);
// Alternate logon method that uses a specific profile name.
// TODO: If you use this logon method, specify the correct profile name
// and comment the previous Logon line.
//oNS.Logon("profilename",Missing.Value,false,true);
//Get the Inbox folder.
Microsoft.Office.Interop.Outlook.MAPIFolder oInbox = oNS.GetDefaultFolder(Microsoft.Office.Interop.Outlook.OlDefaultFolders.olFolderInbox);
//Get the Items collection in the Inbox folder.
Microsoft.Office.Interop.Outlook.Items oItems = oInbox.Items;
// Get the first message.
// Because the Items folder may contain different item types,
// use explicit typecasting with the assignment.
// Microsoft.Office.Interop.Outlook.MailItem oMsg = (Microsoft.Office.Interop.Outlook.MailItem)oItems.GetFirst();
// Microsoft.Office.Interop.Outlook.MailItem oMsg = (Microsoft.Office.Interop.Outlook.MailItem)oItems;
foreach (Microsoft.Office.Interop.Outlook.MailItem mail in oItems)
{Activity.StartTime = mail.ReceivedTime;
Activity.EndTime = mail.ReceivedTime;
Activity.Details = mail.Subject;
Activity.Notes = mail.Body;}
I am able to access mail.Body , Subject etc but I can not access mail.Sender and mail.SenderEmailAddress
Does anybody had this problem?
I see here https://msdn.microsoft.com/en-us/library/office/dn320330.aspx
that it is a mailItem property so I do not know what is going on with my .dll. I have added as Reference C:\WINDOWS\assembly\GAC_MSIL\Office\15.0.0.0__71e9bce111e9429c\Office.dll
C:\Program Files (x86)\Microsoft.NET\Primary Interop Assemblies\Microsoft.Office.Interop.Outlook.dll
Do I need to add more .dlls to get access to sender property?

Google Drive Change File Permissions within a folder

I wondered if someone could help. We use Google Apps for Education. Within the system we have a shared folder where teachers can place files for students to access. E.g. The entire team of 8 science teachers all add files to "Science Shared."
Science Shared folder is on a separate google account "science-shared#domain.com"
Over time, files will take up quota of individual users and if they leave and their account is deleted, all these files will go. We obviously do not want to transfer their entire data to science-shared using the transfer facility.
Ideally, I am looking for some sort of script which can traverse through the shared folder and change the permissions of each file and folder so that science-shared is the owner and the individual teacher has edit access.
Is this possible and if so, can anyone provide some help on how/where to start...clueless at the moment.
Thanks in advance.
Edit:
Refer to issue 2756, noting that an administrator cannot change the ownership of files via Google Apps Script:
...It's related to the fact that you can't change the own for files
you don't own. This error occurs for any illegal ACL change, such as
trying to call addEditor() when you are a viewer.
To change the ownership of files not owned by the administrator, they must use the Google Drive SDK, authenticated via OAuth.
This is certainly possible, although only for files owned by the user running the script.
Here's a script that will find all the files owned by the current user in the Science Shared folder, and transfer ownership to user science-shared. It's designed as a spreadsheet-contained script, which creates a custom menu and uses the spreadsheet Browser UI. Put the spreadsheet into your shared directory, and any teacher should be able to use it to transfer their own files, wholesale.
An admin should be able to use the script to change ANY teacher's files - just collect the id of the origOwner, and pass it to chownFilesInFolder.
Caveat: It only deals with files, not sub-directories - you could extend it if needed.
/**
* Find all files owned by current user in given folder,
* and change their ownership to newOwner.
* Note: sub-folders are untouched
*/
function chownFilesInFolder(folderId,origOwner,newOwner) {
var folder = DriveApp.getFolderById(folderId);
var contents = folder.getFiles();
while(contents.hasNext()) {
var file = contents.next();
var name = file.getName();
var owner = file.getOwner().getEmail();
// Note: domain security policies may block access to user's emails
// If so, this will return a blank string - good enough for our purposes.
if (owner == origOwner) {
// Found a file owned by current user - change ownership
Logger.log(name);
//file.setOwner(newOwner);
}
}
};
/**
* Spreadsheet browser-based UI driver for chownFilesInFolder()
*/
function changeOwnership() {
var resp = Browser.msgBox("Transfer 'Science Shared' files",
"Are you sure you want to transfer ownership of all your shared files?",
Browser.Buttons.YES_NO);
if (resp == "yes") {
var folderName = "Science Shared";
// Assume there is just one "Science Shared" folder.
var folder = DriveApp.getFoldersByName(folderName);
if (!folder.hasNext()) {
throw new Error("Folder not found ("+folderName+")");
}
else {
var folderId = folder.next().getId();
var origOwner = Session.getActiveUser().getEmail(); // Operate on own files
var newOwner = "science_shared#example.com";
chownFilesInFolder(folderId,origOwner,newOwner);
Browser.msgBox("Operation completed", Browser.Buttons.OK);
}
}
else Browser.msgBox("Operation aborted", Browser.Buttons.OK);
}
/**
* Adds a custom menu to the active spreadsheet, containing a single menu item
*/
function onOpen() {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var entries = [{
name : "Transfer 'Science Shared' files",
functionName : "changeOwnership"
}];
spreadsheet.addMenu("Science-Shared", entries);
};
I hope that helps get you started.