Tuesday, November 11, 2008

Signed Assemblies and COM References

Something weird happened recently. A co-worker needed to use an assembly I had written, but he was going to be referencing it from a project that would be called from a classic ASP application so the assembly needed to be strongly-signed. "No problem," I thought. I strongly-signed my assembly and recompiled. A few minutes later another application I was working on -- which also used my newly-signed assembly -- started getting the following error:

"The located assembly's manifest definition with name [AssemblyName] does not match the assembly reference"

So what happened? I wouldn't think that just strongly-signing an assembly would cause problems, but apparently it did. After a bit of digging, I discovered the problem: my assembly had references to COM DLLs. When you add a reference to a COM DLL, Visual Studio creates an interop wrapper around the COM DLL... but you already knew that. What you might not have realized (I certainly didn't!) is that in order to use a COM DLL in a signed assembly, the interop file also has to be strongly signed. When I signed my assembly I inadvertently broke it because the interop files were still unsigned.

Once I knew that the solution was pretty simple: remove the references to the COM interops and add them back again after the project is strongly-signed. With the project strongly-signed, Visual Studio creates strongly-signed versions of the Interop files.

Not so fast...
Actually, the solution wasn't quite that easy. In addition to a reference to the strongly-signed assembly, my application also had references to the same COM DLLs used by that assembly. That meant I had a conflict because my application had unsigned interops for those DLLs but the signed assembly expected those interops to be signed.

Since I no longer actually needed those references in my project (I did at one point, but they were no longer necessary), I deleted them. After deleting those references from my application, I was started getting "File Not Found" messages. I didn't need references to the COM DLL in my application, but my referenced signed assembly did. The problem in this case was that I had copied my strongly-signed assembly to a shared folder and referenced it from there. What I didn't do was copy the strongly-signed interop files and my application could no longer find any interop files for those COM DLLs.

Once I copied those interop files to my network share, everything was fine. I also modified my post-build event to make sure those interop files got copied whenever a release build the project was run. My assembly worked, my coworker's project worked, and my application worked. Above all, I learned something interesting about the inner workings of .NET.

The whole experience was both frustrating and facinating. The error was very confusing and there didn't seem to be much useful information online. Apparently using a COM DLL in a .NET assembly that's then used in a COM environment doesn't come up all that often! Once I started getting a handle on the problem it seemed more logical, but then the next error in the stack reared up. Working through the chain of problems to get everything resolved was an exercise in persistance more than ingenuity, but in the end everything came together.

I never paid much attention to strongly-signing assemblies before this because I'd frankly never had a need to do it. The software I write is deployed internally, so I don't need to worry about third parties using my assemblies and I haven't had to write anything in .NET that needed to be used in a COM environment. However, since strongly signing a project takes all of two seconds once you've created a key I'm going to start strongly-signing all of my reusable code libraries and controls. It doesn't make any difference when the assembly is used in a non-signed project, but is essential in order to be used by a signed project so it just makes sense to me to be consistent and do it on every project.