I am working on an Outlook AddIn with a custom ribbon. The user opens a mail item in Read mode and clicks a button on the ribbon and the program will move the email to a folder (not the user's personal mailbox, but rather to a different mailbox that user has access to).
When the program is run, it works the first time, but the second time the user runs it, it throws an error:
"The attempted operation failed. An object could not be found."
Here is the relevant code:
(in ThisAddIn.cs)
public partial class ThisAddIn
{
public Outlook.Application OutlookApplication;
void ThisAddIn_Startup(object sender, System.EventArgs e)
{
OutlookApplication = this.Application;
}
(etc)
(In Ribbon1.cs, in a method that gets called upon button_Click)
Outlook.Inspector inspector = Globals.ThisAddIn.OutlookApplication.ActiveInspector();
Outlook.MailItem item = inspector.CurrentItem as Outlook.MailItem;
Outlook.Stores stores = null;
Outlook.Folder destinationMailboxFolderInbox = null;
and
try
{
// Set the mailbox move location
stores = Globals.ThisAddIn.OutlookApplication.GetNamespace("MAPI").Stores;
foreach (Outlook.Store store in stores)
{
attachmentsFoundTotal++;
if (store.DisplayName == destinationMailbox)
{
destinationMailboxFolderInbox = (Outlook.Folder)store.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox);
try
{
// the code breaks on this line below:
item.Move(destinationMailboxFolderInbox.Folders[destinationMailboxFolder]);
}
catch (Exception ex3)
{
System.Windows.Forms.MessageBox.Show(ex3.Message + " Could not find Outlook folder " + destinationMailboxFolder + ". The mail item was not moved." );
}
}
}
}
catch (Exception ex2)
{
System.Windows.Forms.MessageBox.Show(ex2.Message);
}
UPDATE: After trial & error testing, the only way I could resolve the Outlook 2010 bug was to have the Outlook view switch to the folder where the mail item was moved, using this command after the command to move the item to myfolder.
Globals.ThisAddIn.OutlookApplication.ActiveExplorer().CurrentFolder = myFolder;
When the program is run, it works the first time, but the second time the user runs it, it throws an error:
The Outlook UI is not refreshed when an item is moved to another place. You need to refresh the view on your own to get a live reference. Any UI objects still hold an old reference.
For example, the Move method moves a Microsoft Outlook item to a new folder and returns an Object value that represents the item which has been moved to the designated folder.
Related
I am working on an outlook add-in that will save customer userProperties to an outlook appointment. I am able to get reference to an existing appointment using the selectionChange event.
public partial class ThisAddIn
{
Outlook.Explorer currentExplorer = null;
private Outlook.AppointmentItem appointmentItem = null;
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
currentExplorer = this.Application.ActiveExplorer();
currentExplorer.SelectionChange += new Outlook.ExplorerEvents_10_SelectionChangeEventHandler(CurrentExplorer_Event);
}
private void CurrentExplorer_Event()
{
String expMessage = "default";
try
{
if (this.Application.ActiveExplorer().Selection.Count > 0)
{
Object selObject = this.Application.ActiveExplorer().Selection[1];
if (selObject is Outlook.AppointmentItem)
{
appointmentItem = (selObject as Outlook.AppointmentItem);
expMessage = "The item is an appointment. The subject is " + appointmentItem.Subject + ".";
MessageBox.Show(expMessage);
}
}
}
catch (Exception ex)
{
expMessage = "Error happened " + ex.Message;
}
}
However when a user initiates a new appointment, the this.Application.ActiveExplorer().Selection.Count is 0.
I wanted to see how we can get a reference to on Outlook AppointmentItem that was triggered by a user and not been saved yet
You are trying to mix different Outlook windows (inspectors and explorers) to get items there. If you need to get an item when a user creates a new appointment item you need to handle the NewInspector event which is fired whenever a new inspector window is opened, either as a result of user action or through program code. You can differentiate existing items and new ones by checking the EntryID property which returns an empty string in case of new items. The EntryID property value is set when an item is saved to the store.
So, there is no need to handle the SelectionChange event which is fired when the user selects a different or additional Microsoft Outlook item programmatically or by interacting with the user interface. This event also occurs when the user (either programmatically or via the user interface) clicks or switches to a different folder that contains items, because Outlook automatically selects the first item in that folder.
class Connect
{
// Connect class-level Instance Variables
// Outlook inspectors collection
private Outlook.Inspectors inspectors;
public Connect(Outlook.Inspectors Inspectors)
{
inspectors = Inspectors;
// Hook up NewInspector event
inspectors.NewInspector += new
Outlook.InspectorsEvents_NewInspectorEventHandler(
inspectors_NewInspector);
}
// NewInspector event creates new instance of OutlookInspector
void inspectors_NewInspector(Outlook.Inspector Inspector)
{
// use the CurrentItem to get the item object
// inspector.CurrentItem
}
}
So, there is no need to use the ActiveInspector property.
You may find the Implement a wrapper for inspectors and track item-level events in each inspector article helpful.
This is how I was able to figure the solution. The above code works fine when an existing appointment is opened.
For new appointment, you can use the ActiveInspector to get reference to the new AppointmentItem
Outlook.AppointmentItem appointment = null;
Outlook.Inspector inspector = Globals.ThisAddIn.Application.ActiveInspector();
if (inspector.CurrentItem is Outlook.AppointmentItem)
appointment = inspector.CurrentItem as Outlook.AppointmentItem;
You can use either Inspectors.NewInspector event when new new appointment is displayed or Items.ItemAdd event on the MAPIFolder corresponding to Calendar folder after the new appointment is saved.
I have an Outlook addin for encryption and decryption that supports Outlook 2010 to 2013.
I am trying encrypt email to decrypt and display.
I am using open mail through mailItem_Open function
wrappedMailItem.Open += new MailItemInspectorOpenDelegate(mailItem_Open);
through this function i just decrypt email and content updated using
mailItem.HTMLBody = decryptString
Then the inspector window open and showing decrypt mail. Its working fine. I close the inspector window
mailItem_close function call
void mailItem_Close(Outlook.MailItem mailItem, ref bool Cancel)
{
try
{
if (mailItem == null)
return;
if (mailItem.Sent != false)
{
var signProperty = GetProperty(mailItem, "App.Decrypted");
// NOTE: Cannot call mailItem.Close from Close event handler
// instead we will start a timer and call it after we
// return. There is a small race condition, but 250
// milliseconds should be enough even on slow machines.
if ((bool)signProperty)
{
var timer = new System.Windows.Forms.Timer { Interval = 250};
timer.Tick += new EventHandler((o, e) =>
{
timer.Stop();
((Outlook._MailItem)mailItem).Close(Outlook.OlInspectorClose.olDiscard);
mailItem = null;
});
Cancel = true;
timer.Start();
}
}
}
catch
{
// Ignore random COM errors
}
Marshal.ReleaseComObject(mailItem);
}
The issue is
But am not closing the inspector window (Showing decrypt message) i just click the forward button its open new inspector window by out look and forward email and close it. Then i close the inspector window ,but the parent mailItem (Ie. inbox mail) showing in decrypt mode . In mailItem_close function i just discard all changes but its not working
This issue is not happening in Reply procedure in same steps, only happens forward case
Please help me
Instead of modifying the message contents on the fly, you'd be better off creating your own form that shows the decrypted data without setting and potentially saving the decrypted data on the original message.
I have a VSTO addin that displays a a dialog box with buttons yes no and cancel. I want the form to close anytime cancel or the X are clicked. I also want the application to quit when the form is closed. Here is my code:
var frm = new Form1();
DialogResult res = frm.ShowDialog();
if (client != null)
{
if (res == DialogResult.Yes)
{
path = DRIVE_LETTER + ":/Clients/" + client + "/Correspondence/";
}
else if (res == DialogResult.No)
{
path = DRIVE_LETTER + ":/Clients Project History/" + client + "/Correspondence/";
}
else if (res == DialogResult.Cancel)
{
frm.Close();
}
else
{
frm.Close();
}
And then my form closing event handler:
private void Form1_Closing(object sender, CancelEventArgs e)
{
Application.Exit();
}
But it doesn't seem to work. Microsoft.Office.Interop.Outlook.Application doesn't have an Exit method. How can I do the equivalent from within VSTO? I want my application to stop executing completely when those forms are canceled/closed.
Thanks
EDIT: can anyone provide an example of quitting the addin. Or stopping all execution if a certain condition is met, like Pyton's sys.exit(). I don't want outlook to close, just the addin to stop execution. Not even unload, just stop.
If you need to shut down Outlook you may use the Quit method of the Application class. The associated Outlook session will be closed completely; the user will be logged out of the messaging system and any changes to items not already saved will be discarded.
But if you need to shut down the add-in (not the host application) you can:
Disable all event handlers and UI controls. To get the job done you may check out the global boolean variable which can indicate the state of the add-in (allowed to run or not).
The Connect property of the ComAddIn class allows to set the state of the connection for the specified COMAddIn object. The property returns true if the add-in is active; it returns false if the add-in is inactive. An active add-in is registered and connected; an inactive add-in is registered but not currently connected.
Outlook.Application outlook = new Outlook.Application();
if (outlook.Application.COMAddIns.Item("OutlookAddIn").Connect)
{
outlook.Application.COMAddIns.Item("OutlookAddIn").Connect = false;
}
else
{
outlook.Application.COMAddIns.Item("OutlookAddIn").Connect = true;
}
I want to attach a message from outlook in TFS workitem, so I created a small add-in in outlook to attach a message to workitem.
every thing is work fine, but it's work just once.
when i want to attach a message to a workitem, i've open a window form and select the workitem and attach the message. even though when the form closed it's wont be open again.
i check every thing and i found the problem : when i use tfsTeamProjectCollection.GetService(); the form wont be open again.
also I tried to put GetService in the startup, the Click event of button wont work.
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
TeamConfigurations = new Configuration();
_inspectors = Application.Inspectors;
TeamConfigurations.TempFolder = #"C:\EntekhabTfsOutlook\";
LoadSetting();
CheckTempFolder();
AddMenuBar();
}
and the click event
private void AddMailToAttachment_Click(CommandBarButton ctrl, ref bool cancelDefault)
{
var explorer = Application.ActiveExplorer();
var selection = explorer.Selection;
var frm = new FrmWorkItemSelector(selection);
frm.ShowDialog();
}
and finally
var tfsTeamProjectCollection = new TfsTeamProjectCollection(new Uri(url))
{
ClientCredentials = new TfsClientCredentials(true)
};
var workService = tfsTeamProjectCollection.GetService<WorkItemStore>();
i couldn't find the problem.
p.s: i don't want to use another add-in like http://www.teamsystemsolutions.com/teamlook/features/microsoft-outlook-integration.aspx Or another 3rd party tools.
the problem is not related to tfs
I chnage the Create toolbar method , it's work
http://msdn.microsoft.com/en-us/library/ms268864(v=vs.90).aspx
This is web single sign on code that runs on a .net 3.5 winform. The code runs fine for ie6 or ie8 as long as ie8 only has one tab open. The problem is that if the user opens a new tab (tab 2,3,etc.) and navigates to a web site (web form internal in the organization) the below code will be executed but the ie COM automation object will return the HTMLDocument for the first tab (Tab 1) even though tab 2 is the active tab. I can't find any IE tab references in the InternetExplorer or HTMLDocument classes anywhere. Actually, there's very little IE tab related documentation anywherer in the IE COM automation docs.
AutoResetEvent ie2_NavigateCompleteAutoReset;
/// <summary>
/// Given the handle of an Internet Explorer instance, this method performs single sign on to
/// several known web login forms.
/// </summary>
/// <param name="iEFramHandle"></param>
private void WebFormSignOn(int iEFramHandle)
{
foreach (SHDocVw.InternetExplorer ie2 in new SHDocVw.ShellWindows())
{
if (ie2.HWND == iEFramHandle)
{
while (true)
{
Thread.Sleep(100);
if (ie2.ReadyState == SHDocVw.tagREADYSTATE.READYSTATE_COMPLETE)
{
try
{
mshtml.HTMLDocument doc = (mshtml.HTMLDocument)ie2.Document;
ie2.NavigateComplete2 += new SHDocVw.DWebBrowserEvents2_NavigateComplete2EventHandler(ie2_NavigateComplete2);
ie2_NavigateCompleteAutoReset = new AutoResetEvent(false);
/*Find the username element and enter the user's username*/
mshtml.HTMLInputElement userID = (mshtml.HTMLInputElement)doc.all.item("username", 0);
userID.value = Globals.Username;
/*Find the password element and enter the user's password*/
mshtml.HTMLInputElement pwd = (mshtml.HTMLInputElement)doc.all.item("password", 0);
pwd.value = Globals.GetAppName();
/*Find the submit element/button and click it*/
mshtml.HTMLInputElement btnsubmit = (mshtml.HTMLInputElement)doc.all.item("submit", 0);
btnsubmit.click();
/*Wait up to 5 seconds for the form submit to complete.
This is to prevent this method from being called multiple times
while waiting for the form submit and subsequent navigation from completing.*/
ie2_NavigateCompleteAutoReset.WaitOne(5000);
return;
}
catch (Exception err)
{
Logger.Log(err.ToString(), Logger.StatusFlag.Error, this.ToString(), "WebFormSignOn");
return;
}
finally
{
/*Remove the event handler*/
ie2.NavigateComplete2 -= ie2_NavigateComplete2;
}
}
}
}
}
}
void ie2_NavigateComplete2(object pDisp, ref object URL)
{
ie2_NavigateCompleteAutoReset.Set();
}
It turns out that each tab in IE 8 has it's own process and handle. In the original code i was always getting the handle from the first IEFrame. I modified the code (below) and now it works. The change is that instead of looking for just the first IEFrame handle, the code also looks for a LocationURL that matches the url that triggerd the method that calls WebFormsSignOut.
private void WebFormSignOn(int iEFramHandle,string addressBarText)
{
var shellWindows = new SHDocVw.ShellWindows();
foreach (SHDocVw.InternetExplorer ie2 in shellWindows)
{
if (ie2.LocationURL==addressBarText)
{ //rest of the code (see orignal post)
Internet Explorer does not have any public tab APIs (beyond allowing you to target a navigation to a new foreground or background tab). Each ActiveX control or BHO is loaded individually into an individual tab instance. Trying to walk down from the ShellWindows collection isn't likely to work in general, instead you should have your plugin reach out to its hosting site (e.g. IObjectWithSite::SetSite will convey this info) which will allow you to determine your hosting tab.