Searching user tasks by task.body text - c#

Good morning all! I have spent a good bit of time searching and reading forums about this, but I can't seem to find a solution. Any help you all can offer is greatly appreciated.
I have created an Outlook Add-In via C# and Visual Studio 2013. This add-in will create a number of custom tasks, which is working. The problem I am having is searching for those custom created tasks and deleting them. I have code working to search based on a strict subject line, but nothing using "LIKE" to find partial search matches. I've also read that the find method cannot search the body, so then advanced search is better. I am trying to work with the advanced search documentation I can find, but am not sure of what the "scope" parameter would be for tasks, or even if this is the best solution.
I figured the best approach was to append all custom created tasks with a footer notated "DO NOT DELETE" and output text here that I could search to determine the task was created by my add-in and then delete it. I have also considered storing the custom task's EntryID at the time of creation, but have read that this number can change, so I am not sure this will be the best method to always find and delete the custom created tasks.
I am hoping one of you would be able to assist with a code sample of searching a user's task folder for all tasks containing a string in the body of the task. Alternatively, I can definitely work with a search of all tasks containing a string in the subject line. I am pulling my hair out over this, and I appreciate any guidance, articles, or code samples you all can provide! I either find examples in VB, or partial explanations that I am unable to put into practice.
EDIT: SOLVED
Thanks to the below response marked as an answer, I wanted to give more code detail here in case anybody needs a more detailed answer in the future. This solution does not search the body text as stated in this post title as that solution was not the best way to accomplish what I needed.
Creating the task
using Outlook = Microsoft.Office.Interop.Outlook;
Outlook.Application app = new Outlook.Application();
Outlook.TaskItem task = app.CreateItem(Outlook.OlItemType.olTaskItem) as Outlook.TaskItem;
Outlook.UserProperties taskUserProperties = null;
Outlook.UserProperty taskUserProperty = null;
try {
taskUserProperties = task.UserProperties;
taskUserProperty = taskUserProperties.Add("Custom Property Name", Outlook.OlUserPropertyType.olText, true, 1);
taskUserProperty.Value = "Custom value";
task.Save();
}
catch (Exception ex) {
MessageBox.Show(ex.Message);
}
finally {
if (taskUserProperty != null) Marshal.ReleaseComObject(taskUserProperty);
if (taskUserProperties != null) Marshal.ReleaseComObject(taskUserProperties);
}
Finding it
string searchCriteria = "[Custom Property Name] = 'VALUE TO FIND'";
Outlook.Application app = new Outlook.Application();
Outlook._NameSpace ns = app.GetNamespace("MAPI");
Outlook.MAPIFolder folder = ns.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderTasks);
Outlook._TaskItem taskItem = null;
Outlook.Items folderItems = null;
object resultItem = null;
try {
folderItems = folder.Items;
folderItems.IncludeRecurrences = true;
if (folderItems.Count > 0) {
resultItem = folderItems.Find(searchCriteria);
if (resultItem != null) {
if (resultItem is Outlook._TaskItem) {
taskItem = resultItem as Outlook._TaskItem;
MessageBox.Show(taskItem.Subject, "Task found!", MessageBoxButtons.OK);
}
}
Marshal.ReleaseComObject(resultItem);
}
}
catch (Exception ex) {
MessageBox.Show(ex.Message);
}
finally {
if (folderItems != null) Marshal.ReleaseComObject(folderItems);
}

Why not use the user properties (TaksItem.UserPropertiers.Add)?
If the user fields is added to the folder fields, you can search for that property using Items.Find/FindNext/Restrict.

Related

How to point to the correct Store in Outlook automation by C#?

I have a lot of VBA automation that interlinks an Outlook and Word solution; it is fine, but time is inexorable... so, I'm start to decorating and extending that old solution, wraping it with C#/VS2017.
Through a conventional Winform I can choose my patients, and from this action I do a lot of actions, including open the correct Outlook contact; that's the problem, because I can't get the correct Store; the patients.pst, depending on the machine, may be the 1st, 2nd, 3rd...
In VBA I do this:
WhichStoreNameToPointAt="patients"
Set myNamespace = myolApp.GetNamespace("MAPI")
For i = 1 To myNamespace.Stores.Count Step 1
If myNamespace.Stores.item(i).DisplayName = WhichStoreNameToPointAt Then
intOutlookItemStore = i
End if
End If
Set myFolderPatients = myNamespace.Stores.item(intOutlookItemStore).GetDefaultFolder(olFolderContacts)
And it always functions like a charm.
In C# I tried a lot of variations, and could not point to the correct store:
public void OpenPatientContact(string patientName)
{
Outlook.Store whichStore = null;
Outlook.NameSpace nameSpace = OlkApp.Session;
int i = 1;
foreach (Outlook.Folder folder in nameSpace.Folders)
{
bool p = false;
if (whichStoreNameToPointAt == folder.Name)
{
p = true;
whichStore = folder.Store;
//Correct Store selected; I can tell because of this watch:
//whichStore.displayname == whichStoreNameToPointAt
}
i++;
if (p)
break;
}
var contactItemsOlk = whichStore.Session.GetDefaultFolder
(Outlook.OlDefaultFolders.olFolderContacts).Items;
// The problem is below; always the first Store
Outlook.ContactItem contact = (Outlook.ContactItem)contactItemsOlk
.Find(string.Format("[FullName]='{0}'", patientName)); //[1];
if (contact != null)
{
contact.Display(true);
}
else
{
MessageBox.Show("The contact information was not found.");
}
}
Unfortunately, it keeps pointing ever to the same first Store, the one that has no patients...
If I change the Store order I can get past this and test other stuff, but of course it is not the right way.
Any other heads/eyes to see the light?
TIA
While seated writing the question, looking at a yellow rubber duck - and a lot of other stuff that belongs to my 1 yo daughter ;), I realized that whichStore.Session.GetDefaultFolder is a little strange in this context. I only changed this
var contactItemsOlk = whichStore.Session.GetDefaultFolder
(Outlook.OlDefaultFolders.olFolderContacts).Items;
To that:
var contactItemsOlk = whichStore.GetDefaultFolder
(Outlook.OlDefaultFolders.olFolderContacts).Items;
Voilá! Magic happens with C# too!
Session returns the default NameSpace object for the current session.
PS: yellow rubber duck; guys of The Pragmatic Programmer really knows some secrets and tricks ;)
Thanks Thomas and Hunt!

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/

How to access WinRM in C#

I'd like to create a small application that can collect system information (Win32_blablabla) using WinRM as opposed to WMI. How can i do that from C#?
The main goal is to use WS-Man (WinRm) as opposed to DCOM (WMI).
I guess the easiest way would be to use WSMAN automation. Reference wsmauto.dll from windwos\system32 in your project:
then, code below should work for you. API description is here: msdn: WinRM C++ API
IWSMan wsman = new WSManClass();
IWSManConnectionOptions options = (IWSManConnectionOptions)wsman.CreateConnectionOptions();
if (options != null)
{
try
{
// options.UserName = ???;
// options.Password = ???;
IWSManSession session = (IWSManSession)wsman.CreateSession("http://<your_server_name>/wsman", 0, options);
if (session != null)
{
try
{
// retrieve the Win32_Service xml representation
var reply = session.Get("http://schemas.microsoft.com/wbem/wsman/1/wmi/root/cimv2/Win32_Service?Name=winmgmt", 0);
// parse xml and dump service name and description
var doc = new XmlDocument();
doc.LoadXml(reply);
foreach (var elementName in new string[] { "p:Caption", "p:Description" })
{
var node = doc.GetElementsByTagName(elementName)[0];
if (node != null) Console.WriteLine(node.InnerText);
}
}
finally
{
Marshal.ReleaseComObject(session);
}
}
}
finally
{
Marshal.ReleaseComObject(options);
}
}
hope this helps, regards
I've got an article that describes an easy way to run Powershell through WinRM from .NET at http://getthinktank.com/2015/06/22/naos-winrm-windows-remote-management-through-net/.
The code is in a single file if you want to just copy it and it's also a NuGet package that includes the reference to System.Management.Automation.
It auto manages trusted hosts, can run script blocks, and also send files (which isn't really supported but I created a work around). The returns are always the raw objects from Powershell.
// this is the entrypoint to interact with the system (interfaced for testing).
var machineManager = new MachineManager(
"10.0.0.1",
"Administrator",
MachineManager.ConvertStringToSecureString("xxx"),
true);
// will perform a user initiated reboot.
machineManager.Reboot();
// can run random script blocks WITH parameters.
var fileObjects = machineManager.RunScript(
"{ param($path) ls $path }",
new[] { #"C:\PathToList" });
// can transfer files to the remote server (over WinRM's protocol!).
var localFilePath = #"D:\Temp\BigFileLocal.nupkg";
var fileBytes = File.ReadAllBytes(localFilePath);
var remoteFilePath = #"D:\Temp\BigFileRemote.nupkg";
machineManager.SendFile(remoteFilePath, fileBytes);
Hope this helps, I've been using this for a while with my automated deployments. Please leave comments if you find issues.
I would like to note that this shows an interop error by default in Visual Studio 2010.
c.f. http://blogs.msdn.com/b/mshneer/archive/2009/12/07/interop-type-xxx-cannot-be-embedded-use-the-applicable-interface-instead.aspx
There appear to be two ways to solve this. This first is documented in the article listed above and appears to be the correct way to handle the problem. The pertinent changes for this example is:
WSMan wsManObject = new WSMan();
This is in lieu of IWSMan wsman = new WSManClass(); which will throw the error.
The second resolution is to go to the VS2010—>Solution Explorer—>Solution—>Project—>References and select WSManAutomation. Right click or hit Alt-Enter to access the properties. Change the value of the "Embed Interop Types" property of the wsmauto reference.

InterIMAP, Viewing UNREAD IMAP mail and Downloading Attachments in C#

I was wondering if anyone can help me on this cause its driving me mad trying get this working
I was working with the trail of mail.dll from http://www.lesnikowski.com/mail/ which is an extremely fantastic tool which unfortunately i cannot afford being a student (even though its around 150eur, its still very expensive to me :/) and this would be a small module in my thesis and my faculty cannot afford to buy these things for students either :/ so anyway I had to go for a free tool (so please dont suggest any non open source ones - trust me i have tried them ALL)..
Well, i'm trying to explore InterIMAP, and for several hours have been trying to list unread emails from my gmail account but it just doesn't seem to be working. I can connect just fine but finding the unread emails seems to be no easy task.. I have tried countless approaches but non seem to give me unread emails in my inbox (I have loads of emails in my inbox and i just want the unread ones). Would someone please assist me? I have been trying to get this working for ages now, but documentation is rather lacking and my every attempt has resulted in a fail so far.
Please help!!
Some code i currently have:
` IMAPConfig config = new IMAPConfig("myhost", "username", "pass", true, true, "");
config.CacheFile = "";
IMAPClient client = null;
try
{
client = new IMAPClient(config, null, 5);
}
catch (IMAPException e)
{
Console.WriteLine(e.Message);
return;
}
Console.WriteLine(DateTime.Now.ToString());
IMAPFolder f = client.Folders["INBOX"];
IMAPSearchResult sResult = f.Search(IMAPSearchQuery.QuickSearchNew()); // <--- Gives me no results even though i do have unread messages!
If you did't reach your goal, here we go:
You should code in the following way:
1st: Inside your SearchQuery class, add a new property "unread", for example.
2nd: Add a new Method that returns an IMAPSearchQuery. It'll quick search unread mails. Something like that:
public static IMAPSearchQuery QuickSearchUnread()
{
IMAPSearchQuery query = new IMAPSearchQuery();
query.unread = true;
return query;
}
3td: Inside your class IMAPFolder, you have a method called that will return an IMAPSearchResult type and that receives an IMAPSearchQuery as parameter.
This method "build" your query with IMAP command queries (IMAP based protocol).
To the Unread query you should add:
public IMAPSearchResult Search(IMAPSearchQuery query)
{
...
if (query.Unread)
searchTerms.Add("UNSEEN");
.
.
...
}
4th: Call the Search method with the new QuickSearch:
config.CacheFile = "";
IMAPClient client = null;
try
{
client = new IMAPClient(config, null, 5);
}
catch (IMAPException e)
{
Console.WriteLine(e.Message);
return;
}
Console.WriteLine(DateTime.Now.ToString());
IMAPFolder f = client.Folders["INBOX"];
IMAPSearchResult sResult = f.Search(IMAPSearchQuery.QuickSearchUnread());
Let me know about your progress.
I hope it can be helpful.
Bye.
I honestly just ended up using Mail.dll trial version as interIMAP was not working properly for me and way to slow because it indexes the emails for some reason :s

Categories