views:

339

answers:

3

I have a .NET dll which has some interfaces\classes which are exposed to com. during the build procedure a .tlb file is generated and this tlb is referenced by some c++ code. As a result the compiler generates a .tlh file for the tlb.

When I run the build locally one of the properties in one of the interfaces ends up with a corresponding method in the tlh which does not have the same name. The property in the .net code is called PropertyA end up being called get_propertyA, while PropertyB ends up called get_PropertyB. I didn't bat an eyelid when this happened, just used the method as defined in the tlh and assumed everything was hunky dory, however when I comitted these changes the build did not work for anyone else, as the compiler generated properties called get_PropertyA and get_PropertyB (notice case mismatch in propertyA).

The tlb files generated on both machines are identical (according to a hex comparer) and the tlh files are both generated by the same compiler version.

The build procedure creates the tlb by doing: regasm path\to\dll\Mydll.dll -tlb:path\to\output\mydll.tlb

Any ideas why my local version ends up with a property with the incorrect name? Or what I can do to fix it?

UPDATE: I read that tlbexp will use the first version of the string that it finds and that can change with a recompile. Although I'm not using tlbexp, I wondered if that was the problem. I found parameters with the same name as my method (in other methods) but with a lower case letter at the start. So I replaced all of those. Re-built, no change. SO I then renamed my COM method. Re-Built and got the expected missing method errors. Renamed the method back to the original name, and hey presto it seemed fixed. As it now seems to work and I can't get it to fail again I can't try out the suggested solutions, but I like the rename idea in case this happens in the future.

A: 

Sanity check: are you absolutely sure the same #import directive is used on both machines? i.e. the same exactly source files being compiled?

Try creating a network share over the directory containing the project and open it on the other machine in order to be 1000% sure these are the same source files being compiled.

Sorry I don't have any more specific suggestions.

Assaf Lavie
yes, we have been through the sanity check. The same files are being compiled, we have checked the cvs version numbers. and we have checked that the intermediate steps produce the correct files (the tlb files are identical) and the properties were newly introduced (renamed) in this version, so the fact that they are appearing at all indicates that it is using the right source...
Sam Holder
Can you post the #import line you're using?
Assaf Lavie
#import <MyDll.tlb> raw_interfaces_only,raw_native_types,no_namespace,named_guids
Sam Holder
+1  A: 

You can use the rename attribute for the import to explicitly rename the properties. Say you have propA that sometimes becomes PropA and propB that sometimes becomes PropB. To always have PropA and PropB use rename as follows:

#import <library> rename( "propA", "PropA" ) rename( "propB", "PropB" )

Use this with care - it causes a simple text substitution that works for any identifiers it encounters in the type library. In some cases it can cause hard to debug undesired side effects.

sharptooth
Can't test to see if this fixes the problem (see update in question above) but seems like it probably would so can't accept as the answer but I have upvoted it. If the problem returns I'll try this and come back. Thanks for the suggestion
Sam Holder
Ok so the problem came back. I tested this and it seems to work well. Thanks!
Sam Holder
I didn't downvote as this does work, but this forces you to do a rename every time you import the library. Wouldn't it be better to force the correct creation of the tlb in the first place (see /names on tlbexp)
Ed Sykes
+1  A: 

I'm having the same problem.

Via another SO question ( http://stackoverflow.com/questions/708721/compare-type-libraries-generated-by-tlbexp ) I found this piece of community content:

http://social.msdn.microsoft.com/Forums/en-US/clr/thread/5003c486-ed3f-4ec8-8398-a1251b0f9e74

Quoting from that content:

In the documentation of tlbexp, there is one useful community content:

http://msdn2.microsoft.com/en-gb/library/hfzzah2c(VS.80).aspx

Quote:

"The reason for the /names option is that type libraries store each identifier in a case-insensitive table. The first case encountered wins. So a class called Monitor might end up being exposed as "monitor" if there's a parameter with such a name encountered first. (And the order in which identifiers are encountered can vary simply by recompiling your assembly!) /names can guarantee stable casing."

The root cause seems to be a bug in the midl, described here:

http://support.microsoft.com/default.aspx?scid=kb;en-us;220137

Quote:

"When there are two identifiers that differ only by case, the case of the second identifier is changed to reflect the case of the first."

So as a solution, I unchecked the option "register for COM interop" in the project settings and added the post-build-steps

"$(DevEnvDir)....\SDK\v2.0\Bin\tlbexp" $(TargetFileName) /names:"$(ProjectDir)Names.txt" %windir%\Microsoft.NET\Framework\v2.0.50727\regasm $(TargetFileName)

The names file contains the entriesthat define how capizalization should be done. In my case it contains only one line:

ID

Best Regards

Bernd Ritter

Using the /names has solved this problem for me.

Ed Sykes
thanks for the update, despite the old question. +1 for an alternative approach.
Sam Holder