从 WPF 调用 COM,即使使用 STAThread 也会获得 E_NOINTERFACE,但在控制台应用程序中有效

Calling COM from WPF, getting E_NOINTERFACE even with STAThread, but works in Console app

我正在尝试遵循此答案 ,以便获得 IIS Express 网站的列表,其中涉及引用 Microsoft.Web.dll(这是一个 .NET 程序集,但大概它使用COM 调用)并调用此代码

using (var runtimeStatusClient = new RuntimeStatusClient())
{
  var workerProcess = runtimeStatusClient.GetWorkerProcess(19464);
  //there's more but this is all that is needed for failure
}

它确实有效,代码运行并具有有意义的数据,但是在它完成几秒钟后我收到此错误

System.InvalidCastException: 
'Unable to cast COM object of type 'System.__ComObject' 
to interface type 'Microsoft.Web.RuntimeStatus.IRsca2_WorkerProcess'. 
This operation failed because the QueryInterface call on the COM component
for the interface with IID '{B1341209-7F09-4ECD-AE5F-3EE40D921870}' failed 
due to the following error: No such interface supported (Exception from 
HRESULT: 0x80004002 (E_NOINTERFACE)).'

E_NOINTERFACE 通常与不使用 STAThread 模型相关联,但我已经验证该线程是 STA。

该代码在控制台应用程序环境中正常运行,但在 WPF 环境中不正常。

上面的回答提到

I looked into RegisteredUrlsInfo (in Microsoft.Web.dll) as well and found that it's using two COM interfaces,

IRsca2_Core (F90F62AB-EE00-4E4F-8EA6-3805B6B25CDD) IRsca2_WorkerProcess (B1341209-7F09-4ECD-AE5F-3EE40D921870)

而且我看到了另一个关于

的答案

Try adding this to your App.exe.manifest:

iid="{C677308A-AC0F-427D-889A-47E5DC990138}"
proxyStubClsid32="{00020424-0000-0000-C000-000000000046}"
baseInterface="{00000000-0000-0000-C000-000000000046}" tlbid = "{PUT-YOUR-TLB-GUID-HERE}" /> Where TLBID can be found from your Visual Studio generated Native.Namespace.Assembly.Name.manifest, looking like this:

但我不清楚这是否适用于此。

我也想知道它是否是 DLL 地狱,但这不能解释为什么它可以在控制台上运行,对吗?

编辑:最小复制。

创建一个 WPF 项目(我使用 4.6.1 运行时)并在我使用的 MainWindow 的代码隐藏中

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfApp2
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();


            using (var runtimeStatusClient = new Microsoft.Web.RuntimeStatus.RuntimeStatusClient())
            {
                var workerProcess = runtimeStatusClient.GetAllWorkerProcesses();

            }
        }
    }
}

唯一困难的部分是您需要找到并引用 Microsoft.Web.DLL(如果您的机器上有 IIS Express,则应该在您的 GAC 中)。您可以在 C:\ 驱动器上进行 Windows 搜索,它可能位于 C:\Windows\assembly\GAC_MSIL\Microsoft.Web.1.0.0__31bf3856ad364e35

之类的地方

这样做,你就会发现问题所在。

显然我做了 2 个致命的假设:

  1. 控制台应用程序使用 STA。但这不是真的,默认情况下它们似乎是 MTA。我想这个数字是因为桌面应用程序必须在 Main 方法中明确声明 STA。

  2. 要进行 COM 互操作,您必须使用 STA。我假设这是因为使用 STA 是解决网络上 E_NOINTERFACE 问题的首选方法。然而,据我所知,一些 COM 可以使用 MTA。它显示 Microsoft.Web.DLL,您需要 MTA。

所以我的解决方案是创建一个新线程(默认使用MTA),例如

    public MainWindow()
    {
        InitializeComponent();

        //Do use ThreadPool instead of this...
        Thread thread = new Thread(new ThreadStart(() => { GetWebsites(); }));
        thread.Start();

    }

    void GetWebsites()
    {