我一直试图构建一个VSTO Outlook Addin。
Addin剂量如下:
1-将电子邮件附件保存到特定路径。
2-写入电子邮件正文中保存的附件的路径。
3-从电子邮件中删除附件
4-保存邮件
我的问题是,每当我试图在用链接编辑电子邮件后保存它,我就会得到以下错误:
System.Runtime.InteropServices.COMException:‘此函数无法执行,因为消息已更改。
此错误仅在尝试使用:getItem.Save();保存电子邮件时发生。
我不保存对邮件项的任何引用,也不会使用Marshal.ReleaseComObject(Item)释放所有对象;
保存函数在整个代码中只发生一次。
注意:异常并不总是抛出,有时保存有效,有时无效。
我该如何解决这个问题?
守则:
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;
}
}
有时仍然会出现错误,我试图在整个代码中使用相同的mailobject,但问题正在发生。
编辑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的click事件之后被严重调用。
private void button1_Click(object sender, RibbonControlEventArgs e)
{
saveInvoice();
}
在试图保存邮件项( System.Runtime.InteropServices.COMException ())时,有时仍然会出现mailObject.Save,我释放了我知道的所有底层COM对象
发布于 2022-02-15 09:23:18
在VSTO外接程序中,您应该使用由Application
类提供的安全ThisAddin
实例,而不是在代码中创建一个新实例。
private void saveEmail(string guid, string folderStoreID) {
Application outlookApplication = new Application();
VSTO外接程序声明如下:
您可以开始在ThisAddIn类中编写VSTO外接程序代码。Visual在VSTO外接程序项目中的ThisAddIn.vb (在Visual中)或ThisAddIn.cs (在C#中)代码文件中自动生成该类。当Microsoft应用程序加载VSTO外接程序时,运行时会自动为您实例化该类。若要访问主机应用程序的对象模型,请使用
Application
类的ThisAddIn
字段。此字段返回表示主机应用程序当前实例的对象。
相反,您的代码可能是这样的:
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>
标记,并将字符串插入其中以保留任何格式。
https://stackoverflow.com/questions/71123533
复制相似问题