首页 > 解决方案 > 使用 Marshal.GetActiveObject("Outlook.Application") 时的奇怪行为

问题描述

为了启动 Outlook 并等到它通过 Interop 可用,我编写了以下控制台应用程序:

static Outlook.Application outlook;
static void Main(string[] args)
{
    Console.WriteLine("Enter Profile:");
    var profile = Console.ReadLine();

    var process = Process.GetProcessesByName("Outlook").FirstOrDefault();
    if (process == null)
    {
        ProcessStartInfo startInfo = new ProcessStartInfo("outlook.exe", $"/profile \"{profile}\""); // MK_E_UNAVAILABLE appears until I click in the console window
        // ProcessStartInfo startInfo = new ProcessStartInfo("outlook.exe"); // Works as expected
        process = Process.Start(startInfo);
    }

    while (outlook == null && !process.HasExited)
    {
        try
        {
            outlook = (Outlook.Application)Marshal.GetActiveObject("Outlook.Application");
            break;
        }
        catch (System.Exception ex)
        {
            Console.WriteLine(ex.Message);
            outlook = null;
        }
        process.Refresh();
        Thread.Sleep(500);
    }

    if (outlook != null)
        Console.WriteLine($"Outlook is running.");

    Console.ReadKey();
}

我在任务管理器中验证了控制台应用程序在与 Outlook 相同的用户下运行(请参见此处)。

控制台应用程序的输出是:

Vorgang nicht verfügbar. (Ausnahme von HRESULT: 0x800401E3 (MK_E_UNAVAILABLE))
Vorgang nicht verfügbar. (Ausnahme von HRESULT: 0x800401E3 (MK_E_UNAVAILABLE))
Vorgang nicht verfügbar. (Ausnahme von HRESULT: 0x800401E3 (MK_E_UNAVAILABLE))

此消息会重复出现,直到我单击控制台窗口或调整其大小。然后输出更改为

Outlook is running.

仅在使用profile参数启动 Outlook 时才会发生这种情况。如果没有配置文件参数,则Outlook is running.在我在 Outlook 配置文件对话框中选择配置文件后,该消息会立即出现。

谁能解释这种行为的原因是什么?

标签: c#outlookoffice-interop

解决方案


尝试使用WaitForInputIdle()强制处理您的应用程序等到消息循环返回到空闲状态。当一个带有用户界面的进程正在执行时,每当操作系统向该进程发送一条 Windows 消息时,它的消息循环就会执行。然后该过程返回到消息循环。当一个进程在消息循环中等待消息时,它被称为处于空闲状态。此状态很有用,例如,当您的应用程序需要等待启动进程完成创建其主窗口之前,应用程序才能与该窗口进行通信。如果进程没有消息循环,则WaitForInputIdle()抛出InvalidOperationException.

另外我建议在检查它是否正在运行之前添加任何延迟。

Process.Start(startInfo);
Thread.Sleep(5000);

另一种可能的解决方案是将 COM 自动化与允许指定配置文件名称的Logon方法一起使用:

myNameSpace = Application.GetNamespace("MAPI"); 
myNameSpace.Logon("LatestProfile", Type.Missing , true, true); 

推荐阅读