I'm using Microsoft.Office.Interop.Outlook to load appointments into the interface and to edit appointments . I can load appointments completely fine, editing does work but not 100% of the time. Sometimes I get the error The operation cannot be performed because the message has been changed
May I ask is there a better approach for editing appointments to avoid The operation cannot be performed because the message has been changed error. Error occurs when trying to Save()
public Outlook.AppointmentItem EditOutlookAppointment(CustomAppointment appointment, int retries = 0)
{
Outlook.AppointmentItem appointReturned = null;
try
{
Outlook.Application outlookApp = new Outlook.Application();
MAPIFolder calendarFolder = outlookApp.Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderCalendar);
for (int i = calendarFolder.Items.Count; i > 0; i--)
{
var appointmentItem = ((Outlook.AppointmentItem)calendarFolder.Items[i]);
if (appointmentItem.GlobalAppointmentID == appointment.UniqueId)
{
// set some properties
appointmentItem.Subject = appointment.Subject;
appointmentItem.Body = appointment.Body;
appointmentItem.Location = appointment.Location; //Set the location
appointmentItem.Start = appointment.Start;
appointmentItem.End = appointment.End;
appointmentItem.Save();
return appointmentItem;
}
}
}
catch (Exception ex)
{
//Error message implementation here
}
return appointReturned;
}
Firstly, errors like that are unavoidable - it means the appointment was modified between the time Outlook opened it and the time you called Save. And since Outlook really likes to cache the appointments (it always caches the appointment being edited in Outlook or the previously edited appointment), that period of time can be quite large. This can happen if the appointment was modified by the Exchange server itself or by an incoming meeting update processing.
Secondly, looping through all items in the Calendar folder can be a huge performance problem. Unfortunately Outlook won't let you search (Items.Find/FindNext and Items.Restrict) on binary (PT_BINARY) properties such as GlobalAppointmentID. You'd need to use Extended MAPI (C++ or Delphi) or Redemption (I am its author - any language) for this: (RDOtems.Find/FindNext/Restrict in Redemption allow to search on binary properties).
UPDATE. The following should work using Redemption (off the top of my head):
publicRedemption.RDOAppointmentItem EditOutlookAppointment(CustomAppointment appointment, int retries = 0)
{
try
{
Outlook.Application outlookApp = new Outlook.Application();
Redemption.RDOSession session = new Redemption.RDOSession();
session.MAPIOBJECT = outlookApp.Session.MAPIOBJECT; //share the Outlook session
RDOFolder calendarFolder = session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderCalendar);
Redemption.RDOAppointmentItem appointmentItem = calendarFolder.Items.Find("GlobalAppointmentID = '"+appointment.UniqueId + "'");
if (appointmentItem != null)
{
// set some properties
appointmentItem.Subject = appointment.Subject;
appointmentItem.Body = appointment.Body;
appointmentItem.Location = appointment.Location; //Set the location
appointmentItem.Start = appointment.Start;
appointmentItem.End = appointment.End;
appointmentItem.Save();
return appointmentItem;
}
}
catch (Exception ex)
{
//Error message implementation here
}
return null;
}
Related
I am trying to add Appointments to my UWP App.
I have successfully set up the Appointment but the result for created Appointment Id is empty which means that the appointment is not created.
Following is my Code:
public static Rect GetElementRect(FrameworkElement element)
{
GeneralTransform transform = element.TransformToVisual(null);
Point point = transform.TransformPoint(new Point());
return new Rect(point, new Size(element.ActualWidth, element.ActualHeight));
}
private async Task<ManagerResponseModel> AddAppointmentToOutlookCalendar(ViewingSummaryModel model)
{
ManagerResponseModel result = new ManagerResponseModel();
//Add appointment if assigned to the same user
if(model.HousingOfficerId == AppSession.LoggedinUserId)
{
// Create an Appointment that should be added the user's appointments provider app.
var appointment = new Appointment();
//Populate Viewing Data in appointment
appointment.Subject = string.Format("Viewing at {0}", model.PropertyAddress);
appointment.Location = (string.IsNullOrWhiteSpace(model.PropertyAddress)) ? "NA" : model.PropertyAddress;
appointment.BusyStatus = AppointmentBusyStatus.Tentative;
appointment.Sensitivity = AppointmentSensitivity.Public;
appointment.AllDay = false;
//var timeZoneOffset = TimeZoneInfo.Local.GetUtcOffset(DateTime.Now);
var date = GeneralHelper.GetCombinedDateTimeStringForViewing(model.ViewingDate, model.FormattedTime);
//var startTime = new DateTimeOffset(date.Year, date.Month, date.Day, date.Hour, date.Minute, 0, TimeZoneInfo.Local.BaseUtcOffset);
appointment.StartTime = date;
appointment.Details = string.Format("Customer: {0}", model.CustomerName) + "\r"
+ string.Format("Housing Officer: {0}", (string.IsNullOrWhiteSpace(model.AssignedTo)) ? "NA" : model.AssignedTo) + "\r"
+ string.Format("Address: {0}", model.PropertyAddress) + "\r"
+ string.Format("Created by: {0}", (string.IsNullOrWhiteSpace(model.CreatorName)) ? "You" : model.CreatorName);
// Get the selection rect of the button pressed to add this appointment
var rect = GetElementRect(this.Frame as FrameworkElement);
string appointmentId = string.Empty;
// ShowAddAppointmentAsync returns an appointment id if the appointment given was added to the user's calendar.
// This value should be stored in app data and roamed so that the appointment can be replaced or removed in the future.
// An empty string return value indicates that the user canceled the operation before the appointment was added.
if (!string.IsNullOrWhiteSpace(model.OutlookIdentifier))
{
appointmentId = await AppointmentManager.ShowReplaceAppointmentAsync(model.OutlookIdentifier, appointment, rect, Placement.Default, date);
/*Appointment doesn't exist on this system, try to add a new one*/
if (string.IsNullOrWhiteSpace(appointmentId))
{
appointmentId = await AppointmentManager.ShowAddAppointmentAsync(appointment, rect, Windows.UI.Popups.Placement.Default);
}
}
else
{
appointmentId = await AppointmentManager.ShowAddAppointmentAsync(appointment, rect, Windows.UI.Popups.Placement.Default);
}
model.OutlookIdentifier = appointmentId;
result.isSuccessful = string.IsNullOrWhiteSpace(appointmentId);
result.responseObject = model;
}
else
{
result.isSuccessful = true;
result.responseObject = model;
}
return result;
}
The following line:
appointmentId = await AppointmentManager.ShowAddAppointmentAsync(appointment, rect, Windows.UI.Popups.Placement.Default);
returns empty only if user cancels the operation or there is some other issue resulting in failure in creation of appointment. I am not cancelling the operation, nor is there any raised exception so I have no idea what am I doing wrong here.
I figured it out after reading a related thread on MSDN. It was a really stupid mistake. I had forgotten to add the Appointments capability in my Package.appxmanifest file.
So the problem was, my App was not authorized to add appointments to User's calendar which is why the appointment Id was returning empty (Would have been nice if the relevant error was returned too, Microsoft).
To fix this, add the following line to your package.appxmanifest file in Capabilities:
<Capabilities>
<uap:Capability Name="appointments" />
</Capabilities>
Or alternatively, you can just click on the file, go to Capabilities tab and check the "Appointments" capability like in the screenshot below:
I want to create a program which makes it possible to create an appointment in someone else's outlook calendar. For example : If someone asks their boss for five days free, their boss needs to be able to approve it and immediately make it visible in the person's outlook calendar. I already made some code in which allows you to set your own appointments. here is my code:
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
AddAppointment("ConferenceRoom #2345", "We will discuss progression the group project.", "Group Project", new DateTime(2016, 02, 23, 15, 30, 52), new DateTime(2016, 02, 23, 20, 30, 52));
}
private void AddAppointment(string location, string body, string subject, DateTime startdatum, DateTime einddatum)
{
try
{
var AppOutlook = new Outlook.Application();
Outlook.AppointmentItem newAppointment =
(Outlook.AppointmentItem)
AppOutlook.CreateItem(Outlook.OlItemType.olAppointmentItem);
newAppointment.Start = startdatum;
newAppointment.End = einddatum;
newAppointment.Location = location;
newAppointment.Body = body;
newAppointment.BusyStatus=Outlook.OlBusyStatus.olTentative;
newAppointment.AllDayEvent = true;
newAppointment.Subject = subject;
newAppointment.Save();
newAppointment.Display(true);
}
catch (Exception ex)
{
MessageBox.Show("The following error occurred: " + ex.Message);
}
}
PS: Sorry if my english isn't great.
I made use of the EWS api (https://msdn.microsoft.com/en-us/library/office/dd633710(v=exchg.80).aspx)
the code i used is:
try
{
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2007_SP1);
service.UseDefaultCredentials = true;
service.Credentials = new WebCredentials("user#domain.com", "password");
service.Url = new Uri("https://mail.domain.com/EWS/Exchange.asmx");
service.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, "user2#domain.com");
Appointment appointment = new Appointment(service);
// Set the properties on the appointment object to create the appointment.
appointment.Subject = "Tennis lesson";
appointment.Body = "Focus on backhand this week.";
appointment.Start = DateTime.Now.AddDays(2);
appointment.End = appointment.Start.AddHours(1);
appointment.Location = "Tennis club";
appointment.ReminderDueBy = DateTime.Now;
// Save the appointment to your calendar.
appointment.Save(SendInvitationsMode.SendToNone);
// Verify that the appointment was created by using the appointment's item ID.
Item item = Item.Bind(service, appointment.Id, new PropertySet(ItemSchema.Subject));
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
There are three main ways for creating appointments in Outlook. You need to use the Items collection of the folder (Calendar folder in our case). The Add method accepts the OlItemType enumeration too and returns a newly created Outlook item. All possible ways are described in depth in the How To: Create a new Outlook Appointment item article.
items = calendarFolder.Items;
appItem = items.Add(Outlook.OlItemType.olAppointmentItem) as Outlook.AppointmentItem;
appItem.Save();
appItem.Display(true);
Note, you need to get a shared calendar folder first. The GetSharedDefaultFolder method of the Namespace class returns a Folder object that represents the specified default folder for the specified user. This method is used in a delegation scenario, where one user has delegated access to another user for one or more of their default folders (for example, their shared Calendar folder). For example:
Sub ResolveName()
Dim myNamespace As Outlook.NameSpace
Dim myRecipient As Outlook.Recipient
Dim CalendarFolder As Outlook.Folder
Set myNamespace = Application.GetNamespace("MAPI")
Set myRecipient = myNamespace.CreateRecipient("Eugene Astafiev")
myRecipient.Resolve
If myRecipient.Resolved Then
Call ShowCalendar(myNamespace, myRecipient)
End If
End Sub
Sub ShowCalendar(myNamespace, myRecipient)
Dim CalendarFolder As Outlook.Folder
Set CalendarFolder = _
myNamespace.GetSharedDefaultFolder _
(myRecipient, olFolderCalendar)
CalendarFolder.Display
End Sub
I have created Following function to set Outlook appointment. It's work fine while running in localhost with visual Studio Editor.
But when I upload publish files on server its not working.
Do I need to set anything in server for Outlook or need to include any additional dll/files?
Any help will be appreciated. Pls let me know, if need more information.
public void OutLookReminder(string DateTimeVal)
{
Outlook.Application outlookApp = new Outlook.Application(); // creates new outlook app
Outlook.AppointmentItem oAppointment = (Outlook.AppointmentItem)outlookApp.CreateItem(Outlook.OlItemType.olAppointmentItem); // creates a new appointment
oAppointment.Subject = "........Subject"; // set the subject
oAppointment.Body = "--------Body"; // set the body
oAppointment.Location = "-------Location"; // set the location
oAppointment.Start = DateTime.ParseExact(DateTimeVal, "dd/MM/yyyy H:mm",null); // Set the start date
oAppointment.End = DateTime.ParseExact(DateTimeVal, "dd/MM/yyyy H:mm", null); // End date
oAppointment.ReminderSet = true; // Set the reminder
oAppointment.ReminderMinutesBeforeStart = 15; // reminder time
oAppointment.Importance = Outlook.OlImportance.olImportanceHigh; // appointment importance
oAppointment.BusyStatus = Outlook.OlBusyStatus.olBusy;
// save the appointment
oAppointment.Save();
}
Do you mean you are running this under a service (such as IIS)? You cannot do that.
Where is the appointment supposed to be created? Current user who is using your code in a browser (then your code needs to run locally in JavaScript)?
Trying to create a prototype application that will post a new Requirement to HPQC 11.
I've managed to get a solid connection but when I attempt to add the blank requirement I get an AccessViolationException.
TDConnectionClass td = HPQC_Connect(); //Open a connection
ReqFactory myReqFactory = (ReqFactory)td.ReqFactory; //Start up the Requirments Factory.
Req myReq = (Req)myReqFactory.AddItem(DBNull.Value); //Create a new blank requirement (AccessViolationException)
myReq.Name = "New Requirement"; //Populate Name
myReq.TypeId = "1"; // Populate Type: 0=Business, 1=Folder, 2=Functional, 3=Group, 4=Testing
myReq.ParentId = 0; // Populate Parent ID
myReq.Post(); // Submit
Any ideas? I'm fairly new to C# and coding in general, so it's probably best to assume I know nothing.
After some significant working through the isse the following code works correctly:
private void HPQC_Req_Create_Click()
{
TDConnection td = null;
try
{
td = new TDConnection();
td.InitConnectionEx("server");
td.Login(HPQCUIDTextbox.Text.ToString(), HPQCPassTextbox.Text.ToString());
Console.WriteLine(HPQCPassTextbox.Text.ToString());
td.Connect("DEFAULT", "Test_Automation_Playground");
bool check = td.LoggedIn;
if (check == true)
{
Console.WriteLine("Connected.");
HPQCStatus.Text = "Connected.";
}
ReqFactory myReqFactory = (ReqFactory)td.ReqFactory;
Req myReq = (Req)myReqFactory.AddItem(-1); //Error Here
myReq.Name = "New Requirement 1";
myReq.TypeId = "1"; // 0=Business, 1=Folder, 2=Functional, 3=group, 4=testing
myReq.ParentId = 0;
myReq.Post();
Console.WriteLine("Requirement Created.");
HPQCStatus.Text = "Requirement Created.";
try
{
td.Logout();
td.Disconnect();
td = null;
}
catch
{ }
}
catch (Exception ex)
{
Console.WriteLine("[Error] " + ex);
try
{
td.Logout();
td.Disconnect();
td = null;
}
catch
{ }
}
This code requires that the Server be patched to QC 11 Patch 9 (Build 11.0.0.7274) in order to work. Previous versions cause errors, most notably the error in the question.
Requirements in ALM are hierarchical, when creating requirement you need to create it under some existing requirement.
What you want to do is get a hold of the root requirement, it's Id should be either 0 or 1, you can check it in ALM UI.
And then get an instance of ReqFactory from a property on that Root requirement.
And then add your requirement to that factory.
Also, make sure you are working on STA and not MTA thread.
I am building an app that is required to live in a trusted domain, monitor a collection of mailbox-calendars on an exchange server in that domain, and sync appointments to different mailboxes on one or many other servers. The mailbox it is synced with is defined in an internal mapping table (sqlce) that is maintained by the user of this application.
The problem I have is I can not work out a way to keep track of the remote appointment so that I can update or delete it if necessary. After I create the appointments on the remote server they have a new itemid which does not correspond to the one returned by the sync folder items call on the local exchange server. I can't find the item by start time/subject as these may have been changed or deleted.
My sync method is below - am I going about this entirely the wrong way or is there a better way to use the SyncFolderItems method?
The best approach I have come up with so far to get around my problem is to save ItemID of the remote appointment into a property of the local appointment but even this I am not sure will work because I don't know what properties are maintained after a delete? Please Help!
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Exchange.WebServices.Data;
using System.Net;
namespace ProExchangeSync2012
{
class ExchangeWebServiceMethods
{
public string ProExchangeSyncCalendars(string LocalMailbox
,string RemoteMailbox
,string SyncState
,ExchangeService RemoteService
,ExchangeService LocalService
)
{
//if SyncState is empty string set to null
if (SyncState.ToString().Length == 0)
{ SyncState = null; }
ExchangeService LocalExchangeService = LocalService;
ExchangeService RemoteExchangeService = RemoteService;
RemoteExchangeService.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress,RemoteMailbox);
//Folders and mailboxes to pass to the webservice in SyncItems call.
Mailbox DonorMailBox = new Mailbox(LocalMailbox);
Mailbox DestinationMailBox = new Mailbox(RemoteMailbox);
FolderId DonorFolder = new FolderId(WellKnownFolderName.Calendar, DonorMailBox);
FolderId DestinationFolder = new FolderId(WellKnownFolderName.Calendar, DestinationMailBox);
//Create a ChangeCollection object and call syncfolderitems on local exchange service.
ChangeCollection<ItemChange> ItemChanges
= LocalExchangeService.SyncFolderItems(new FolderId(WellKnownFolderName.Calendar, DonorMailBox) //PASS IN THE MAILBOX HERE>
, PropertySet.FirstClassProperties
, null
, 512
, SyncFolderItemsScope.NormalItems
, SyncState
);
//Store the SyncState
SyncState = ItemChanges.SyncState;
//Fetch all the required properties of the items
//LocalService.LoadPropertiesForItems(ItemChanges, PropertySet.FirstClassProperties);
if (ItemChanges.Count == 0)
{
Console.WriteLine("There are no items to synchronize.");
}
else
{
foreach (ItemChange ic in ItemChanges)
{
if (ic.ChangeType == ChangeType.Create)
{
Appointment lappointment = Appointment.Bind(LocalExchangeService, ic.ItemId);
Appointment rappointment = new Appointment(RemoteExchangeService);
rappointment.Subject = lappointment.Subject;
rappointment.Start = lappointment.Start;
rappointment.Body = lappointment.Body;
rappointment.End = lappointment.End;
rappointment.Location = lappointment.Location;
rappointment.Save();
}
else if (ic.ChangeType == ChangeType.Update)
{
//Bind to the local appointment and get the start date
Appointment lappointment = Appointment.Bind(LocalExchangeService, ic.ItemId);
DateTime StartDate = lappointment.Start;
ItemId ItemToUpdate = ItemIDSearch(RemoteExchangeService,StartDate,lappointment.Subject);
//Bind to the remote appointment using ItemToUpdate & update all the details
//this is is less intensive than comparing the appointments for changes.
Appointment rappointment = Appointment.Bind(RemoteExchangeService, ItemToUpdate);
rappointment.Subject = lappointment.Subject;
rappointment.Start = lappointment.Start;
rappointment.Body = lappointment.Body;
rappointment.End = lappointment.End;
rappointment.Location = lappointment.Location;
rappointment.Save();
}
else if (ic.ChangeType == ChangeType.Delete)
{
Appointment lappointment = Appointment.Bind(LocalExchangeService, ic.ItemId.UniqueId);
DateTime StartDate = lappointment.Start;
ItemId ItemToUpdate = ItemIDSearch(RemoteExchangeService, StartDate, lappointment.Subject);
Appointment rappointment = Appointment.Bind(RemoteExchangeService, ic.ItemId.UniqueId);
rappointment.Delete(DeleteMode.MoveToDeletedItems);
}
}
}
return SyncState;
}
//End of Sync Method
//Below method returns a single itemid from exchange service based on start datetime of an appointment in a mailbox.
public ItemId ItemIDSearch(ExchangeService ExchangeService, DateTime AppointmentStart, string subject)
{
ItemId FoundItem;
ItemView iv = new ItemView(1000);
iv.Traversal = ItemTraversal.Associated;
SearchFilter.SearchFilterCollection searchFilterCollection = new SearchFilter.SearchFilterCollection(LogicalOperator.And);
searchFilterCollection.Add(new SearchFilter.IsEqualTo(AppointmentSchema.Subject,subject));
searchFilterCollection.Add(new SearchFilter.IsEqualTo(AppointmentSchema.Start, AppointmentStart));
FindItemsResults<Item> fiitems = ExchangeService.FindItems(WellKnownFolderName.Calendar, searchFilterCollection, iv);
if (fiitems.Items.Count == 1)//if we only get one result do the work else return null
{
FoundItem = fiitems.Items[0].Id;
}
FoundItem = null;
return FoundItem;
}
}
}
So the final solution to all this, which came from the messiah of Exchange Web Services, Glen Scales, was that I stored the "CleanGlobalObjectId" of the appointments I was synching with in my internal database along with the EWS UniqueId that is returned when calling the EWS SyncFolderItems method.
Using the CleanGlobalObjectId which is an extended property of the appointment, I was always able to find a specific appointment on the server even if it had been hard deleted, because the value of this property never changes.