Getting contacts from Exchange instead of Outlook - c#

I am currently developing an application to be used internally only at work. I need to get the currently logged in user's contacts to use in the application and I am currently getting the contacts with the following:
Microsoft.Office.Interop.Outlook.Application app = new Microsoft.Office.Interop.Outlook.Application();
Microsoft.Office.Interop.Outlook.NameSpace NameSpace = app.GetNamespace("MAPI");
Microsoft.Office.Interop.Outlook.MAPIFolder ContactsFolder = NameSpace.GetDefaultFolder(Microsoft.Office.Interop.Outlook.OlDefaultFolders.olFolderContacts);
Microsoft.Office.Interop.Outlook.Items ContactItems = ContactsFolder.Items;
foreach (Microsoft.Office.Interop.Outlook.ContactItem item in ContactItems)
{
//do stuff with the contacts here
}
The problem with this approach is that whenever a user opens the application and Outlook is not already open, an Outlook popup appears asking the user to Allow or Deny the application's access to Outlook contacts. This is unnecessary and my only thought of how to stop this form happening is instead of using Outlook itself, get the contacts from the Exchange Server.
I have looked into a bunch of documentation for things like EWS however I have not found reference for EWS to be guaranteed working with Exchange 2019. I would also like any authentication done automatically based on domain authentication with the currently logged in user instead of requiring the user to input a password.
I did try to use this: https://learn.microsoft.com/en-us/exchange/client-developer/exchange-web-services/get-started-with-ews-managed-api-client-applications however ExchangeVersion only has options up to Exchange 2013.
What should I be using to achieve this? Any push in the right direction would be greatly appreciated.

Use the active directory instead of EWS to get network users data, including email address.
the relevant namespace is: System.DirectoryServices
Here is an example that I wrote in my project to get user data including email by the first name and last name from AD. Note: ActiveDirectoryEntity us a class of mine. also, regarding another issue you wrote in your question, entering the user and password is not needed because authentication was already maid when the user authenticated to windows.
public static List<ActiveDirectoryEntity> GetActiveDirectoryData(string sname, string fname)
{
try
{
DirectorySearcher search = new DirectorySearcher();
search.Filter = String.Format("(&(objectCategory=person)(objectClass=user)(givenname={0}*)(sn={1}*))", sname, fname);
search.PropertiesToLoad.Add("givenName");
search.PropertiesToLoad.Add("sn");
search.PropertiesToLoad.Add("mail");
search.PropertiesToLoad.Add("mobile");
search.PropertiesToLoad.Add("department");
var result = search.FindAll();
List<ActiveDirectoryEntity> resultlist = new List<ActiveDirectoryEntity>();
foreach (SearchResult r in result)
{
if (r.Properties["mail"] != null)
{
if (r.Properties["mail"].Count > 0)
{
ActiveDirectoryEntity ade = new ActiveDirectoryEntity();
if ((r.Properties["givenname"].Count > 0))
ade.FirstName = r.Properties["givenName"][0].ToString();
if ((r.Properties["sn"].Count > 0))
ade.LastName = r.Properties["sn"][0].ToString();
if ((r.Properties["mail"].Count > 0))
ade.Email = r.Properties["mail"][0].ToString();
if ((r.Properties["department"].Count > 0))
ade.Department = r.Properties["department"][0].ToString();
resultlist.Add(ade);
}
}
}
return resultlist;
}
catch
{
return null;
}
}

Related

C# - Get Direct Reports under another manager's Direct Reports list

I've been trying to figure out how to do this but I'm always met with a bump on the road. What I'm trying to do is to get the reporting people under my manager's direct reports list; so for example, "Alex" is a direct report under my manager, however, when you go into his organization you see that he also has direct reports that report directly to him - I am trying to get "those" reports not only from his side but from anyone else in the list that has direct reports as well. What is needed for me to effectively execute that idea? Many thanks!
This is my code to only get Direct Reports under my manager tree:
public void GetManagerDirectReports()
{
Application App = new Application();
AddressEntry currentUser = App.Session.CurrentUser.AddressEntry;
if (currentUser.Type == "EX")
{
ExchangeUser manager = currentUser.GetExchangeUser().GetExchangeUserManager();
if (manager != null)
{
AddressEntries addrEntries = manager.GetDirectReports();
if (addrEntries != null)
{
foreach (AddressEntry addrEntry in addrEntries)
{
ExchangeUser exchUser = addrEntry.GetExchangeUser();
StringBuilder sb = new StringBuilder();
sb.AppendLine("Name: "
+ exchUser.Name);
sb.AppendLine("Title: "
+ exchUser.JobTitle);
sb.AppendLine("Department: "
+ exchUser.Department);
sb.AppendLine("Email: "
+ exchUser.PrimarySmtpAddress);
Debug.WriteLine(sb.ToString());
Console.WriteLine(sb.ToString());
Console.ReadLine();
}
}
}
}
}
I opted to go ahead and use LDAP instead of Microsoft's EWS because I saw it uses _ComObject and I don't believe that will work with what I need it to work for. Essentially, I created a master load class and then a sub-class to handle LDAP syntax which would give me the emails of all managers who have direct reports. Something I found useful while doing my research is this filter string which came in quite handy in my time of need (where "cn" is the manager's name):
searcher = new DirectorySearcher
{
Filter = "(&(objectClass=user)(objectCategory=person)(manager=" + cn + ",OU=Unit,OU=People,DC=my,DC=domain,DC=com))"
};
searcher.PropertiesToLoad.Add("DirectReports");
searcher.PropertiesToLoad.Add("mail");
Hope this can serve of some use to coming questions related to this in the future.

How to get the MessageId from all exchange items

Hello I recently got into development around EWS. One of the issue came up to me is that a client ask me to import emails into database and he wants to detect the duplicate based on InternetMessageID this way he doesn't have to import the duplicate emails and my code came up to this point.
private static string GetInternetMessageID(Microsoft.Exchange.WebServices.Data.Item email)
{
EmailMessage emailMsg = email as EmailMessage;
string returnId = string.Empty;
if ((emailMsg != null)) {
try {
emailMsg.Load();
//loads additional info, without calling this ToRecipients (and more) is empty
} catch (ArgumentException ex) {
//retry
email.Load();
}
returnId = emailMsg.InternetMessageId;
} else {
//what to do?
}
return returnId;
}
I can handle regular emails, but for special exchange objects such as contact, Calendar, Posts etc it does not work because it could not cast it to an EmailMessage object.
And I know you can extract the internetMessageId from those objects. Because the client used to have another software that extract this ID for them, maybe the property is not called internetMessageID, I think I probally have to extract it from the internetMessageHeader. However when ever I try to get it from the item object it just throws me an error. How do I get the internet messageID from these "Special" exchange items?
PS i am aware of item.id.UniqueID however that is not what I want as this id changes if I move items from folder to another folder in exchange
Only objects that have been sent via the Transport service will have an InternetMessageId so things like Contacts and Tasks because they aren't messages and have never been routed via the Transport service will never have an Internet MessageId. You probably want to look at using a few properties to do this InternetMessageId can be useful for messages PidTagSearchKey https://msdn.microsoft.com/en-us/library/office/cc815908.aspx is one that can be used (if you good this there are various examples of using this property).
If your going to use it in Code don't use the method your using to load the property on each item this is very inefficient as it will make a separate call for each object. Because these I'd's are under 256 Kb just retrieve then when using FindItems. eg
ExtendedPropertyDefinition PidTagSearchKey = new ExtendedPropertyDefinition(0x300B, MapiPropertyType.Binary);
ExtendedPropertyDefinition PidTagInternetMessageId = new ExtendedPropertyDefinition(0x1035, MapiPropertyType.String);
PropertySet psPropSet = new PropertySet(BasePropertySet.IdOnly);
psPropSet.Add(PidTagSearchKey);
psPropSet.Add(PidTagInternetMessageId);
ItemView ItemVeiwSet = new ItemView(1000);
ItemVeiwSet.PropertySet = psPropSet;
FindItemsResults<Item> fiRess = null;
do
{
fiRess = service.FindItems(WellKnownFolderName.Inbox, ItemVeiwSet);
foreach (Item itItem in fiRess)
{
Object SearchKeyVal = null;
if (itItem.TryGetProperty(PidTagSearchKey, out SearchKeyVal))
{
Console.WriteLine(BitConverter.ToString((Byte[])SearchKeyVal));
}
Object InternetMessageIdVal = null;
if (itItem.TryGetProperty(PidTagInternetMessageId, out InternetMessageIdVal))
{
Console.WriteLine(InternetMessageIdVal);
}
}
ItemVeiwSet.Offset += fiRess.Items.Count;
} while (fiRess.MoreAvailable);
If you need larger properties like the Body using the LoadPropertiesForItems Method https://blogs.msdn.microsoft.com/exchangedev/2010/03/16/loading-properties-for-multiple-items-with-one-call-to-exchange-web-services/

Checking for an enabled outlook account

I just started to dabble into using Microsoft.Office.Interop.Outlook. I was able to successfully send an email using the bit of code below.
public void Send()
{
try
{
Outlook._Application _app = new Outlook.ApplicationClass();
var test = _app.CreateItem(Outlook.OlItemType.olMailItem);
Outlook.MailItem mail = (Outlook.MailItem) _app.CreateItem(Outlook.OlItemType.olMailItem);
mail.To = "testemail#fakeaddress.com";
mail.Subject = "Test Outlook Subject";
mail.Body = "Test Outlook Body";
mail.Importance = Outlook.OlImportance.olImportanceNormal;
((Outlook.MailItem) mail).Send();
}
catch
{
Notification.Notice("Error");
}
}
I would like to have a Validate() function before the try/catch such that it'll check to see if there's a valid outlook account enabled. May I ask does anyone know how I can check if any outlook accounts are setup?
I tried this
public bool validate()
{
Outlook._Application _app = new Outlook.ApplicationClass();
Outlook.Accounts accounts = _app.Session.Accounts;
return accounts.Count > 0;
}
But accounts.Count returned 1 even after I removed my outlook account.
There will always be at least one account - the store. Otherwise Outlook won't run. But even if there are mail accounts, how would you know whether they are configured appropriately? Unless you take over the message submission, there is no way for you to know ahead of time.
UPDATE: Loop through the Namespace.Accounts collection and look for accounts with Account.AccountType == olExchange ,olImap,olPop3, olHttp. Keep in mind that OOM only list mail accounts, not store or address book.
If you were using Extended MAPI (C++ or Delphi), you could use IOlkAccountManager::EnumerateAccounts(CLSID_OlkMail, ...) (you can play with that interface in OutlookSpy (I am its author) - click IOlkAccountManager button). If Extended MAPI is not an option, Redemption (I am also its author) exposes the RDOAccounts object; its GetOrder(acMail) method will return all mail accounts. You'll just need to check if the returned collection has any elements.

How can We Get Live Emails to Windows Phone

I am trying to Get My live Emails to My wp App
Is it Possibly to get Emails into our App,
Now i am Getting Email Address and Date Of Birth and Some Other But Not getting Email's
My Xaml Code is Like this
<live:SignInButton ClientId="000000004C0FWD99" Scopes="wl.basic wl.offline_access wl.signin wl.contacts_birthday wl.emails" Branding="Windows" TextType="Login" d:LayoutOverrides="VerticalAlignment" SessionChanged="SignInButton_SessionChanged" />
C#
private void SignInButton_SessionChanged(object sender, Microsoft.Live.Controls.LiveConnectSessionChangedEventArgs e)
{
if (e != null && e.Session != null && e.Status == LiveConnectSessionStatus.Connected)
{
this.liveClient = new LiveConnectClient(e.Session);
Session = e.Session;
this.LoginIn();
}
else
this.GetUserProfile();
}
private void GetUserProfile()
{
LiveConnectClient clientGetMe = new LiveConnectClient(Session);
clientGetMe.GetCompleted += new EventHandler<LiveOperationCompletedEventArgs>(clientGetMe_GetCompleted);
clientGetMe.GetAsync("me", null);
LiveConnectClient clientGetPicture = new LiveConnectClient(Session);
clientGetPicture.GetCompleted += new EventHandler<LiveOperationCompletedEventArgs>(clientGetPicture_GetCompleted);
clientGetPicture.GetAsync("me/picture");
}
How can I get Email's with with Subject, From Address and Mail Content
is it Possibly
It's not possible. Only interaction with contacts and calendars is possible.
You can use the Live Connect APIs to do the following things in Hotmail:
Create new contacts, and read existing ones, in a user's contacts list.
Create, read, update, and delete a user's calendars and the calendars' associated events.
Subscribe a user to a public calendar, such as a list of holidays.
Use the friend finder feature to discover whether any of a users' contacts are also registered users of your website.
From: Live SDK developer guide

I have a SID of a user account, and I want the SIDs of the groups it belongs to

This has to be obtained from a remote machine. The following query works not for SIDs, but for group and account names.
"SELECT GroupComponent FROM Win32_GroupUser WHERE PartComponent = \"Win32_UserAccount.Domain='" + accountDomain + "',Name='" + accountName + "'\""
The Win32_Group objects it returns come in the forms of strings, and they only have domain and name (even though Win32_Group has a SID property).
I have this sinking feeling I'll have to:
Turn the SID into an account name by querying Win32_SID;
Perform the query above;
Turn each of the resulting group names into SIDs by querying Win32_Group.
Can you use the System.DirectoryServices.AccountManagement namespace classes?
using (var context = new PrincipalContext( ContextType.Domain ))
{
using (var user = UserPrincipal.FindByIdentity( context, accountName ))
{
var groups = user.GetAuthorizationGroups();
...iterate through groups and find SIDs for each one
}
}
It should work with ContextType.Machine, though you'd need to specify the machine name and have appropriate privileges.
using (var context = new PrincipalContext( ContextType.Machine,
"MyComputer",
userid,
password ))
{
...
}
There's a nice MSDN article (longish, though) on using the new .NET 3.5 account management namespace.
Yes there is but some methods depend on having a domain.
See this page for how to convert a SID
to a user id using P/Invoke and the Windows API, or with .NET 2.0+ and no P/Invoke.
using System.Security.Principal;
// convert the user sid to a domain\name
string account = new SecurityIdentifier(stringSid).Translate(typeof(NTAccount)).ToString();
If you have AD and
the user id in there then use the DirectorySearcher
method or Account Management APIs to find the groups.
Otherwise use the method outlined in
this article to get local
groups.
Now use the API suggested by #tvanfosson to iterate the groups and get the SIDs. Or follow the info below.
In an ASP.NET application it is possible to use code like this to access group info provided a user is authenticated by Windows and not Forms authentication. In this example I've left an interesting note about exceptions that are thrown in that environment but it may apply to other users:
public List<string> GetGroupsFromLogonUserIdentity()
{
List<string> groups = new List<string>();
HttpRequest request = HttpContext.Current.Request;
if (request.LogonUserIdentity.Groups != null)
{
foreach (IdentityReference group in request.LogonUserIdentity.Groups)
{
try
{
groups.Add(group.Translate(typeof(NTAccount)).ToString());
}
catch (IdentityNotMappedException)
{
// Swallow these exceptions without throwing an error. They are
// the result of dead objects in AD which are associated with
// user accounts. In this application users may have a group
// name associated with their AD profile which cannot be
// resolved in the Active Directory.
}
}
}
return groups;
}
LogonUserIdentity is based on the WindowsIdentity class. You could modify my code sample to use WindowsIdentity and function in a non-Web application. Once you iterate over a group you should be able to do something like this to get the SecurityIdentifier:
SecurityIdentifier secid = group as SecurityIdentifier;

Categories