3

I used to have a Visual Basic (VB6) COM DLL (let's call it "dllouter") which referenced another Visual Basic (VB6) COM DLL (let's call it "dllinner") with an interface "Interface". "dllouter" loaded "dllinner" in the following way:

Public objCom As dllinner.Interface

Set objCom = New dllinner.Interface

When "dllinner" version changed, without changes in "Interface", "dllouter" was able to load it without problems, without needing to recompile it.

After that, I replaced "dllinner" with a C# .NET assembly (VS2015) with [assembly: ComVisible(true)], and recompiling "dllouter" everything was fine and working.

But if I change [assembly: AssemblyVersion("1.0.0")] to a newer version then Set objCom = New dllinner.Interface fails. I need to recompile "dllouter" after updating the reference to the new "dllinner" to make it work.

I have noticed that comparing the .vbp files with references to Visual Basic "dllinner" and .NET "dllinner" there is a difference in the version listed after the GUID:

Reference=*\G{6B0651C5-5225-42A6-841F0322797E5018}#1.0#0#...

The value in bold is updated for the .NET assembly with the new assembly version (e.g. 2.0) while it remains unchanged for the Visual Basic DLL reference (always 1.0, no matter what the "dllinner" version is).

Thus I tried to add the property [assembly: TypeLibVersion(1,0)] to assemblyinfo.cs and doing this "fixes" the reference in the .vbp meaning that the bold value is kept to 1.0 no matter what the AssemblyVersion is. However, the problem is not solved: "dllouter" still fails to load "dllinner".

Is there a way to fix this problem, avoiding to recompile "dllouter" any time "dllinner" version changes?

BillyJoe
  • 878
  • 7
  • 25
  • If you change the public interface of dllinner this is not possible. If you don't then it should be possible. – Ben May 22 '18 at 08:36
  • The public interface is not changed between following versions. – BillyJoe May 22 '18 at 08:37
  • 1
    And have you set the CLSID and interface IDs? – Ben May 22 '18 at 08:37
  • 2
    I.e. every class must have a `ComClassAttribute` which explicitly sets the interface ID (and CLSID if it is to be creatable): https://msdn.microsoft.com/en-us/library/microsoft.visualbasic.comclassattribute(v=vs.110).aspx – Ben May 22 '18 at 08:40
  • 1
    If you don't set them then the compiler generates them automatically, and they will be different every version. To be compatible you need to set them to the same values as the previous version. – Ben May 22 '18 at 08:41
  • The .vbp project will stodgily remember the original *type library* (not .net assembly) until you remove and re-add it in the VB6 ide. The type library is the file with the .tlb extension. – Hans Passant May 22 '18 at 08:51
  • @Ben I added explicit ID: `[Guid("7A0CDBEE-4897-400C-8B5F-5F06C20F98AB")] public interface Interface { ... }` and recompiled everything but problem is still there. @Hans Passant I removed and re-added the .tlb reference, problem is I need to do it every time I change "dllinner" .NET assembly version. – BillyJoe May 22 '18 at 09:37
  • Sure, there is nothing subtle about making a major version change. Neither in .NET nor in COM. You only do that when you have shipped a lesser version. It is a bigger issue in COM because not doing this risk raging DLL Hell. You've already seen what some of that hell looks like :) – Hans Passant May 22 '18 at 09:50
  • 1
    @Ben I missed an explicit ID in front of the class too `[Guid("C0DC113C-3839-4427-8DA5-35D768AFF3D6")] partial class classInterface : Interface {};` in addition to the interface ID. Now it works. I wonder if you want to answer the question or if I should answer it myself. – BillyJoe May 22 '18 at 10:44

1 Answers1

1

If you wish a COM visible assembly to be compatible with a previous release, you must:

1) Make sure that there are no changes visible in the interface. If there are changes to deployed interfaces, then your new object is not compatible, and you will have to generate new CLSIDs, IIDs and a new type library.

2) Make sure that every visible class is annotated with an IID which is the same as the previous version. If you forgot to do this on the previous version, the compiler will generate one for you, so you need to find out what it was so the new version can be the same. You can use TlbView or the registry to find this out.

3) make sure every creatable class is annotated with a CLSID which is the same as the previous version.

It's also a good idea to check the type library is the same. You can decompile the previous version of the type library using TlbView or a similar tool, then do the same with the new version. You should not see any importand differences.

Note: If you want to add additional functions, you can do this but you need to create a new IID for the new interface, which should inherit from the old interface (which should be tagged with the old IID).

Ben
  • 34,935
  • 6
  • 74
  • 113