File version vs. assembly version
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 th开发者_如何学编程e 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.
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.
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.
精彩评论