views:

172

answers:

2

I have a .NET (FW 2.0) library which is used by a COM (vb6) application and also by a .NET application.

The TLB generated for COM is registered which a version consisting of the first two digits of the "assembly version". For example, 1.2.5.7 assembly version becomes version 1.2 of the TLB. This is very inconvenient, because sometimes I would like to change the second digit without needing to recompile the vb6 application, but it seems that I need to recompile when the TLB version changes.

So, I began researching about the file/assembly thing, and tried to use the file version to identify my changes, maintaining the assembly version constant.

This works as expected (i.e. it just works) with the .NET app, but not so with the vb6 one. If any of the third digits is different, (e.g. assembly version 1.0.0.0 / file version 1.0.2.0), each time I try to create a new object I get an "Automation error -2147024894 (0x80070002)".

Can anyone tell me why this happens, and if there's any workaround?

Edit: Sorry, the error code was wrong. I don't know where that 438 came from, but I'd swear I saw it around...

Edit (2010-09-14): Strangely, if I compile with assembly version 1.0.0.0, then I got the "Automation error" even if I recompile the vb6 project.

Edit (2010-09-14, again): After checking with ProcMon and FileMon, there doesn't seem to be any file access in the offending code, only registry queries, most referring to other versions of the DLL. It seem to try all previous versions from 1.0.0.0 (which is the one with the newest code) to 1.0.1.3, but not the next (1.0.2.0). The file version is, right now, 1.0.2.1, and it works if I set the assembly version to 1.0.2.1, recompile and register it.

Edit (2010-09-15): There are three PATH_NOT_FOUND errors when it searches for version 1.0.1.3 of my .Net DLL in the GAC (in c:\windows\assembly\GAC_32, c:\windows\assembly\GAC_MSIL and c:\windows\assembly\GAC) There are some other errors (NAME_NOT_FOUND, mostly) but it seems to find all of them after trying several variations. The problem seems to be that it's searching for the wrong version of the DLL.

Another edit (2010-09-15, still): Following Hans Passant advice, I have ran the offending code with fuslogvw executing in background, and these are the important lines it throws:

LOG: DisplayName = E_DataIndex, Version=1.0.1.3, Culture=neutral, PublicKeyToken=c322af271028978e (Fully-specified)

LOG: Appbase = file:///C:/Archivos de programa/Microsoft Visual Studio/VB98/

LOG: AppName = VB6.EXE

LOG: Unsuccessful search in GAC.

Edit: According to Hans' answer, I am early-binding and have changed every Guid of the interface. This seems to work (yeas, I know, I should've tried this before)...

Edit (2010-09-17): I have found the cause of the original problem: I was not unregistering the TLB before registering the new one and, as I wasn't changing the UUID's, it keep searching for the latest version installed. Sure, my versioning system is not the best in the world, but knowing these pitfalls I'll keep using it for now (I'm quite new to COM and .NET interop). As the "Types don't match" seem to be another, unrelated problem, I'm deleting the info and, if I don't manage to solve it myself, I'll open a new question.

+4  A: 

Versioning in COM is a serious matter. The dreaded DLL Hell is always around the corner. One of the stone cold hard rules is that if you change a public interface of your COM component then you have to give the interface a new GUID. This ensures that an client that was compiled with the previous version of the type library cannot accidentally use the new interface.

Not following that rule produces very difficult to diagnose problems. The client of the server will call the wrong method. Or the right method but with the wrong arguments. The outcome is never pretty, anything can happen. If you are lucky then the program crashes with an AccessViolation. That kind of luck is hard to come by though, it usually just malfunctions in very inscrutable ways.

Judging from your question, you don't use the [Guid] attribute on your interfaces but leave it up to the compiler to automatically generate one. It does so by considering, for one, the [AssemblyVersion] attribute. A different version automatically generates a different GUID. What's next is, indeed, the DLL Hell avoidance at work, your VB6 program can no longer find the interface. You'll get a predictable error message instead of a random call going into the weeds. The VB6 program has to be recompiled to use the new type library.

Do note that this is very much a feature, not a bug. You can take control over it yourself by giving the interfaces a [Guid] attribute so they no longer depend on the assembly name or version. You now have to manually change it when you change the interface. And will pay the price sooner or later when you forget to do so.

The type library version number is similarly important. Changing the public interface is not a simple build or revision change, it is a sweeping change. Clients of your COM server have to be recompiled. Express this in your version number, increment either the minor or major version number.

Hans Passant
I **AM** using the [Guid] attribute. So that's not the problem.
Jaime Pardos
Sorry, I hit enter too soon... Thank you very much for your detailes answer. I **AM** using the [Guid] attribute, so that's not the problem. I thought that using fixed DispID's would avoid the need to recompile, so if I have an interface with method ids 1 through 18, and then add a new one with DispId 19, I wouldn't have to recompile (late binding). So if I add a new method without changing existing ones, everything should keep working. Indeed, if I do so without changing version numbers, then everything seems to work correctly, thus my question. So, any other idea about what's wrong here?
Jaime Pardos
Well, my answer goes in the toilet with that approach. Are you sure the VB6 code actually uses late binding? It really likes early. The error you are getting is pretty unusual, it is a Windows error, "File not found". You can use SysInternals' ProcMon utility to find out what that file might be.
Hans Passant
No, I'm not sure. How can I be **absolutely** sure that it's using late binding?
Jaime Pardos
Anyway, this is not the problem. It throws that error even if I compile against the new dll.
Jaime Pardos
So, what file could not be found? ProcMon told you.
Hans Passant
Sorry, I forgot that part. If I put a breakpoint in VB6 in the line which tries to instantiate the object, then clear the ProcMon log, then step over said line (to the error handling code), there's no entry (successful or otherwise) about file accessing, only registry queries, most related to the GUID of the class I'm trying to instantiate, and a lot with the result of "NAME_NOT_FOUND". I have tried with filemon (just in case), but no luck, either... There seems to be queries for versions 1.0.0.0 thru 1.0.1.3, while I have registered 1.0.0.0 and 1.0.2.1 right now. It works with only 1.0.2.1.
Jaime Pardos
An error like that cannot hide from ProcMon. Be sure that you have file tracing turned on as well. It is a button on the toolbar, the tooltip says "Show File System Activity". Make sure the button is pressed.
Hans Passant
Thank you for your patience, Hans. It seems that VB6 only looks for the files once, and the first time I did it yesterday I had no filters and there was too much information. I'm updating the question with the results.
Jaime Pardos
Why does the [AssemblyVersion] keep changing? You do have to re-register the assembly after it changed or the CLR will indeed refuse to load it. Fuslogvw.exe is the tool to diagnose this.
Hans Passant
I have checked that there was no other version of the DLL in the GAC, re-registered the assembly and then created a new .tlb from the 1.0.0.0 version, but it still doesn't work. I have ran the offending code with fuslogvw executing, and it only says that it can't find the 1.0.1.3 version... Any other idea? :-(
Jaime Pardos
Creating a .tlb doesn't make sense, you said you were using late binding. Sure sounds like you are not, which makes my answer again very relevant. The different [AssemblyVersion] values is why you get the error.
Hans Passant
Oh, and GAC is another angle, you'd commonly register the DLL with the /codebase option. If you don't then you really *do* have to put the assembly in the GAC with gacutil.exe
Hans Passant
I was not really using late binding (I thought I was) but after researching a bit, I tried it and keep having the same problem. Now I have removed the reference to the TLB in my project just to be sure, and I'm still getting the same error. And I _have_ the assembly in the GAC, that's what I mean with "registering it". I will do some tests and update again. But it seems as if the GAC had incorrect information about the installed version, or something.
Jaime Pardos
Thank you very much for your efforts, Hans. I finally managed to solve the issue, thanks to your help.
Jaime Pardos
So, what was the real problem?
Hans Passant
The real problem was that I had the 1.0.1.3 tlb version in the windows registry, pointing to the same file as the new 1.0.0.0 I was creating. I thought that when I register a tlb, the old versions unregistered automatically, but apparently it isn't so. It seems that for some reason vb was searching for the latest version in the registry, and then trying to find the .Net DLL corresponding to that version number, which, of course, didn't exist, thus the "File not found problem".
Jaime Pardos
+1  A: 

Is this being compiled/run on a Windows 7 64-bit machine?

Go to the project properties window and try switching the "Platform Target" from "Any CPU" to "x86"


Try running reg cleaning tool like CCleaner and remove any registry entries pointing at the dll. I've had a rare case where two keys with matching sigs got in the registry and manually unregistering/registering the right components never mattered until the bad keys were cleaned up. The references in the VS project looked correct (you could browse to the right files and add them without error) but were in actuality pointing to "ghost files" from the bad registry key and would die every time the project was run/compiled.

jasonk
It's a 32-bit Windows XP. Will try that CCleaner thing, thank you very much.
Jaime Pardos
CCleaner couldn't find anything related to that DLL or its associated TypeLib.
Jaime Pardos