سؤال

Got an issue where I am a COM type in c# using

    this.rtwbType = Type.GetTypeFromProgID(progId, true);
    this.rtwb = Activator.CreateInstance(this.rtwbType);

I am then doing some stuff, and when I am done I call exit on the rtwb - so it can close down, and then calling:

    Marshal.ReleaseComObject(this.rtwb);

In 2008 R2 this is fine and dandy - but the instance we take to 2012 an exception is thrown here.

System.Runtime.InteropServices.COMException (0x800706BA): The RPC server is unavailable. (Exception from HRESULT: 0x800706BA)

like I say works fine elsewhere.

Any pointers?

هل كانت مفيدة؟

المحلول

The following code reproduces this error (at least under Windows 8.1):

var type = Type.GetTypeFromProgID("InternetExplorer.Application", true);
dynamic ie = Activator.CreateInstance(type);

ie.Quit(); // this disconnects the COM proxy from the out-of-proc IE object

Marshal.ReleaseComObject(ie); // throws

Apparently, the implementation of ReleaseComObject is doing something more than calling IUnknown::Release, when it deals with a COM proxy object. My guess is, it may be calling IRemUnknown::RemRelease, which returns HRESULT with an error, so ReleaseComObject throws, because the out-of-proc object has been already disconnected and destroyed with ie.Quit().

Presumably, this behavior was introduced in Windows Server 2012.

The best thing you could probably do is to ignore this specific error:

try
{
    Marshal.ReleaseComObject(ie);
}
catch (COMException ex)
{
    // I'm getting 0x80010108, rather than 0x800706BA
    // The object invoked has disconnected from its clients. (Exception from HRESULT: 0x80010108 (RPC_E_DISCONNECTED)

    if ((uint)ex.ErrorCode != 0x80010108) 
        throw;
}

Updated: this is the expected behavior for an API like Quit, it doesn't violate any COM rules. The goal of Quit is to provide an API for explicit shutdown. I believe it uses CoDisconnectObject internally, as a part of the shutdown process. This disconnects all external proxy references, so the server knows it can shut down safely.

The fact that ReleaseComObject is still trying to do some stuff after that (like, most likely, calling IUnknown::QueryInterface on the disconnected proxy) is not a fault of the COM server.

نصائح أخرى

I'm assuming that rtwb is an InternetExplorer.Application, as Noseratio's test seems to replicate the issue rather well.

It seems Internet Explorer's Quit() method is not really safe to call outside the application's context, e.g. through out-of-process automation. It violates the rules of COM server applications.

For instance, Office applications keep running while there are external references, even after Quit(), so this is really a bit unexpected.

For safety, you can let the reference go when quitting, using the OnQuit event:

using System;
using System.Threading;
using System.Runtime.InteropServices;

public class TestIE
{
    public static void Main()
    {
        Console.WriteLine("Creating application");
        dynamic app = Activator.CreateInstance(Type.GetTypeFromProgID("InternetExplorer.Application"));
        Console.WriteLine("Created application");
        app.Visible = true;

        app.OnQuit += new Action(() => {
            Console.WriteLine("Entered OnQuit");
            Marshal.ReleaseComObject(app);
            app = null;
            Console.WriteLine("Leaving OnQuit");
        });

        Console.WriteLine("Sleeping");
        Thread.Sleep(5000); // enough time to see if iexplore.exe is running
        Console.WriteLine("Slept");

        Console.WriteLine("Quitting");
        app.Quit();
        Console.WriteLine("Quit");

        Console.WriteLine("Sleeping");
        Thread.Sleep(5000); // enough time to see if iexplore.exe is running
        Console.WriteLine("Slept");

        Console.WriteLine(app == null);
    }
}

If you really need to use ReleaseComObject, discard any exceptions it might throw. I'm not sure if you should discard only COMException, try it out during development.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top