I'm trying to follow this answer https://stackoverflow.com/a/47295752/1237135, in order to get a list of IIS Express websites, which involves referencing Microsoft.Web.dll (this is a .NET assembly, but presumably it uses COM calls) and calling this code
using (var runtimeStatusClient = new RuntimeStatusClient())
{
var workerProcess = runtimeStatusClient.GetWorkerProcess(19464);
//there's more but this is all that is needed for failure
}
It actually works, the code runs and has meaningful data, however a few seconds after it completes I get this error
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
is often associated with not using an STAThread model, but I've verified that the thread is STA.
The code works without error in a Console app environment, but not WPF.
The answer above mentions
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)
And I saw another answer https://stackoverflow.com/a/1058978/1237135 that talks about
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:
but I'm unclear if this applies here.
I also wondered if it's DLL Hell but that wouldn't explain why it works from a Console, would it?
EDIT: minimal reproduction.
Create a WPF project (I used 4.6.1 runtime) and in the codebehind for the MainWindow I used
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();
}
}
}
}
The only hard part is you need to find and reference Microsoft.Web.DLL (which if you have IIS Express on your machine should be in your GAC). You can do a Windows search on your C:\ drive and it will probably be somewhere like C:\Windows\assembly\GAC_MSIL\Microsoft.Web\7.1.0.0__31bf3856ad364e35
Do that and you'll see the problem.
Apparently I made 2 fatal assumptions:
Console Apps use STA. However this isn't true, it seems by default they are MTA. That figures I suppose as desktop apps have to explicitly state STA in the Main method.
To do COM interop you have to use STA. I assumed this because using STA is the go-to solution to E_NOINTERFACE problems on the web. However as I understand it now, some COM can use MTA. It appears for Microsoft.Web.DLL, you need MTA.
So my solution is to create a new thread (which will use MTA by default), eg.
public MainWindow()
{
InitializeComponent();
//Do use ThreadPool instead of this...
Thread thread = new Thread(new ThreadStart(() => { GetWebsites(); }));
thread.Start();
}
void GetWebsites()
{
User contributions licensed under CC BY-SA 3.0