How can I "replace" the interface for an object returned from COM to managed code?

196 views Asked by At

I face some weird performance issues and I suspect that they may arise from having a dynamic specified as return type of a COM interface method. Specifically this is how IHTMLDOMChildrenCollection interface ends up in the interop:

[DefaultMember("item")]
[Guid("3050F5AB-98B5-11CF-BB82-00AA00BDCE0B")]
[TypeLibType(4160)]
public interface IHTMLDOMChildrenCollection : IEnumerable
{
    [DispId(-4)]
    [TypeLibFunc(65)]
    IEnumerator GetEnumerator();
    [DispId(0)]
    dynamic item(int index); // HERE is the dynamic

    [DispId(1500)]
    int length { get; }
}

and I suspect that once my code calls item() method then some extra wiring is created which reduces performance in the other code.

The code is organized like this:

var document = (IHTMLDocument)documentBrowser.Document;
var selector = (IDocumentSelector)document;
IHTMLDOMChildrenCollection allElements = selector.querySelectorAll("*");
int length = allElements.length;
for (int index = 0; index < length; index++)
{
    var item = allElements.item(index);
}

This answer states that performance maybe would improve if IHTMLDOMChildrenCollection interface was declared such that item() returned object, not dynamic.

Would it be possible to declare a new interface and somehow convince the CLR to give me an RCW implementing that interface?

I tried the following:

[DefaultMember("item")]
[Guid("3050F5AB-98B5-11CF-BB82-00AA00BDCE0B")]
[TypeLibType(4160)]
public interface IHTMLDOMChildrenCollectionCopy : IEnumerable
{
    [DispId(-4)]
    [TypeLibFunc(65)]
    IEnumerator GetEnumerator();
    [DispId(0)]
    [return: MarshalAs(UnmanagedType.IDispatch)]
    object item(int index); // HERE it's object, not dynamic

    [DispId(1500)]
    int length { get; }
}

and in the code:

IHTMLDOMChildrenCollection allElementsOriginal = selector.querySelectorAll("*");
var unknown = Marshal.GetIUnknownForObject(allElements);
var newElements = (IHTMLDOMChildrenCollectionCopy)Marshal.GetTypedObjectForIUnknown(
   unknown , typeof(IHTMLDOMChildrenCollectionCopy));
int length = newElements.length; // this fails

and it works until the line reading .length is executed. The latter fails with

System.AccessViolationException: 'Attempted to read or write protected memory. This is often an indication that other memory is corrupt.'

Should this work? What am I doing wrong?

1

There are 1 answers

0
sharptooth On

Yes, this should work. The missing part was the interface was not decorated with [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]

This is complete declaration:

[DefaultMember("item")]
[Guid("3050F5AB-98B5-11CF-BB82-00AA00BDCE0B")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
[TypeLibType(4160)]
public interface IHTMLDOMChildrenCollectionCopy : IEnumerable
{
   // same members as in the question
}