VB6 BadImplementsRefInCompatLib and .NET-COM Interop

You've created a VB6 COM library, MyLib1 (1.0).
You then have two usage scenarios for MyLib1:
  1. MyLib1 is, in turn, used by another VB6 library, MyLib2

  2. MyLib1 is used to generate a COM Interop assembly which is consumed by a .NET assembly called MyDotNetLib, which itself is also a COM interop library.

It so happened that you needed to add some new public methods to MyLib1, which would bring its versioning to 1.1. You were careful to craft the change such that it's backward compatible. Yet, something happened--you don't yet know exactly what it was--and when to try to recompile the above two libraries, you get the following problem scenarios:

  1. Recompile MyLib2, you get this error: BadImplementsRefInCompatLib

  2. Recompile MyDotNetLib, you get:
    Error 63 The assembly "..MyDotNetLib.dll" could not be converted to a type library. Type library exporter encountered an error while processing 'MyDotNetLib.MyComponent, MyDotNetLib'. Error: Referenced type is defined in managed component, which is imported from a type library that could not be loaded (type: 'MyLib1._IComponent'; component: '...\Interop.MyLib1.dll')

What did you do wrong?
As it turns out, the "something" in "something happened" above was an IDE debug session that had previously crashed, and MyLib1.vbp (1.1) remained registered in the system.

Why did that cause the above problems to occur?
The IDE looks for the latest version of the library (same GUID) to bind to.

How to avoid it?
Well, you can't avoid it, any more than you can avoid making VB6 crash.

What to do when it happens?

  • Temporarily remove reference to MyLib1 from your MyLib2 project, then attempt to re-add the reference, looking through the list of registered libraries to see how many instances of MyLib1 are registered--there should be only one. If you see a MyLib1.vbp showing in your list, you'll need to go through the registry and manually remove all CLSIDs with InprocServer32="MyLib1.vbp". The automatic registry clean tools, that I've tried, couldn't remove these entries for me.

  • Retrace your changes to make sure that you haven't made any modification to public interfaces that could break binary compatibility.

  • Use the OLE/COM Object Viewer tool to view Type Library info of MyLib1.dll. Then compare the output with a previous version of the binary to see if any of the interface GUIDs got inadvertently changed.

Pre-emptive comment:
Yes, I know VB6 (the IDE) has reached its end-of-life, which means MS support for it will be scarce--all the more reason for writing this post.


Terry C said...

Great article. I happen to be in the exact same situation however the suggestions did not help.

I did try changing the GUID on the class and it resulted in a different error which may provide some insight although I don't know how to resolve this error either.

The new error is: "The assembly "C:\....X.DLL" could not be converted to a type library. Type library exporter encountered an error while processing 'X.SomeClass, X'. Error: Element not found."

If anyone has some suggestions they would be greatly appreciated!

th2tran said...

Hi Terry,
I think your problem may be in the .NET assembly rather than in the VB6 library as is the case of this article.
google://"could+not+be+converted+to+a+type+library"+"Element+not+found" took me to this stackoverflow entry which seems to be a recurring root cause of the "Element not found" error for at least a couple of people:
make sure if you're specifying a .NET class GUID, that you are providing a unique GUID and not the same as the one for the assembly..
Not sure if it helps in your case, but thought I'd mention it.

Terry C said...

Hi th2tran,

Thanks for the quick feedback. I did have a slightly different scenario although the same error was occurring on COM Interop registration. In fact if I ran "regsvcs" from the Visual Studio command prompt it would actually go into an infinite loop which I believe is a bug with the .NET Framework although the underlying problem was the error that was detected while trying to do the same command from the IDE. Not sure what the IDE does different but it obviously takes a different path than running the EXE.

However, after plugging away at this for most of the day I was finally able to resolve it.

The class that was being referenced was a C++ IDL library that had been exported for use as a reference in a .NET project. The .NET project had a class that was implementing some interfaces from the C++ IDL. It was also inheriting from ServicedComponent (.NET COM+ component) therefore requiring a "regsvcs" versus "regasm".

What I discovered was that the UUIDs defined in the C++ IDL did not match the interface IIDs that Visual Studio was showing when I viewed the definition.

For example, Visual Studio showed this in the meta data for an IMessages interface:

public interface IMessages : IEnumerable

However, the actual C++ project had a definition like this:

helpstring("IMessages Interface"),
interface IMessages : IDispatch

After realizing that the GUIDs were different, and with the help of reading this article (and others) I focused on searching the Windows System registry to find the GUIDs defined in the C++ IDL project. I did find about 7 of them under HKEY CLASSES ROOT. I manually deleted them all. After doing so, the Interop registration from Visual Studio succeeded.

Therefore, it looks like these old registry entries that had the same ProgId were tripping up the COM+ registration. This is something that should probably be fixed in the .NET Framework since the "regsvcs" command goes into an infinite loop when in this bad registry state. I still am finding that a manual "regsvcs" command hangs although I can manage to get the component registered via the IDE.

The short story is that bogus registry entries were the root of the problem. I had previously tried running RegClean although it did not solve the problem.

What a headache it was to resolve, but at least I have some clue as to what went wrong. A prior installation of this IDL must have seeded the registry with these GUIDS and a subsequent IDL with different GUIDS (keeping the same class/interface/progId) trips up the registration process.