While working on one of our projects at Monfort, we encountered a very
strange problem. The project was dependant on several COM objects, one of which
queries a database according to a predefined interface and returns the results
in an ADO Recordset. Our project is key-signed, so
we had to import the COM objects using the .NET SDK tlbimp utility; everything worked perfectly until we added
the querying logic that receives the Recordset
result object. The class loader would throw an exception on calling the relevant
method (it was invoked dynamically) claiming that the "referenced assembly adodb.dll could not be found." To quote Monty Python: this is, of course, pure
bullshit. The imported ADODB wrapper was right there in the directory. I
couldn't figure out the problem in a reasonably timespan, and finally decided to
ignore it altogether by using the ADODB primary interop assembly distributed
with .NET 1.1. I added a reference to the ADODB PIA and everything compiled and
seem to run fine.
This turns out to have been a mistake. I was absent from work for a few days
getting ready for a test, and came back to find that a colleague has encountered
the same problem but elsewhere; examination of the compiled executable's
references (via ILDASM) revealed that it was in fact
generated with two separate references to ADODB with two different public
key tokens. One of the references was the PIA, which was just peachy, but
the other one (in the hidden namespace ADODB_19 of
all things) referenced a non-existing assembly with the same public key
token we sign our other assemblies with. I concluded that there must be
some sort of problem with the build process - left over dependencies that
weren't compiled properly or something of the sort. Cleaning up the build
directory and rebuilding did not alleviate the issue. In desperation I tried to
manually edit the project files but couldn't find anything.
I then decided that one of the COM object wrappers we created using tlbimp was, for some reason, referencing a non-existing
version of the ADODB wrapper; I went through the wrapper assemblies one by one
and managed to find the offending library. Turns out that tlbimp does not, by default, look for primary interop
assemblies and opts to import ADODB every single time (singing the new wrapper
assembly accordingly). When I deleted what I thought was a redundant ADODB
wrapper assembly, I in fact deleted a version of the wrapper that was referenced
by one the imported COM wrappers. So now all I had to do was add a /reference: directive to the tlbimp call in our build script and voilla - everything
works!
Only it doesn't. Another COM object which references ADODB was still
generated with the mangled assembly reference (i.e. it still referenced the
wrapper with our own public key). I retried all sorts of tricks and couldn't
figure out why it was generated that way; I eventually tried to re-import it
manually with a /strictref directive, which resulted
in the following error message:
C:\Temp>tlbimp SomeComServer.exe
/keyfile:PublicKey.snk /reference:"%PROGRAMFILES%\Microsoft.NET\Primary Interop
Assemblies\ADODB.dll" /strictref
Microsoft (R) .NET Framework Type Library to
Assembly Converter 1.1.4322.573
Copyright (C) Microsoft Corporation
1998-2002. All rights reserved.
TlbImp error: System.ApplicationException
- Type library 'ADODB' is not in the list of references
A fit of confused rage and an hour later I sat at the computer again to try
to figure things out; finally (as usual) it was just a matter of asking Google
the right
question:
The problem was that MyCOMLib.dll was compiled against ADO 2.5 and
the PIA released by MS is registered only for ADO 2.7. I don't have access to
the COM component to recompile it for ADO 2.7, so I had to get a PIA for ADO
2.5.
Further research into this turned out a little
detail most .NET developers are probably unaware of:
Issue 7: You experience problems working with components that
expect ADO 2.8 interfaces
The ADODB PIA that is included with Visual Studio 2005 is the same
component that was included with Visual Studio .NET 2003 and was built by
using the Microsoft .NET Framework 1.1. The ADODB PIA was built to interact
with ADO 2.7 interfaces and has not been updated to work with ADO 2.8
interfaces.
Therefore, attempts to use the ADODB PIA together with components that
expose ADO 2.8 interfaces will fail. This
scenario is not supported with the ADODB PIA.
Oh, great job, Microsoft! You spent so much time on the interop issues with
.NET (of which, by the way, there is an impressively small number), came up with
the concepts for Primary Interop Assemblies, Publisher Policy Files and
everything else that went into .NET in order to prevent DLL hell, you tout
Primary Interop Assemblies as a way for component publishers to guarantee that
their components are usable under .NET, and finally you go on to release a PIA
for one of the most widely used COM objects in Windows which only
supports one version!
So there you are, I hope this might save someone the frustration of having to
figure all of this out on their own.
As a footnote, ILDASM is one of the worst tools
I've ever used. I can't believe how Microsoft managed to wrap a useful debugging
tool with one of the most frustrating and least usable UIs I've ever
encountered. Don't know what I'm talking about? Try double-clicking on a node
(method, manifest, whatever). You can't run a search through the text; worse
still, you can't even Ctrl+A, Ctrl+C then paste into notepad! Only now, days
after I needed it, did it occur to me to rightclick and "select all". I suppose
it's OK if you don't follow the classic usability patterns and guidelines in a
small and esoteric development tool, but please try and exercise a bit of
common sense...