System.Runtime.InteropServices.COMException:"无法执行此功能,因为消息已更改



我一直在尝试构建一个VSTO Outlook加载项。

Addin剂量如下:

1-将电子邮件附件保存到特定路径

2-在电子邮件正文中写入保存的附件的路径

3-从电子邮件中删除附件

4-保存电子邮件

我的问题是,每当我试图在用链接编辑后保存电子邮件时,我都会收到以下错误:

System.Runtime.InteropServices.COMException:'由于消息已更改,无法执行此函数。

只有在尝试使用以下方法保存电子邮件时才会发生错误:getItem.save((

我没有持有对mailitem的任何引用,也使用Marshal.ReleaseComObject(item(释放了所有对象;

save函数在整个代码中只出现一次。

注意:异常并不总是抛出的,有时保存有效,有时无效

我该如何解决这个问题?

代码:

class SaveInvoice {
dynamic activeWindow = Globals.ThisAddIn.Application.ActiveWindow();
private MailItem getCurrentEmailObject() {

try {
if (activeWindow is Explorer) {
dynamic i = activeWindow.currentFolder;
if (activeWindow.Selection.Count > 0) {
object selObject = activeWindow.Selection[1];
if (selObject is MailItem) {
MailItem mailItem = (selObject as MailItem);
return mailItem;
}
}
}
else {
return activeWindow.currentitem;
}
} catch (System.Exception) {
return null;
}
return null;
}
private void addLinkToEmail(string savedpath) {
if (Globals.ThisAddIn.Application.ActiveWindow() is Explorer) {
MailItem selObject = Globals.ThisAddIn.Application.ActiveExplorer().Selection[1];
selObject.HTMLBody = savedpath + "<br>" + Environment.NewLine + selObject.HTMLBody;
Marshal.ReleaseComObject(selObject);

}
else {
MailItem selObject = Globals.ThisAddIn.Application.ActiveInspector().CurrentItem;
selObject.HTMLBody = savedpath + "<br>" + Environment.NewLine + selObject.HTMLBody;
Marshal.ReleaseComObject(selObject);
}
}
private void saveEmail(string guid, string folderStoreID) {
Application outlookApplication = new Application();
NameSpace outlookNamespace = outlookApplication.GetNamespace("MAPI");      
MailItem getItem = (MailItem)outlookNamespace.GetItemFromID(guid, folderStoreID);
getItem.Save();   //the Expection is Thrown here

Marshal.ReleaseComObject(getItem);
}
private string SaveFileTo(string initStorage, string fileName) {
SaveFileDialog fd = new SaveFileDialog();
fd.AddExtension = true;
fd.ValidateNames = true;
fd.FileName = fileName;
fd.InitialDirectory = initStorage;
fd.Filter = "PDF files|*.pdf";
if (fd.ShowDialog() == DialogResult.OK)
return fd.FileName;
return "";
}
public void saveInvoice() {
MailItem mailObject = getCurrentEmailObject();
if (mailObject != null) {
string CustomerName = "CutomerNameTest"

foreach (Attachment attachment in mailObject.Attachments) {
string saveToPath = "stringPath";           
if (attachment.FileName.Contains(".pdf")) {
attachment.SaveAsFile(saveToPath);
attachment.Delete();
addLinkToEmail(saveToPath);
}
}
string guid = mailObject.EntryID;
var folderStoreID = mailObject.Parent.StoreID;
Marshal.ReleaseComObject(mailObject);
try {
saveEmail(guid, folderStoreID);
} catch (COMException e) {
MessageBox.Show(e.ToString());
}
activeWindow = null;
}
}
private string getSenderEmailAddress(MailItem mail) {
AddressEntry sender = mail.Sender;
string SenderEmailAddress = "";
if (sender.AddressEntryUserType == OlAddressEntryUserType.olExchangeUserAddressEntry
|| sender.AddressEntryUserType == OlAddressEntryUserType.olExchangeRemoteUserAddressEntry) {
ExchangeUser exchUser = sender.GetExchangeUser();
if (exchUser != null) {
SenderEmailAddress = exchUser.PrimarySmtpAddress;
}
}
else {
SenderEmailAddress = mail.SenderEmailAddress;
}
return SenderEmailAddress;
}
}

单击功能区中的按钮调用发票类:

private void button1_Click(object sender, RibbonControlEventArgs e)
{
SaveInvoice currentData = new SaveInvoice();
currentData.saveInvoice();
//MessageBox.Show(currentData.getCurrentEmailData());
}

提前感谢

编辑1:

我把代码改成:

class SaveInvoice {
dynamic activeWindow = Globals.ThisAddIn.Application.ActiveWindow();
private MailItem getCurrentEmailObject() {

try {
if (activeWindow is Explorer) {
dynamic i = activeWindow.currentFolder;
if (activeWindow.Selection.Count > 0) {
object selObject = activeWindow.Selection[1];
if (selObject is MailItem) {
MailItem mailItem = (selObject as MailItem);
return mailItem;
}
}
}
else {
return activeWindow.currentitem;
}
} catch (System.Exception) {
return null;
}
return null;
}
private void addLinkToEmail(string savedpath, MailItem mailItem) {
mailItem.HTMLBody = savedpath + "<br>" + Environment.NewLine + mailItem.HTMLBody;
}

public void saveInvoice() {
MailItem mailObject = getCurrentEmailObject();
if (mailObject != null) {
foreach (Attachment attachment in mailObject.Attachments) {
string saveToPath = "savePath";
attachment.SaveAsFile(saveToPath);
attachment.Delete();
addLinkToEmail(saveToPath, mailObject);

}
mailObject.Save();
Marshal.ReleaseComObject(mailObject);
activeWindow = null;
}
}
private string getSenderEmailAddress(MailItem mail) {
AddressEntry sender = mail.Sender;
string SenderEmailAddress = "";
if (sender.AddressEntryUserType == OlAddressEntryUserType.olExchangeUserAddressEntry
|| sender.AddressEntryUserType == OlAddressEntryUserType.olExchangeRemoteUserAddressEntry) {
ExchangeUser exchUser = sender.GetExchangeUser();
if (exchUser != null) {
SenderEmailAddress = exchUser.PrimarySmtpAddress;
}
}
else {
SenderEmailAddress = mail.SenderEmailAddress;
}
return SenderEmailAddress;
}
}

有时仍然会出现错误,我试图在整个代码中使用相同的邮件对象,但问题出现在中

编辑3:

我删除了所有其他功能,只留下一个保存:

public void saveInvoice(){
MailItem mailObject = Globals.ThisAddIn.Application.ActiveWindow().Selection[1];
try {
if (mailObject != null) {
foreach (Attachment attachment in mailObject.Attachments) {
string saveToPath = "saveToPath";
attachment.SaveAsFile(saveToPath);
attachment.Delete();
mailObject.Save();
}
}
Marshal.ReleaseComObject(mailObject);
Marshal.ReleaseComObject(Globals.ThisAddIn.Application.ActiveWindow().Selection[1]);
Marshal.ReleaseComObject(Globals.ThisAddIn.Application.ActiveWindow());
Marshal.ReleaseComObject(Globals.ThisAddIn.Application);
}
catch (System.Exception){
throw;
}

}

在Ribbon1.cs 的点击事件之后直接调用

private void button1_Click(object sender, RibbonControlEventArgs e)
{
saveInvoice();
}

System.Runtime.InteropServices.COMException有时在尝试保存邮件项(mailObject.save(((时仍然会发生,我释放了我从中知道的所有底层COM对象

在VSTO加载项中,您应该使用ThisAddin类提供的安全Application实例,而不是在代码中创建新实例。

private void saveEmail(string guid, string folderStoreID) {
Application outlookApplication = new Application();

程序VSTO加载项声明如下:

您可以在ThisAddIn类中开始编写VSTO加载项代码。Visual Studio会在VSTO外接程序项目中的ThisAddIn.vb(在Visual Basic中(或ThisAddIn.cs(在C#中(代码文件中自动生成此类。当Microsoft Office应用程序加载VSTO外接程序时,Visual Studio Tools for Office运行时会自动为您实例化此类。

要访问主机应用程序的对象模型,请使用ThisAddIn类的Application字段。此字段返回一个对象,该对象表示主机应用程序的当前实例。

您的代码可能看起来像这样:

private void saveEmail(string guid, string folderStoreID) {
Application outlookApplication = Globals.ThisAddin.Application;

您需要查看代码库并重构函数,以重用检索到的Outlook项,而不是将其单独放入每个函数中。例如,几乎在所有函数中,我都看到以下内容:

if (Globals.ThisAddIn.Application.ActiveWindow() is Explorer) {
MailItem selObject = Globals.ThisAddIn.Application.ActiveExplorer().Selection[1];

但同时还有一个函数可以返回当前显示的项目:

private MailItem getCurrentEmailObject() {

try {
if (activeWindow is Explorer) {
dynamic i = activeWindow.currentFolder;
if (activeWindow.Selection.Count > 0) {
object selObject = activeWindow.Selection[1];
if (selObject is MailItem) {
MailItem mailItem = (selObject as MailItem);
return mailItem;
}
}
}
else {
return activeWindow.currentitem;
}
} catch (System.Exception) {
return null;
}
return null;
}

因此,您需要提取一次它,然后通过代码重复使用,直到完成所有修改/工作。

此外,我建议立即释放底层COM对象。例如:

MailItem selObject = Globals.ThisAddIn.Application.ActiveExplorer().Selection[1];

一行代码包含多个返回COM对象的属性和方法调用。完成后,内存中会留下一些对象。ActiveExplorer方法返回Explorer类的一个实例。然后调用Selection属性,该属性返回同样留在内存中的Selection对象。

此外,我注意到您在代码中使用了foreach循环:

foreach (Attachment attachment in mailObject.Attachments) {

注意,在这种情况下,附件对象不会随每次迭代而释放。我建议使用for,这样可以显式地检索附件对象,然后在每次迭代时释放它。

请注意,不需要每次删除附件时都调用Save方法。因此,在循环之外进行以下调用:

mailObject.Save();

最后,HTMLBody属性返回一个字符串,该字符串表示HTML格式的消息正文。因此,当您为属性设置任何值时,您需要确保您设置了一个有效的HTML字符串,例如:

private void addLinkToEmail(string savedpath, MailItem mailItem) {
mailItem.HTMLBody = savedpath + "<br>" + Environment.NewLine + mailItem.HTMLBody;
}

代码在HTML字符串之前插入一个路径字符串,也就是说在<html><body>标记之前。相反,您需要找到打开的<body>标记,并在那里插入字符串以保留任何格式。

相关内容

最新更新