Sunday, March 04, 2007

COM Interop Problem with Type Library CoClass Names Changing

Message

This was a real pain, and google was no help, so here's a post that hopefully helps someone else. 

(By the way, if you want to have some fun with VB developers who are calling your .Net Assemblies, this is hours of good, wholesome, plausibly deniable bug hunting.)   

Background:  A Visual Basic 6 project has been happily consuming a set of C# Serviced Components for some centuries.  One day, some functionality was added to the C# solution.   Suddenly the VB projects wouldn't compile.  

Basic investigation on the VB side revealed that the names of the components had changed from <BusinessObject> to <CompanyAccronym>_<FunctionalArea>_<BusinessObject>.  Basically, the new name of the object had become the namespace with dots replaced by underscores.  

We had enough experts around that we quickly narrowed it down to a naming conflict.  Ripping all the new functionality out of the solution fixed the problem, so it was obviously a conflict with a newly added object.  Renaming anything that might conflict would be the solution.  

The lingering question was exactly what circumstances cause the problem?   

To that end I've created a very simple repro in VS. 

The bottom line is that you can't have two classes with the same name in the same solution, no matter what you do with the namespaces.  As soon as you add the second (conflicting) class, you hose all of your COM clients who call your first class.  


Here's the Repro:

Create a solution. 
Add a class file:

namespace RobTest.TypeLib
{
 public class conflict
 {
  public conflict()
  {
  }
 }
}

Generate the type lib with Tlbexp:
[
  uuid(44EB15C1-7CE0-39D7-9ADB-B0086CE7F6EB),
  version(1.0),
    custom({0F21F359-AB84-41E8-9A78-36D110E6D2F9}, "RobTest.TypeLib.conflict")
]
coclass conflict {
    [default] interface _conflict;
    interface _Object;
};

Looks OK.

Now add another class in a totally different Namespace

namespace RobTest2.TypeLib2
{
 public class conflict
 {
  public conflict()
  {
  }
 }
}

Regenerate the type lib:
[
  uuid(90EDBC47-D360-3545-9786-15FF36E3AF66),
  version(1.0),
    custom({0F21F359-AB84-41E8-9A78-36D110E6D2F9}, "RobTest.TypeLib.conflict")
]
coclass RobTest_TypeLib_conflict {
    [default] interface _RobTest_TypeLib_conflict;
    interface _Object;
};
[
  uuid(A45FCAAA-AC72-31DA-A6FC-DEC2EDE2489F),
  version(1.0),
    custom({0F21F359-AB84-41E8-9A78-36D110E6D2F9}, "RobTest2.TypeLib2.TypeLibProblem2.conflict")
]
coclass RobTest2_TypeLib2_conflict {
    [default] interface _RobTest2_TypeLib2_conflict;
    interface _Object;
};

Not Good! 

The only solution is to rename the conflicting class:

namespace RobTest2.TypeLib2
{
 public class NoConflict
 {
  public NoConflict()
  {
  }
 }
}

Then everything goes happily back to the way it was.  
[
  uuid(D26D7937-EA9B-30B6-BA98-A36614BF7CCE),
  version(1.0),
    custom({0F21F359-AB84-41E8-9A78-36D110E6D2F9}, "RobTest.TypeLib.conflict")
]
coclass conflict {
    [default] interface _conflict;
    interface _Object;
};
[
  uuid(3E9830B2-B787-3FBF-A493-370273AB224F),
  version(1.0),
    custom({0F21F359-AB84-41E8-9A78-36D110E6D2F9}, "RobTest2.TypeLib2.NoConflict")
]
coclass NoConflict {
    [default] interface _NoConflict;
    interface _Object;
};

No comments:

Post a Comment