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

-1

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.

c#
.net
wpf
com

1 Answer

0

Apparently I made 2 fatal assumptions:

  1. 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.

  2. 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()
    {
answered on Stack Overflow Jun 3, 2018 by Jim W says reinstate Monica • edited Jun 4, 2018 by Jim W says reinstate Monica

User contributions licensed under CC BY-SA 3.0