如何通过VSTO可靠检测Outlook中新增的账户或存储?
可靠检测Outlook新增账户/存储的解决方案
1. 修复StoreAdd事件失效问题:保持对象生命周期
你之前的代码中Stores是局部变量,方法执行完成后会被垃圾回收,导致事件订阅失效。需要将其声明为类级字段,确保持续被引用:
private Outlook.Stores _outlookStores; private void ThisAddIn_Startup(object sender, System.EventArgs e) { Outlook.NameSpace ns = Application.Session; // 保存为类级变量,避免被GC回收 _outlookStores = ns.Stores; _outlookStores.StoreAdd += Stores_StoreAdd; _outlookStores.StoreRemove += Stores_StoreRemove; } private void Stores_StoreAdd(Outlook.Store Store) { // 处理新增存储逻辑 } private void Stores_StoreRemove(Outlook.Store Store) { // 处理删除存储逻辑(可选) }
2. 直接使用AccountAdded事件(Exchange账户优先)
Outlook的Application对象提供了AccountAdded事件,专门针对账户新增操作,比StoreAdd更直接适配Exchange场景:
private void ThisAddIn_Startup(object sender, System.EventArgs e) { Application.AccountAdded += Application_AccountAdded; } private void Application_AccountAdded(Outlook.Account Account) { // 账户新增触发,若需要关联到对应Store,可延迟后遍历查找 Task.Delay(2000).ContinueWith(_ => { this.Invoke(new Action(() => { foreach (Outlook.Store store in Application.Session.Stores) { if (store.Account == Account) { // 找到对应存储,执行操作 break; } } })); }); }
注:Exchange账户配置是异步过程,AccountAdded触发时Store可能未同步完成,延迟操作可提高可靠性。
3. 改进轮询方案(兜底方案)
之前的轮询仅对比Count不准确,改为遍历存储名称列表,精准捕获新增项:
private List<string> _existingStoreNames; private CancellationTokenSource _pollingCts; private void ThisAddIn_Startup(object sender, System.EventArgs e) { // 初始化现有存储名称列表 _existingStoreNames = Application.Session.Stores.Cast<Outlook.Store>() .Select(s => s.DisplayName) .ToList(); // 启动异步轮询 _pollingCts = new CancellationTokenSource(); Task.Run(() => PollForNewStores(_pollingCts.Token), _pollingCts.Token); } private async Task PollForNewStores(CancellationToken token) { while (!token.IsCancellationRequested) { await Task.Delay(5000, token); // 每5秒轮询一次 try { this.Invoke(new Action(() => { var currentStores = Application.Session.Stores.Cast<Outlook.Store>() .Select(s => s.DisplayName) .ToList(); // 找出新增的存储名称 var newStoreNames = currentStores.Except(_existingStoreNames).ToList(); if (newStoreNames.Any()) { foreach (var name in newStoreNames) { var targetStore = Application.Session.Stores.Cast<Outlook.Store>() .FirstOrDefault(s => s.DisplayName == name); if (targetStore != null) { // 执行新增存储的处理逻辑 } } // 更新现有列表 _existingStoreNames = currentStores; } })); } catch (Exception ex) { // 处理异常,如Outlook对象调用失败 } } } private void ThisAddIn_Shutdown(object sender, System.EventArgs e) { // 终止轮询任务 _pollingCts?.Cancel(); _pollingCts?.Dispose(); }
关键注意点
- 所有Outlook对象模型的操作必须在UI线程执行,异步代码中需用
this.Invoke切换线程。 - 优先使用
AccountAdded事件+类级Stores对象的组合方案,轮询仅作为兜底。
内容的提问来源于stack exchange,提问作者Willy
相关产品推荐
相关产品推荐

