Get Sent Date from Sent Items - VSTO - Outlook - c#

I need to get the "Sent Date" from the Sent Items e-mails, but I am following the example from Microsoft's documentation, for the implementation, making changes to get it from the sent items folder, it doesn't give any errors, but it doesn't work. Microsoft Documentation.
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
outlookNameSpace = this.Application.GetNamespace("MAPI");
inbox = outlookNameSpace.GetDefaultFolder(
Microsoft.Office.Interop.Outlook
.OlDefaultFolders.olFolderSentMail);
items = inbox.Items;
items.ItemAdd +=
new Outlook.ItemsEvents_ItemAddEventHandler(items_ItemAdd);
}
UPDATE
It doesn't even give an error, I have done tests to check, what seems to happen is that it is picking up from submitted items 1, and I need it to pick up from submitted items 2.

The code in the ThisAddIn_Startup event handler looks good. Try to set a breakpoint in the code and see how it works and whether any exception is thrown at runtime.
Be aware, Outlook may block add-ins that throw exceptions at runtime. So, if your breakpoint is not hit in the ThisAddIn_Startup event handler it makes sense to check the Disabled Items list in Outlook. Most probably your add-in was disabled automatically by the host application. See Support for keeping add-ins enabled for more information.
To get the default folder from another store you need to use the Store.GetDefaultFolder method instead of the following code:
inbox = outlookNameSpace.GetDefaultFolder(
Microsoft.Office.Interop.Outlook
.OlDefaultFolders.olFolderSentMail);
This method is similar to the GetDefaultFolder method of the NameSpace object. The difference is that this method gets the default folder on the delivery store that is associated with the account, whereas NameSpace.GetDefaultFolder returns the default folder on the default store for the current profile.
You can iterate over all stores in the profile in the following way:
private void EnumerateStores()
{
Outlook.Stores stores = Application.Session.Stores;
foreach (Outlook.Store store in stores)
{
if (store.IsDataFileStore == true)
{
Debug.WriteLine(String.Format("Store: "
+ store.DisplayName
+ "\n" + "File Path: "
+ store.FilePath + "\n"));
}
}
}

Related

How can Outlook addin detect when an account is removed

I get accounts from Outlook like below.
Outlook.NameSpace ns = null;
Outlook.Accounts accounts = null;
Outlook.Account account = null;
string accountList = string.Empty;
try
{
ns = OutlookApp.Session;
accounts = ns.Accounts;
for (int i = 1; i <= accounts.Count; i++)
{
account = accounts[i];
accountList += String.Format("{0} - {1}{2}",
account.UserName,
account.SmtpAddress,
Environment.NewLine);
if (account != null)
Marshal.ReleaseComObject(account);
}
MessageBox.Show(accountList);
}
finally
{
if (accounts != null)
Marshal.ReleaseComObject(accounts);
if (ns != null)
Marshal.ReleaseComObject(ns);
}
However, Outlook return accounts, including those that have been removed.
It seems that there are no events that occur when the account is removed.
After the account is removed, is there a way to get the accounts excluding the removed account? How I can get accounts excluding removed account?
On the MAPI level (C++ or Delphi), account events are implemented through IOlkAccountManager::Advise method. You can see the events fire in OutlookSpy (I am its author - click IOlkAccountManager button, go to the Advise tab.
Outlook Object Model does not expose these events. If using Redemption (I am also its author) is an option, it exposes all account events through the RDOAccounts object - AccountChange, AccountAdd, AccountRemove, AccountBeforeRemove, AccountOrderChange.
The Outlook object model doesn't provide any event for that. The best what you can do is to handle the Stores.BeforeStoreRemove event which is fired when a Store is about to be removed from the current session either programmatically or through user action. Here is what MSDN says for the event:
Outlook must be running in order for this event to fire. This event will fire when any of the following occurs:
A store is removed by the user clicking the Close command on the Shortcut menu.
A store is removed programmatically by calling Namespace.RemoveStore.
This event will not fire when any of the following occurs:
When Outlook shuts down and closes a primary or delegate store.
If a store is removed through the Mail applet in the Microsoft Windows Control Panel and Outlook is not running.
A delegate store is removed on the Advanced tab of the Microsoft Exchange Server dialog box.
A store is removed through the Data Files tab of the Account Manager dialog box when Outlook is not running.
An IMAP Store is removed from the profile.
You can use this event to determine that a store has been removed, and take appropriate actions if the store is required for your application (such as remounting the store). Otherwise you would have to resort to polling the Stores collection.

Automatically create rule in Outlook c#

I need to create a set of rules in Microsoft Outlook via C#.
I have found a lot of documentation online, but unfortunately it is not working for me.
I create the rule using the function below:
{
Outlook.Application OutlookApplication = Marshal.GetActiveObject("Outlook.Application") as Outlook.Application;
Outlook.MAPIFolder OutlookInbox = (Outlook.MAPIFolder)OutlookApplication.Application.ActiveExplorer().Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox);
Outlook.Rules rules = null;
try
{
rules = OutlookApplication.Session.DefaultStore.GetRules(); //Gets list of outlook rules
}
catch
{
Debug.WriteLine("Could not obtain rules collection.");
return;
}
string ruleName = "TestRule";
Outlook.Rule rule = rules.Create(ruleName, Outlook.OlRuleType.olRuleReceive); //Creates new rule in collection
rule.Name = ruleName;
//From condition
rule.Conditions.From.Recipients.Add("allixhd#gmail.com");
rule.Conditions.From.Enabled = true;
//Subject condition
rule.Conditions.Subject.Text = new string[] { "#test" };
rule.Conditions.Subject.Enabled = true;
//Move action
Outlook.MAPIFolder ruleFolder = OutlookInbox.Folders["TestFolder"]; //Gets the folder with name TestFolder
rule.Actions.MoveToFolder.Folder = ruleFolder;
rule.Actions.MoveToFolder.Enabled = true;
rule.Enabled = true;
//Save rules
try
{
rules.Save(true);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
The problem I face is that the rule is created successfully in my Outlook instance. I am able to open the Rule window in Outlook and I can see my rule. When I open the rule, all the settings appear correct. However when I run the rule it does not work.
If I manually create an identical rule in Outlook it does work.
I have also found the following:
- Create the rule in Outlook via my C# function
- Open the rule window in Outlook
- Tick and untick a checkbox and save
- The rule runs correctly.
- Note: If I only change the name, rather than unticking and reticking a checkbox, this does not work.
- I tried to replicate this with a hack in the code forcing this behaviour, and the rule did not work.
I wonder if there is some setting I am missing that is applied when you use the rule interface in Outlook?
Any advice would be very welcome.
Thanks
I'd suggest starting from releasing all underlying com objects in the code. Use System.Runtime.InteropServices.Marshal.ReleaseComObject to release an Outlook object when you have finished using it. Then set a variable to Nothing in Visual Basic (null in C#) to release the reference to the object.
To release all COM objects you need to break the chain of calls and declare the each property and method call on a separate line of code.
OutlookApplication.Application.ActiveExplorer().Session.GetDefaultFolder
There is no need to call the Application property of the Application class. You have already got an instance of the Application class.
Also there is no need to call the ActiveExplorer method of the Application class. You can use the Session property or the GetNamespace method of the Application class. Review the code and release all underlying COM objects.
rules.Save(true);
Do you get any exceptions when calling the Save method?
Anyway, I'd suggest creating a rule manually and then exploring its properties to create the same rule programmatically.
I have solved this question.
The domain of the From email address was in a hierarchy in outlook and so not being recognized correctly. I have included all the email addresses for the user and now the rule works.

VSTO Outlook User Properties (Custom) not syncing

I have the problem that when i add a custom UserProperty to an Outlook MailItem, it does not get synced to other connected Outlooks.
What can i do to force Outlook to sync the whole email?
My overal problem:
I've got a shared Exchange Mailbox, opened on two clients (in Outlook)
I would like to lock a mail item, if it gets opened in one Outlook and show the second Outlook user a message like "The user XX is currently reading this email"
My way to solve the problem:
Creating a Outlook Plugin.
When user "A" is opening the Email, I am adding a "LockingUser" UserProperty to the MailItem object. If user "B" is trying to open the Email, I am first looking if a "LockingUser" Property exists.
I have disabled the cached mode.
I have tried to update the subject of the email: this works perfectly and gets synced immediatly (but is not a solution for my problem)
private void SetLockingUser(Outlook.MailItem mail)
{
var lockingUserProperty = mail.UserProperties.Find("LockingUser");
if (lockingUserProperty != null)
{
MessageBox.Show("Email locked by: " + lockingUserProperty.Value);
return;
}
var identity = System.Security.Principal.WindowsIdentity.GetCurrent();
var username = identity != null ? identity.Name : "";
lockingUserProperty = mail.UserProperties.Add("LockingUser", Outlook.OlUserPropertyType.olText, false, 1);
lockingUserProperty.Value = username;
mail.Save();
}
Please show the relevant snippet of your code and make sure you call MailItem.Save. Also keep in mind that there will always be lag since the changes will take up to a couple minutes to sync to Exchange and then to another user if cached mode is used. You'd be better off using some external sync mechanism instead of a user property.

Flajaxian Custom Folder Save Location

I see a lot of people coming up with some excessive ways to change the folder location on the fly with flajaxian multiple file upload control.
Was just wondering if the more experienced could take a look at the way I've come up with and let me know if there are any major issues I should be concerned about. (Assuming I have the proper error checking in place.)
I planned on initializing the control as seen below. :
<cc1:FileUploader ID="FileUploader1" runat="server" OnFileReceived="fileUploader_FileReceived" RequestAsPostBack="true">
</cc1:FileUploader>
(I RequestAsPostBack="true" as there are some other controls I need to check in my event handler)
I simply change the HttpFileCollection.SaveAs property in the fileUploader_FileReceived event. Since flajaxian does this one file upload at a time, we can expect that there is only 1 file in the collection (or else we could use a loop).
protected void fileUploader_FileReceived(object sender,
com.flajaxian.FileReceivedEventArgs e)
{
HttpFileCollection files = Request.Files;
// Change path to whichever folder I need
String TempFileName = "C:\\NEW\\PATH\\TO\\Folder\\" + files[0].FileName;
// Save the file.
files[0].SaveAs(TempFileName);
}
This implementation seems to work great as long as the folder is existing! I was just wondering if there is anything technically wrong with an implementation like this, again , assuming all error checking was in place.
Thanks!
A better way to do this would be to use an adapter, and over write the folder location in the
OnFileNameDetermining event. This way, we also get all the goodies with the adapter.
<cc1:FileUploader ID="FileUploader1" runat="server"` OnFileReceived="fileUploader_FileReceived" RequestAsPostBack="true">
<Adapters>
<cc1:FileSaverAdapter runat="server" FolderName="Ups" OnFileNameDetermining="fileUploader_FileDetermined" />
</Adapters>
</cc1:FileUploader>
In the file determined event, we can change the folder location programatically
protected void fileUploader_FileDetermined(object sender, com.flajaxian.FileNameDeterminingEventArgs e)
{
e.FileName = "C:\\NewFolder\\" + e.File.FileName;
}
We can use the FileReceived event to check if the folder exists, and if not, create it.
protected void fileUploader_FileReceived(object sender, com.flajaxian.FileReceivedEventArgs e)
{
int fileIndex = e.Index;
if (fileIndex == 0)
{
// We are on our first file, check if the new folder exists, if not, create it
}
}
What you are doing is fine, although, if you are saving files within the web site, consider using the MapPath method to create a physical folder from a virtual path within the web site
MapPath("/Images/User1")
This my mininal APSX implementation
<fjx:FileUploader ID="FileUploader1" runat="server" OnFileReceived="FileUploader2_FileReceived">
</fjx:FileUploader>
No adapters or folder is specified. When the FileRecevied event fires, I save files to a folder based on the Forms Authentication user name (names do not use characters not allowed in folder names).
Also note that the FileReceivedEventArgs has a reference to the (HTTP) file
e.File
The FileUploader control will show all files processed - you can even set the status code (e.g. 550) if there is an error, which is returned to the client.
Note that, the server call to the FileReceived event does not occur inside a nornal page postback, even if you specify
RequestAsPostBack="true"
So, a PagePreRender does not take place.
The only issue is, how do you perform any other processing at the client after the uploads complete (e.g. showing images uploaded).
Work I have in progress to this end is to use the client side event
FileStateChanged
When the last file is processed
if (file.state > Flajaxian.File_Uploading && isLast) {
I use JQuery to click a hidden submit button. The postback looks through session values stored when the files were saved, and renders back the images into a DIV.
However, an immediate submit causes issues with empty session inside the FileReceived event for some reason (I assume because the internal asynchronous call back has not completed). A pause of a few seconds before initiating the postback works OK.

Unable to change Mail HTML body from VSTO Outook 2007 add-in

The plot:
I am creating an outlook 2007 add-in using VSTO and C# in Visual Studio 2010. The purpose of the add-in is to cause tracking of mails sent to customers. The add-in should insert a tracking code into every outbound mail that later enables it to be recognized and auto archived once the customer replies. My goal is to insert a tracking code as an attribute in the opening tag of the mail's HTML:
<html tracking="ddfwer4w5c45cgx345gtg4g" ...>
This will be done in the event handler of the Application.ItemSend event:
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
this.Application.ItemSend += new Microsoft.Office.Interop.Outlook.ApplicationEvents_11_ItemSendEventHandler(Application_ItemSend);
}
void Application_ItemSend(object Item, ref bool Cancel)
{
if (Item is Outlook.MailItem)
{
Outlook.MailItem mail = (Outlook.MailItem)Item;
mail.HTMLBody = "<html tracking=\"4w5te45yv5e6ye57j57jr6\" ...";
}
}
The problem:
It seem to never change the HTMLBody property. And it does not throw any exceptions. It just does nothing. I rewrote this logic directly in VBA in Outlook, and tried to change the HTMLBody, but it still did not change. How I know it did not change the HTMLBody is by stepping through and hovering over the property to see the current value.
I am however able to make changes to the MailItem.Body property. But since I need to hide the tracking code in some way, the Body property does not help me at all. I also added the MailItem.Save() method after changing the HTMLBody property, but no change.
I thought that perhaps the ItemSend event is to late and I can't change the HTMLBody at that time anymore, but I can't find any event like BeforeSend or alike.
Any ideas would be much appreciated.
I use VB a lot more than C#, but I think I see your problem.
Check the function signature of Application_ItemSend. I see that Cancel is passed by reference; shouldn't Item be passed that way too?
void Application_ItemSend(ref object Item, ref bool Cancel)
{
// ...
}
If Item is passed by value, then your code is only manipulating the copy that was made; the original object isn't being touched. At least, that's how it worked in C++ (but my C++ experience is before .Net, so maybe that doesn't apply).
I had a similar need to put some ids into the message. Seems like Outlook thoroughly scrubs the email before sending it, so by the time it is sent, all the nice hidden properties that you set have been removed.
For my particular need, I ended up instead using the UserProperties element of MailItem. This persists from when the email is created to when it ends up in the Sent Items folder. Perhaps this approach can work for you if the customer reply is tried to the sent email through a conversation?
mailItem.UserProperties.Add("myid", Outlook.OlUserPropertyType.olText);
mailItem.UserProperties["myid"].Value = myid;

Categories