Accessing an ITypeInfo that references an ITypeInfo from an importlib-ed unregistered type library causes TYPE_E_CANTLOADLIBRARY error

0

I am using the Automation API from .NET (System.Runtime.InteropServices.ComTypes) to inspect a type library Bar.tlb that I generated myself (see below). This type library declares an interface IBar which inherits from interface IFoo which is defined in an imported type library Foo.tlb. Inspecting the ITypeInfo representing IBar causes an exception. (Code follows below.)

Before I get to my code, here's how I generated the Bar.tlb type library.

Bar.idl:

[uuid(32E81FDD-BCB0-481B-AD3C-3ED04BFA7D1F)]
library Bar
{
    importlib("Foo.tlb");

    [uuid(CF062BE8-86D2-4D9B-8D1D-D889A77DA876)]
    interface IBar : IFoo { };
}

Foo.idl:

[uuid(22E81FDD-BCB0-481B-AD3C-3ED04BFA7D1E)]
library Foo
{
    importlib("stdole32.tlb");

    [uuid(BF062BE8-86D2-4D9B-8D1D-D889A77DA875)]
    interface IFoo : IUnknown { };
}

I compiled both IDL files using the following commands, which succeeded without any errors or warnings:

midl.exe /mktyplib203 /env win32 /i … /tlb Foo.tlb Foo.idl
midl.exe /mktyplib203 /env win32 /i … /tlb Bar.tlb Bar.idl

Now what I am trying to do is this:

using System.Runtime.InteropServices;
using ITypeLib = System.Runtime.InteropServices.ComTypes.ITypeLib;
using ITypeInfo = System.Runtime.InteropServices.ComTypes.ITypeInfo;

static class Program
{
    [DllImport("oleaut32.dll", CharSet = CharSet.Unicode, PreserveSig = false)]
    static extern ITypeLib LoadTypeLibEx(string path, REGKIND regkind);

    enum REGKIND { REGKIND_NONE = 2 }

    public static void Main()
    {
        ITypeLib typeLib = LoadTypeLibEx(@"C:\Path\To\Bar.tlb", REGKIND.REGKIND_NONE);
        ITypeInfo typeInfo;
        typeLib.GetTypeInfo(0, out typeInfo);
        IntPtr typeAttrPtr;
        typeInfo.GetTypeAttr(out typeAttrPtr); //! COMException: TYPE_E_CANTLOADLIBRARY
        …                                      //                (HRESULT 0x80029c4a)
    }
}

The exception is thrown on the line marked with //!. The inspected ITypeInfo is the one for the IBar interface.

I understand that the Automation API must have trouble locating the inherited interface IFoo, which is contained in a different type library that is also not registered.

But apparently it should be possible to inspect Bar.tlb anyway. OleView.exe manages just fine:

Inspecting <code>Bar.tlb</code> in OleView works fine.

(Yes, it gives a warning about not being able to reconstruct the external type library's filename, which is because I did not register Foo.tlb. That's not what I am worried about.)

If OleView.exe can inspect IBar without crashing, why does my code crash for something as simple as typeInfo.GetTypeAttr()? How do I fix this?

c#
.net
ole
midl
ole-automation

1 Answer

0

TL;DR: The Automation API appears to access type libraries in the current working directory behind the scenes in order to resolve ITypeInfo references. The issue can be resolved by doing this:

// using static System.IO.Directory;
SetCurrentDirectory(@"C:\Path\To");
ITypeLib typeLib = LoadTypeLibEx("Bar.tlb", REGKIND.REGKIND_NONE);

instead of:

ITypeLib typeLib = LoadTypeLibEx(@"C:\Path\To\Bar.tlb", REGKIND.REGKIND_NONE);

I experimented some more and found out that OleView.exe can only successfully inspect IBar type details in Bar.tlb if Foo.tlb is present in the same directory. Once I move Foo.tlb off somewhere else, OleView.exe fails just like my own code does:

Same COM error occurs in <code>OleView.exe</code> once <code>Foo.tlb</code> is moved away from <code>Bar.tlb</code>

I wanted to see which API calls OleView.exe used to access type libraries, so I checked:

dumpbin.exe /imports OleView.exe
dumpbin.exe /exports C:\WINDOWS\…\oleaut32.dll

and found that it references LoadTypeLib — not LoadTypeLibEx. The two functions differ in how they do type library registration. So I looked up the MSDN documentation for LoadTypeLib and found this:

"LoadTypeLib will not register the type library if the path of the type library is specified."

Which gave me the idea to rewrite my code as shown at the beginning of this answer.

It's important however to point out that contrary to what the quoted text suggests, it is not the presence or absence of a directory in the path passed to LoadTypeLibExit's the call to Directory.SetCurrentDirectory that resolves the error. The following would work as well:

// using static System.IO.Directory;
SetCurrentDirectory(@"C:\Path\To");
ITypeLib typeLib = LoadTypeLibEx("C:\Path\To\Bar.tlb", REGKIND.REGKIND_NONE);

User contributions licensed under CC BY-SA 3.0