Outlook nicht mehr von Termingenerator ansprechbar?

  • C#
  • .NET (FX) 4.5–4.8

Es gibt 9 Antworten in diesem Thema. Der letzte Beitrag () ist von MichaHo.

    Outlook nicht mehr von Termingenerator ansprechbar?

    Moin Leute,
    ich hab einen Outlook-Termingenerator, mit dem ich mir diverse Termine generiere:
    Spoiler anzeigen

    C#-Quellcode

    1. using Outlook = Microsoft.Office.Interop.Outlook;
    2. // ---
    3. private void btnCreate_Click(object sender, EventArgs e)
    4. {
    5. try
    6. {
    7. Outlook.Application app;
    8. Outlook.AppointmentItem appt;
    9. try
    10. {
    11. // TODO: aufklären
    12. object xxx = Marshal.GetActiveObject("Outlook.Application");
    13. // mit dem laufenden Outlook verbinden
    14. app = (Outlook.Application)xxx;
    15. }
    16. catch (System.Runtime.InteropServices.COMException ex)
    17. {
    18. MessageBox.Show(@"Please start Outlook" + Environment.NewLine + ex.Message);
    19. return;
    20. }
    21. appt = (Outlook.AppointmentItem)app.CreateItem(Outlook.OlItemType.olAppointmentItem);
    22. appt.Subject = this.tbSubject.Text;
    23. appt.Body = this.tbBody.Text;
    24. appt.Location = this.tbLocation.Text;
    25. appt.Start = this.dtpStart.Value;
    26. appt.End = this.dtpEnd.Value;
    27. appt.ReminderSet = true;
    28. appt.ReminderMinutesBeforeStart = 30;
    29. appt.Save();
    30. appt.Display(true);
    31. }
    32. catch (Exception ex)
    33. {
    34. MessageBox.Show(@"The following error occurred: " + ex.Message);
    35. }
    36. }
    Das ganze hat bis vorige Woche funktioniert.
    Nun kommt im inneren try/catch-Block folgende Exception:

    Quellcode

    1. {"Vorgang nicht verfügbar. (Ausnahme von HRESULT: 0x800401E3 (MK_E_UNAVAILABLE))"} System.Runtime.InteropServices.ExternalException {System.Runtime.InteropServices.COMException}
    Dabei ist es egal, ob ich die Microsoft.Office.Interop.Outlook.dll der Version 14 oder 15 nehme. Ich arbeite mit Outlook 2010 und dem Studio 2013.
    Wie sieht das bei Euch aus?
    Hat Bill sich da was neues einfallen lassen?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Hatte in den letzten Wochen bei einigen Kunden Probleme mit dem Outlook Interop Interface. Von heut auf morgen ging der Mist nicht mehr.

    1. Plötzlich 2. Outlook Version installiert
    -> falsche überflüssige Version deinstallieren

    2. Outlook kaputt
    -> Office Reparatur durchführen
    "Gib einem Mann einen Fisch und du ernährst ihn für einen Tag. Lehre einen Mann zu fischen und du ernährst ihn für sein Leben."

    Wie debugge ich richtig? => Debuggen, Fehler finden und beseitigen
    Wie man VisualStudio nutzt? => VisualStudio richtig nutzen
    Hi @RodFromGermany
    weis nicht ob es Dir hilft, nutzt Du das Outlook mit Exchange?
    Ich hab in meinem Programm eine Methode für das Anlegen eines Outlook Contact.
    Hier mal meine ganze Klasse für die Verbindung zum AD. Die Methode fürs Outlook ist ganz unten ExportToOutlook
    Spoiler anzeigen

    C#-Quellcode

    1. using System.Collections.Generic;
    2. using System.Data;
    3. using System.DirectoryServices;
    4. using System.DirectoryServices.AccountManagement;
    5. using System.Net;
    6. using Microsoft.Exchange.WebServices.Data;
    7. using System.Security;
    8. using System;
    9. namespace ADConnection
    10. {
    11. public class ADConnect
    12. {
    13. string _Domain;
    14. string _AutoDiscoverURLMail;
    15. public ADConnect(string domain, string adminmail)
    16. {
    17. this._Domain = domain;
    18. this._AutoDiscoverURLMail = adminmail;
    19. }
    20. public void GetGroupUserList(DataTable dt, string group)
    21. {
    22. using (PrincipalContext con = new PrincipalContext(ContextType.Domain, _Domain))
    23. {
    24. using (PrincipalSearcher pS = new PrincipalSearcher(new UserPrincipal(con)))
    25. {
    26. GroupPrincipal grp = GroupPrincipal.FindByIdentity(con, IdentityType.Name, group);
    27. foreach (UserPrincipal usr in pS.FindAll())
    28. {
    29. if(usr.IsMemberOf(grp))
    30. {
    31. DirectoryEntry dirEntry = (DirectoryEntry)usr.GetUnderlyingObject();
    32. DataRow row = dt.NewRow();
    33. row[1] = dirEntry.Properties["givenName"].Value;
    34. row[2] = dirEntry.Properties["sn"].Value;
    35. row[3] = dirEntry.Properties["title"].Value;
    36. row[4] = dirEntry.Properties["department"].Value;
    37. row[5] = dirEntry.Properties["telephoneNumber"].Value;
    38. row[6] = dirEntry.Properties["facsimileTelephoneNumber"].Value;
    39. row[7] = dirEntry.Properties["mobile"].Value;
    40. row[8] = dirEntry.Properties["mail"].Value;
    41. dt.Rows.Add(row);
    42. }
    43. }
    44. }
    45. }
    46. }
    47. public void GetUser(DataTable dt)
    48. {
    49. using (PrincipalContext domain = new PrincipalContext(ContextType.Domain, _Domain))
    50. {
    51. using (UserPrincipal user = new UserPrincipal(domain))
    52. {
    53. user.Enabled = true;
    54. using (PrincipalSearcher search = new PrincipalSearcher())
    55. {
    56. search.QueryFilter = user;
    57. PrincipalSearchResult<Principal> results = search.FindAll();
    58. dt.Clear();
    59. foreach (Principal pc in results)
    60. {
    61. DirectoryEntry dirEntry = (DirectoryEntry)pc.GetUnderlyingObject();
    62. DataRow row = dt.NewRow();
    63. row[1] = dirEntry.Properties["givenName"].Value;
    64. row[2] = dirEntry.Properties["sn"].Value;
    65. row[3] = dirEntry.Properties["title"].Value;
    66. row[4] = dirEntry.Properties["department"].Value;
    67. row[5] = dirEntry.Properties["company"].Value;
    68. row[6] = dirEntry.Properties["streetAddress"].Value;
    69. row[7] = dirEntry.Properties["postalCode"].Value;
    70. row[8] = dirEntry.Properties["l"].Value;
    71. row[9] = dirEntry.Properties["telephoneNumber"].Value;
    72. row[10] = dirEntry.Properties["facsimileTelephoneNumber"].Value;
    73. row[11] = dirEntry.Properties["mobile"].Value;
    74. row[12] = dirEntry.Properties["mail"].Value;
    75. row[13] = dirEntry.Properties["wwwHomePage"].Value;
    76. row[14] = dirEntry.Properties["sAMAccountName"].Value;
    77. dt.Rows.Add(row);
    78. }
    79. dt.AcceptChanges();
    80. }
    81. }
    82. }
    83. }
    84. void UpdateUser(string username, Dictionary<string,object> prop)
    85. {
    86. using (PrincipalContext domain = new PrincipalContext(ContextType.Domain, _Domain))
    87. {
    88. using (UserPrincipal user = new UserPrincipal(domain))
    89. {
    90. user.SamAccountName = username;
    91. using (PrincipalSearcher search = new PrincipalSearcher(user))
    92. {
    93. UserPrincipal mainProps = (UserPrincipal)search.FindOne();
    94. DirectoryEntry lowProps = (DirectoryEntry)mainProps.GetUnderlyingObject();
    95. foreach (var item in prop)
    96. {
    97. if(string.IsNullOrEmpty(item.Value.ToString()))
    98. lowProps.Properties[item.Key].Clear();
    99. else
    100. lowProps.Properties[item.Key].Value = prop[item.Key].ToString();
    101. }
    102. lowProps.CommitChanges();
    103. }
    104. }
    105. }
    106. }
    107. public void ChangeUserValue(DataTable dt, string user)
    108. {
    109. Dictionary<string,object> dictChanges = new Dictionary<string, object>();
    110. DataTable changes = dt.GetChanges(DataRowState.Modified);
    111. foreach (DataRow row in dt.Rows)
    112. {
    113. foreach (DataColumn col in dt.Columns)
    114. {
    115. var origin = row[col, DataRowVersion.Original];
    116. var current = row[col, DataRowVersion.Current];
    117. if (!current.Equals(origin))
    118. dictChanges.Add(col.ColumnName, current);
    119. }
    120. }
    121. UpdateUser(user, dictChanges);
    122. dictChanges.Clear();
    123. }
    124. private Dictionary<string,object> GetUserDetails(DataTable dt, string user)
    125. {
    126. Dictionary<string, object> dictDetails = new Dictionary<string, object>();
    127. foreach (DataRow row in dt.Rows)
    128. {
    129. foreach (DataColumn col in dt.Columns)
    130. {
    131. var current = row[col, DataRowVersion.Current];
    132. dictDetails.Add(col.ColumnName, current);
    133. }
    134. }
    135. return dictDetails;
    136. }
    137. private static bool RedirectionUrlValidationCallback(string redirectionUrl)
    138. {
    139. bool result = false;
    140. Uri redirectionUri = new Uri(redirectionUrl);
    141. if (redirectionUri.Scheme == "https")
    142. {
    143. result = true;
    144. }
    145. return result;
    146. }
    147. public void ExportToOutlook(string givenname,string surname, string title,string company, string street, string postal, string location, string phone, string fax, string mobile, string mail, string hp )
    148. {
    149. ExchangeService exService = new ExchangeService(ExchangeVersion.Exchange2013);
    150. exService.AutodiscoverUrl(_AutoDiscoverURLMail, RedirectionUrlValidationCallback);
    151. exService.UseDefaultCredentials = true;
    152. ContactsFolder contactFolder = ContactsFolder.Bind(exService, WellKnownFolderName.Contacts);
    153. Contact newContact = new Contact(exService);
    154. newContact.GivenName = givenname;
    155. newContact.Surname = surname;
    156. newContact.FileAsMapping = FileAsMapping.SurnameCommaGivenName;
    157. newContact.CompanyName = company;
    158. newContact.JobTitle = title;
    159. newContact.BusinessHomePage = hp;
    160. newContact.PhoneNumbers[PhoneNumberKey.BusinessPhone] = phone;
    161. newContact.PhoneNumbers[PhoneNumberKey.MobilePhone] = mobile;
    162. newContact.PhoneNumbers[PhoneNumberKey.BusinessFax] = fax;
    163. newContact.EmailAddresses[EmailAddressKey.EmailAddress1] = mail;
    164. PhysicalAddressEntry adr = new PhysicalAddressEntry();
    165. adr.Street = street;
    166. adr.City = location;
    167. adr.PostalCode = postal;
    168. newContact.PhysicalAddresses[PhysicalAddressKey.Business] = adr;
    169. newContact.Save();
    170. }
    171. }
    172. }


    funktioniert bei mir sogar mit Exchange 2016 OnPrem als Hybrid mit Exchange online.
    Sollte mit Terminen eigentlich genauso klappen.
    Ansonsten kannst Du mal schauen, ob Du diese Klasse für Deine Bedürfnisse anpassen kannst, das ist eine Klasse um alle Termine eines angegbenen Raums (kann auch ein User sein) auflistet, bzw. ob Frei ist oder nicht und wann der nächste Termin ansteht.
    Spoiler anzeigen

    C#-Quellcode

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using System.Net;
    5. using Microsoft.Exchange.WebServices.Data;
    6. namespace MHoApps.RoomAppointment
    7. {
    8. public class RoomAppointment
    9. {
    10. //Properties
    11. public string mailBox { get; set; }
    12. public string user { get; set; }
    13. public string pass { get; set; }
    14. public string domain { get; set; }
    15. public DateTime startDate { get; set; }
    16. public DateTime endDate { get; set; }
    17. public int maxItems { get; set; }
    18. public bool IsRoomFree { get; private set; }
    19. //Fields
    20. DateTime timeNow = DateTime.Now;
    21. //Constructor
    22. public RoomAppointment(string mailBox, string user, string pass, string domain, DateTime startDate, DateTime endDate, int maxItems)
    23. {
    24. this.mailBox = mailBox;
    25. this.user = user;
    26. this.pass = pass;
    27. this.domain = domain;
    28. this.startDate = startDate;
    29. this.endDate = endDate;
    30. this.maxItems = maxItems;
    31. }
    32. //Function
    33. public FindItemsResults<Appointment> GetAppointments()
    34. {
    35. var netCred = new NetworkCredential(this.user, this.pass, this.domain);
    36. var exVers = ExchangeVersion.Exchange2013;
    37. var exService = new ExchangeService(exVers);
    38. exService.Credentials = netCred;
    39. exService.AutodiscoverUrl(this.mailBox);
    40. var calFolder = CalendarFolder.Bind(exService, WellKnownFolderName.Calendar);
    41. var calView = new CalendarView(this.startDate, this.endDate, this.maxItems);
    42. calView.PropertySet = new PropertySet(BasePropertySet.FirstClassProperties);
    43. FindItemsResults<Appointment> appointments = calFolder.FindAppointments(calView);
    44. IEnumerable<Appointment> result = appointments.Where(a => timeNow >= a.Start && timeNow <= a.End);
    45. if (result.Any()) { IsRoomFree = false; } else { IsRoomFree = true; }
    46. return appointments;
    47. }
    48. //Method
    49. public string CheckAllocation(string room)
    50. {
    51. IEnumerable<Appointment> busyResult = GetAppointments().Where(a => timeNow >= a.Start && timeNow <= a.End);
    52. IEnumerable<Appointment> freeResult = GetAppointments().Where(a => a.Start >= timeNow).Take(1);
    53. string message = "";
    54. if (!IsRoomFree)
    55. {
    56. foreach (Appointment a in busyResult)
    57. {
    58. if(a.End.Day != a.Start.Day)
    59. message = $"{room}\nDer Raum ist belegt, bitte anderen Raum buchen. \nGebucht: {a.Start} - {a.End} - {a.Organizer}";
    60. else
    61. message = $"{room}\nDer Raum ist belegt, bitte anderen Raum buchen. \nGebucht: {a.Start:d}, {a.Start:t} - {a.End:t} Uhr - {a.Organizer}";
    62. }
    63. }
    64. else
    65. {
    66. foreach (Appointment a in freeResult)
    67. {
    68. if(a.End.Day != a.Start.Day)
    69. message = $"{room}\nDer Raum ist frei und kann gebucht werden. \nNächste Buchung: {a.Start} - {a.End} - {a.Organizer}";
    70. else
    71. message = $"{room}\nDer Raum ist frei und kann gebucht werden. \nNächste Buchung: {a.Start:d}, {a.Start:t} - {a.End:t} Uhr - {a.Organizer}";
    72. }
    73. }
    74. return message;
    75. }
    76. public string CheckAllocation()
    77. {
    78. IEnumerable<Appointment> busyResult = GetAppointments().Where(a => timeNow >= a.Start && timeNow <= a.End);
    79. IEnumerable<Appointment> freeResult = GetAppointments().Where(a => a.Start >= timeNow).Take(1);
    80. string message = "";
    81. if (!IsRoomFree)
    82. {
    83. foreach (Appointment a in busyResult)
    84. {
    85. if (a.End.Date != a.Start.Date)
    86. message = $"Gebucht: {a.Start} - {a.End} - {a.Organizer}";
    87. else
    88. message = $"Gebucht: {a.Start:d}, {a.Start:t} - {a.End:t} Uhr - {a.Organizer}";
    89. }
    90. }
    91. else
    92. {
    93. foreach (Appointment a in freeResult)
    94. {
    95. if (a.End.Date != a.Start.Date)
    96. message = $"Anstehend: {a.Start} - {a.End} - {a.Organizer}";
    97. else
    98. message = $"Anstehend: {a.Start:d}, {a.Start:t} - {a.End:t} Uhr - {a.Organizer}";
    99. }
    100. }
    101. return message;
    102. }
    103. }
    104. }
    "Hier könnte Ihre Werbung stehen..."
    @MichaHo Danke.
    Ich werde es mir mal zu Hause ansehen, dann melde ich mich.
    @mrMo So krass war es bei mir nicht, der Rest läuft noch.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @MichaHo Ich habs mal probiert, aber ich komme mit der Initialisierung nicht klar.
    Brauche ich dazu noch eine Exchane-Server-Installation?
    Kannst Du mir bitte mal ein Aufrufbeispiel posten?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @RodFromGermany Hi, so, sorry, war gestern doch noch länger unterwegs.
    Ich hab mir meine Klasse RoomAppointment nochmal zur Brust genommen und paar Fehler entdeckt (habs bei Microsoft Docs nochmal nachgelesen).
    Hab eine neue abgespeckte Klasse erstellt, die lediglich einen TestTermin anlegt, klappt bei mir hervorragend, egal ob Outlook offen oder geschloßen ist.
    Muss aber dazu sagen, das ich ein Exchange Konto habe, weis nicht ob das bei Dir eine Rolle spielt.

    Die Klasse EWSHandler:
    Spoiler anzeigen

    C#-Quellcode

    1. using Microsoft.Exchange.WebServices.Data;
    2. using System;
    3. using System.Net;
    4. namespace OutlookAppointment
    5. {
    6. public class EWSHandler
    7. {
    8. private EWSHandler()
    9. { }
    10. public static EWSHandler Instance = new EWSHandler();
    11. static bool RedirectionCallback(string url)
    12. {
    13. return url.ToLower().StartsWith("https://");
    14. }
    15. public void CreateAppointment(string userEmailAddress, string userPlainPassword)
    16. {
    17. ExchangeService service = new ExchangeService();
    18. service.Credentials = new NetworkCredential(userEmailAddress, userPlainPassword.Secure());
    19. service.AutodiscoverUrl(userEmailAddress, RedirectionCallback);
    20. var calFolder = CalendarFolder.Bind(service, WellKnownFolderName.Calendar);
    21. var appointment = new Appointment(service);
    22. appointment.Start = DateTime.Now.AddHours(1);
    23. appointment.End = DateTime.Now.AddHours(3);
    24. appointment.Subject = "TestTermin";
    25. appointment.Save();
    26. }
    27. }
    28. }

    die NetCredentials können einen SecureString annehmen, daher anbei noch meine HelperKlasse für PlainStrings in SecureStrings zu wandeln:
    Spoiler anzeigen

    C#-Quellcode

    1. using System;
    2. using System.Runtime.InteropServices;
    3. using System.Security;
    4. namespace OutlookAppointment
    5. {
    6. /// <summary>
    7. /// Helfer für die Klasse <see cref = "SecureString" />
    8. /// </ summary>
    9. public static class SecureStringHelpers
    10. {
    11. /// <summary>
    12. /// hebt die Sicherung von <see cref = "SecureString" /> für einfachen Text auf
    13. /// </ summary>
    14. /// <param name = "secureString"> die sichere Zeichenfolge </ param>
    15. /// <returns> </ returns>
    16. public static string Unsecure(this SecureString secureString)
    17. {
    18. if (secureString == null) return string.Empty;
    19. var unmanagedString = IntPtr.Zero;
    20. try
    21. {
    22. unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(secureString);
    23. return Marshal.PtrToStringUni(unmanagedString);
    24. }
    25. finally
    26. {
    27. Marshal.ZeroFreeGlobalAllocUnicode(unmanagedString);
    28. }
    29. }
    30. /// <summary>
    31. /// Konvertiert eine ungesicherte Zeichenfolge in eine gesicherte Zeichenfolge.
    32. /// </ summary>
    33. /// <param name = "plainPassword"> das einfache Passwort </ param>
    34. public static SecureString Secure(this string plainPassword)
    35. {
    36. if (string.IsNullOrEmpty(plainPassword))
    37. {
    38. return new SecureString();
    39. }
    40. SecureString secure = new SecureString();
    41. foreach (var ch in plainPassword)
    42. {
    43. secure.AppendChar(ch);
    44. }
    45. return secure;
    46. }
    47. /// <summary>
    48. /// Kopiert die vorhandene Instanz einer sicheren Zeichenfolge in das Ziel und löscht das Ziel zuvor
    49. /// </ summary>
    50. /// <param name = "source"> die sichere Zeichenfolge </ param>
    51. /// <param name = "destination"> das Ziel </ param>
    52. public static void CopyTo(this SecureString source, SecureString destination)
    53. {
    54. destination.Clear();
    55. foreach (var ch in source.Unsecure())
    56. {
    57. destination.AppendChar(ch);
    58. }
    59. }
    60. }
    61. }


    aufrufen geht dann so:

    C#-Quellcode

    1. class Program
    2. {
    3. static void Main(string[] args)
    4. {
    5. EWSHandler.Instance.CreateAppointment("DeineEmailAdresse","DeinPlainStringPasswort");
    6. }
    7. }

    Du benötigst die Microsoft.Exchange.WebServices.dll, daher hänge ich Dir das Projekt hier an.

    EDIT: @RodFromGermany Danke Dir für den Hinweis, ich hab die Datei entfernt und nochmal neu gezippt.
    Dateien
    "Hier könnte Ihre Werbung stehen..."

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „MichaHo“ ()

    @MichaHo Ich hab mal reingeschaut, ein Exchange-Konto ist nicht mein Ding, da ich auf keinen lokalen Server Zugriff habe.
    Glücklicherweise war gestern-heute ein Update (das sehe ich am Default-Hintergrund beim Einloggen) und nun läuft mein "altes" Outlook-Zeug wieder.
    Trotzdem Danke. :thumbup:
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!