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.
[uuid(32E81FDD-BCB0-481B-AD3C-3ED04BFA7D1F)]
library Bar
{
importlib("Foo.tlb");
[uuid(CF062BE8-86D2-4D9B-8D1D-D889A77DA876)]
interface IBar : IFoo { };
}
[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:
(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?
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:
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 LoadTypeLibEx
— it'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