为什么我的 Outlook 添加随机停止工作?
Why does my Outlook add in randomly stop working?
我在 Outlook 2013 中有一个插件 运行,用于过滤垃圾邮件。它会查看传入的电子邮件,通过 half-dozen 启发式算法运行,如果其中 1 或 2 封 return 是肯定的,它会将邮件移至另一个文件夹进行检查;如果三个或更多被绊倒,它会永久删除邮件。
我的问题是它随机停止工作。加载项未标记为 "disabled" 或 "inactive";它只是不再工作了。禁用它然后启用它会使它重新联机,然后它将正常工作,包括 在它刚刚失败的邮件上。然后它会在几条消息之后再次随机失败。据我所知,没有出现任何错误;没有抛出异常。
我觉得这与 Outlook 对 add-ins 的限制有关,因为它是 (a) non-deterministic 并且 (b) 似乎发生得越频繁,我检查的越多。但我所做的一切都不沉重;其中大部分只是检查电子邮件地址、主题和 header 中的内容,或者在 body 中查找关键短语。仔细观察,每条消息似乎都在几分之一秒内处理完毕。我想第一个问题是,有没有办法查看 Outlook 对此的决策过程,即它正在跟踪的特定性能统计信息? (预计到达时间:Outlook 会在调用 add-in 时或仅在启动时强制执行其限制吗?我能找到的所有内容都仅指启动。)
似乎没有一张支票是负责任的;我已经分别禁用了每一个,而且它一直在发生。
这是中央代码。 SpamMarkerCheckList 是单个启发式检查的委托列表。 ClearAllMailMarkers() 执行诸如将邮件标记为已读、将优先级设置为正常、删除任何标志等操作。headers是一个字典,其中 header 名称作为键,字符串列表作为值。
private static void FilterInbox(object mailItem)
{
try
{
if (mailItem != null)
{
var mail = (Outlook.MailItem)mailItem;
var markers = SpamMarkersCount(mail, 3);
if (markers > 2)
{
PermanentlyDeleteEmail(mail);
}
else if (markers > 0)
{
ClearAllMailMarkers(mail);
mail.Move(_sequesteredFolder);
}
}
}
catch (Exception e)
{
MessageBox.Show("FilterSpam caught unexpected exception -- " + e);
}
}
private static int SpamMarkersCount(Outlook.MailItem mail, int threshold)
{
var spamMarkerCount = 0;
var headers = GetHeaderProperties(mail);
var emailAddressList = BuildAddressList(mail, headers);
var fullStringList = GetAllStrings(mail, headers);
foreach (var spamMarkerCheck in SpamMarkerCheckList)
{
if (spamMarkerCheck(mail, headers, emailAddressList, fullStringList))
{
spamMarkerCount++;
if (spamMarkerCount >= threshold)
{
return spamMarkerCount;
}
}
}
return spamMarkerCount;
}
我正在做的检查是:
- Non-ASCII 发件人姓名或地址中的字符(例如红心、购物车等)
- header 中的指标,例如存在 List-Unsubscribe 或 SPF、DKIM 或 DMARC 身份验证失败
- 格式错误或缺失的电子邮件地址
- 如果它是从假域发送的(通过执行 DNS 查找)
- 主题或发件人姓名中存在用户的电子邮件别名 ("delius1967 is a winner!")
- 如果它是从已知的域阻止列表发送的
- 如果它包含特定的短语(例如 "this is an advertisement")
我建议检查 Windows 事件查看器以获取特定于 Outlook 的记录。您的加载项很可能会在运行时触发异常。
首先,Microsoft Office 应用程序可以禁用行为异常的 VSTO 加载项。如果某个应用程序未加载您的 VSTO 外接程序,则该应用程序可能已硬禁用或软禁用您的 VSTO 外接程序。
当 VSTO 加载项导致应用程序意外关闭时,可能会发生硬禁用。如果您在 VSTO 外接程序中的启动事件处理程序正在执行时停止调试器,它也可能发生在您的开发计算机上。
当 VSTO 外接程序产生不会导致应用程序意外关闭的错误时,可能会发生软禁用。例如,如果应用程序在执行启动事件处理程序时引发未处理的异常,则它可能会软禁用 VSTO 加载项。
当您重新启用软禁用的 VSTO 外接程序时,应用程序会立即尝试加载 VSTO 外接程序。如果最初导致应用程序软禁用 VSTO 外接程序的问题尚未修复,应用程序将再次软禁用 VSTO 外接程序。在 How to: Re-enable a VSTO Add-in that has been disabled 文章中阅读更多相关信息。
其次,扩展了 Outlook 2010 的加载项弹性支柱,Outlook 2013 及更高版本监控加载项性能指标,例如加载项启动、关闭、文件夹切换、项目打开和调用频率。 Outlook 记录每个性能监控指标的经过时间(以毫秒为单位)。
例如,启动指标测量 Outlook 启动期间每个连接的加载项所需的时间。然后,Outlook 会计算 5 次连续迭代的中值启动时间。如果中值启动时间超过 1000 毫秒(1 秒),则 Outlook 会禁用加载项并向用户显示加载项已被禁用的通知。用户可以选择始终启用加载项,在这种情况下,即使加载项超过 1000 毫秒性能阈值,Outlook 也不会禁用该加载项。在 Performance criteria for keeping add-ins enabled 部分阅读更多相关信息。
您可能会发现 Outlook’s slow add-ins resiliency logic and how to always enable slow add-ins 文章很有帮助。
最后,Outlook 使用单线程单元模型,可以通过在运行时抛出异常来阻止来自辅助线程的任何调用。您应该只在主线程上使用 OOM。如果您需要在后台做任何处理,您可以考虑提取所需的纯数据并将其传递给处理。扩展 MAPI 还允许 运行 个辅助线程。
我找到了最直接的原因:垃圾收集。在调试器中 运行 一段时间后,我注意到加载项在 GC 事件后立即停止工作;再运行几次表明这种情况一直在发生。
最终原因在于我如何将函数与文件夹相关联:
private static Outlook.Account emailStore; // outside ThisAddIn_Startup
[...]
emailStore.DeliveryStore.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox).Items.ItemAdd += FilterInbox;
由于我不完全理解的原因,这会在 GC 之后导致问题,但这不会:
private static Outlook.Items emailItems; // outside ThisAddIn_Startup
[...]
emailItems = emailStore.DeliveryStore.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox).Items;
emailItems.ItemAdd += FilterInbox;
我明白为什么将其设为静态很重要,但我不明白为什么它会对作业产生影响。谁能对C#对象有更深入的了解,谁能解释一下,我很想听听。
我在 Outlook 2013 中有一个插件 运行,用于过滤垃圾邮件。它会查看传入的电子邮件,通过 half-dozen 启发式算法运行,如果其中 1 或 2 封 return 是肯定的,它会将邮件移至另一个文件夹进行检查;如果三个或更多被绊倒,它会永久删除邮件。
我的问题是它随机停止工作。加载项未标记为 "disabled" 或 "inactive";它只是不再工作了。禁用它然后启用它会使它重新联机,然后它将正常工作,包括 在它刚刚失败的邮件上。然后它会在几条消息之后再次随机失败。据我所知,没有出现任何错误;没有抛出异常。
我觉得这与 Outlook 对 add-ins 的限制有关,因为它是 (a) non-deterministic 并且 (b) 似乎发生得越频繁,我检查的越多。但我所做的一切都不沉重;其中大部分只是检查电子邮件地址、主题和 header 中的内容,或者在 body 中查找关键短语。仔细观察,每条消息似乎都在几分之一秒内处理完毕。我想第一个问题是,有没有办法查看 Outlook 对此的决策过程,即它正在跟踪的特定性能统计信息? (预计到达时间:Outlook 会在调用 add-in 时或仅在启动时强制执行其限制吗?我能找到的所有内容都仅指启动。)
似乎没有一张支票是负责任的;我已经分别禁用了每一个,而且它一直在发生。
这是中央代码。 SpamMarkerCheckList 是单个启发式检查的委托列表。 ClearAllMailMarkers() 执行诸如将邮件标记为已读、将优先级设置为正常、删除任何标志等操作。headers是一个字典,其中 header 名称作为键,字符串列表作为值。
private static void FilterInbox(object mailItem)
{
try
{
if (mailItem != null)
{
var mail = (Outlook.MailItem)mailItem;
var markers = SpamMarkersCount(mail, 3);
if (markers > 2)
{
PermanentlyDeleteEmail(mail);
}
else if (markers > 0)
{
ClearAllMailMarkers(mail);
mail.Move(_sequesteredFolder);
}
}
}
catch (Exception e)
{
MessageBox.Show("FilterSpam caught unexpected exception -- " + e);
}
}
private static int SpamMarkersCount(Outlook.MailItem mail, int threshold)
{
var spamMarkerCount = 0;
var headers = GetHeaderProperties(mail);
var emailAddressList = BuildAddressList(mail, headers);
var fullStringList = GetAllStrings(mail, headers);
foreach (var spamMarkerCheck in SpamMarkerCheckList)
{
if (spamMarkerCheck(mail, headers, emailAddressList, fullStringList))
{
spamMarkerCount++;
if (spamMarkerCount >= threshold)
{
return spamMarkerCount;
}
}
}
return spamMarkerCount;
}
我正在做的检查是:
- Non-ASCII 发件人姓名或地址中的字符(例如红心、购物车等)
- header 中的指标,例如存在 List-Unsubscribe 或 SPF、DKIM 或 DMARC 身份验证失败
- 格式错误或缺失的电子邮件地址
- 如果它是从假域发送的(通过执行 DNS 查找)
- 主题或发件人姓名中存在用户的电子邮件别名 ("delius1967 is a winner!")
- 如果它是从已知的域阻止列表发送的
- 如果它包含特定的短语(例如 "this is an advertisement")
我建议检查 Windows 事件查看器以获取特定于 Outlook 的记录。您的加载项很可能会在运行时触发异常。
首先,Microsoft Office 应用程序可以禁用行为异常的 VSTO 加载项。如果某个应用程序未加载您的 VSTO 外接程序,则该应用程序可能已硬禁用或软禁用您的 VSTO 外接程序。
当 VSTO 加载项导致应用程序意外关闭时,可能会发生硬禁用。如果您在 VSTO 外接程序中的启动事件处理程序正在执行时停止调试器,它也可能发生在您的开发计算机上。
当 VSTO 外接程序产生不会导致应用程序意外关闭的错误时,可能会发生软禁用。例如,如果应用程序在执行启动事件处理程序时引发未处理的异常,则它可能会软禁用 VSTO 加载项。
当您重新启用软禁用的 VSTO 外接程序时,应用程序会立即尝试加载 VSTO 外接程序。如果最初导致应用程序软禁用 VSTO 外接程序的问题尚未修复,应用程序将再次软禁用 VSTO 外接程序。在 How to: Re-enable a VSTO Add-in that has been disabled 文章中阅读更多相关信息。
其次,扩展了 Outlook 2010 的加载项弹性支柱,Outlook 2013 及更高版本监控加载项性能指标,例如加载项启动、关闭、文件夹切换、项目打开和调用频率。 Outlook 记录每个性能监控指标的经过时间(以毫秒为单位)。
例如,启动指标测量 Outlook 启动期间每个连接的加载项所需的时间。然后,Outlook 会计算 5 次连续迭代的中值启动时间。如果中值启动时间超过 1000 毫秒(1 秒),则 Outlook 会禁用加载项并向用户显示加载项已被禁用的通知。用户可以选择始终启用加载项,在这种情况下,即使加载项超过 1000 毫秒性能阈值,Outlook 也不会禁用该加载项。在 Performance criteria for keeping add-ins enabled 部分阅读更多相关信息。
您可能会发现 Outlook’s slow add-ins resiliency logic and how to always enable slow add-ins 文章很有帮助。
最后,Outlook 使用单线程单元模型,可以通过在运行时抛出异常来阻止来自辅助线程的任何调用。您应该只在主线程上使用 OOM。如果您需要在后台做任何处理,您可以考虑提取所需的纯数据并将其传递给处理。扩展 MAPI 还允许 运行 个辅助线程。
我找到了最直接的原因:垃圾收集。在调试器中 运行 一段时间后,我注意到加载项在 GC 事件后立即停止工作;再运行几次表明这种情况一直在发生。
最终原因在于我如何将函数与文件夹相关联:
private static Outlook.Account emailStore; // outside ThisAddIn_Startup
[...]
emailStore.DeliveryStore.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox).Items.ItemAdd += FilterInbox;
由于我不完全理解的原因,这会在 GC 之后导致问题,但这不会:
private static Outlook.Items emailItems; // outside ThisAddIn_Startup
[...]
emailItems = emailStore.DeliveryStore.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox).Items;
emailItems.ItemAdd += FilterInbox;
我明白为什么将其设为静态很重要,但我不明白为什么它会对作业产生影响。谁能对C#对象有更深入的了解,谁能解释一下,我很想听听。