首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >System.Runtime.InteropServices.COMException:“此函数无法执行,因为消息已更改

System.Runtime.InteropServices.COMException:“此函数无法执行,因为消息已更改
EN

Stack Overflow用户
提问于 2022-02-15 08:53:18
回答 1查看 965关注 0票数 0

我一直试图构建一个VSTO Outlook Addin。

Addin剂量如下:

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

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

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

4-保存邮件

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

System.Runtime.InteropServices.COMException:‘此函数无法执行,因为消息已更改。

此错误仅在尝试使用:getItem.Save();保存电子邮件时发生。

我不保存对邮件项的任何引用,也不会使用Marshal.ReleaseComObject(Item)释放所有对象;

保存函数在整个代码中只发生一次。

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

我该如何解决这个问题?

守则:

代码语言:javascript
运行
复制
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;
    }
}

“发票”类正在按丝带中的按钮调用:

代码语言:javascript
运行
复制
private void button1_Click(object sender, RibbonControlEventArgs e)
    {
        SaveInvoice currentData = new SaveInvoice();
        currentData.saveInvoice();
        //MessageBox.Show(currentData.getCurrentEmailData());
    }

提前感谢

编辑1:

我将代码更改为:

代码语言:javascript
运行
复制
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:

我删除了所有其他函数,只留下一个要保存:

代码语言:javascript
运行
复制
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事件之后被严重调用。

代码语言:javascript
运行
复制
     private void button1_Click(object sender, RibbonControlEventArgs e)
    {
       saveInvoice();
    }

在试图保存邮件项( System.Runtime.InteropServices.COMException ())时,有时仍然会出现mailObject.Save,我释放了我知道的所有底层COM对象

EN

回答 1

Stack Overflow用户

发布于 2022-02-15 09:23:18

在VSTO外接程序中,您应该使用由Application类提供的安全ThisAddin实例,而不是在代码中创建一个新实例。

代码语言:javascript
运行
复制
 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字段。此字段返回表示主机应用程序当前实例的对象。

相反,您的代码可能是这样的:

代码语言:javascript
运行
复制
 private void saveEmail(string guid, string folderStoreID) {

     Application outlookApplication = Globals.ThisAddin.Application;

您需要检查代码库和重构函数,以重用检索到的Outlook项,而不是在每个函数中分别获得它。例如,几乎在所有函数中,我都看到以下内容:

代码语言:javascript
运行
复制
 if (Globals.ThisAddIn.Application.ActiveWindow() is Explorer) {
            MailItem selObject = Globals.ThisAddIn.Application.ActiveExplorer().Selection[1];

但是,同时有一个函数返回当前显示的项:

代码语言:javascript
运行
复制
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对象。例如:

代码语言:javascript
运行
复制
MailItem selObject = Globals.ThisAddIn.Application.ActiveExplorer().Selection[1];

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

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

代码语言:javascript
运行
复制
foreach (Attachment attachment in mailObject.Attachments) {

注意,在这种情况下,每个迭代都不会释放附件对象。我建议使用for来显式地检索附件对象,然后在每次迭代中释放它。

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

代码语言:javascript
运行
复制
mailObject.Save();

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

代码语言:javascript
运行
复制
private void addLinkToEmail(string savedpath, MailItem mailItem) {
        mailItem.HTMLBody = savedpath + "<br>" + Environment.NewLine + mailItem.HTMLBody;
    }

代码在HTML字符串之前插入路径字符串,这意味着在<html><body>标记之前插入路径字符串。相反,您需要找到打开的<body>标记,并将字符串插入其中以保留任何格式。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71123533

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档