从 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 个致命的假设:
控制台应用程序使用 STA。但这不是真的,默认情况下它们似乎是 MTA。我想这个数字是因为桌面应用程序必须在 Main 方法中明确声明 STA。
要进行 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()
{
我正在尝试遵循此答案 ,以便获得 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 个致命的假设:
控制台应用程序使用 STA。但这不是真的,默认情况下它们似乎是 MTA。我想这个数字是因为桌面应用程序必须在 Main 方法中明确声明 STA。
要进行 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()
{