| « System boots in 2 minutes now | View tool windows with CodeRush and VS .NET 2005 beta 2 » |
InternalsVisibleTo and strong names
10/05/05
InternalsVisibleTo and strong names
I just stumbled upon a funny problem related to the new .NET 2.0 attribute InternalsVisibleTo in conjunction with a strong-named assembly.
The situation is this: I have that assembly that exposes its internals to another assembly (for unit testing) using InternalsVisibleTo. Now I decided to sign that assembly with a strong name. Suddenly, I got the following error message at compile time:
Friend assembly reference 'UnitTests' is invalid. Strong-name signed assemblies must specify a public key token in their InternalsVisibleTo declarations.
So what to do about this? Well, first the UnitTests assembly needs to be signed with a strong name, too. Then the tricky part is using the correct string to pass to the InternalsVisibleTo attribute. Problem is, you won't be able to compile either of the two assemblies before you get that syntax right: the UnitTests assembly doesn't compile because it references the other assembly, which can't be compiled because the attribute is not correctly configured.
The trick is to find out the public key token of the strong name key pair you are using to sign the UnitTests assembly. There are several ways to do that:
- Find any other assembly in the GAC that has been signed with the same strong name and copy the public key token from the properties dialog.
- From the command line, use the sn.exe tool to show the public key token for an assembly that's already signed:
sn.exe -T assembly.dll(note the capital T) - From the command line, use the sn.exe tool to show the token for a public key that you have saved in a file:
sn.exe -t mykey.pubIf you have your key pair together in one file, you'll need to extract the public key into a separate file first:sn.exe -p mykey.snk mykey.pub
There are other ways, especially if you're using a key pair stored in a container. Use the sn.exe command without parameters to get some helpful information on parameter syntax. In either case, you should end up with your public key token, which is a string representing an eight byte hexadecimal number.
The final step to get the assembly combination to compile correctly is to include the public key token in the parameter to the InternalsVisibleTo attribute in the correct syntax (use your own public key token, obviously):
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("UnitTests, PublicKeyToken=123456789abcdef0")]
19 comments
I just double-checked and I find there has to be something funny going on on your end. The thing is this: the compiler checks only for the valid syntax of the string passed to the InternalsVisibleTo attribute. For example, you can even use the string shown above, with the nonsense public key token in it, to make the compiler happy. So if you're still getting errors, there must be another reason... are you sure you're not seeing old messages? VS is sometimes great at this... To be sure, the best way is to switch to the Output tab after a compiler run (not the Error list tab!) and check the output carefully - maybe there's some other problem and VS is showing you the old error.
At first I thought that the documentation included was incorrect and that the final release would only allow you to use the InternalsVisibleTo attribute on strongly signed assemblies. I was in the middle of writing something to MS when I tried to compile their example and was surprised when it worked.
So after much headscractching I discovered that if I removed the following three attributes from my ported AssemblyInfo.cs file:
[assembly: AssemblyDelaySign(false)]
[assembly: AssemblyKeyFile("")]
[assembly: AssemblyKeyName("")]
Then my error went away. It seems that if the compiler detects the presense of these attributes, for the purposes of the InternalsVisibleTo, it seems to think that the assembly is strongly signed.
The InternalsVisibleTo attribute now uses the public key of the assembly, rather than the public key token. This can be extracted by running:
sn -tp mykey.pub
and using PublicKey= rather than PublicKeyToken=
Certainly an interesting hint, but do you find this more useful? I tried it with my own key and while the token has 16 chars (8 bytes), the complete key string is more than 320 chars long. If I have the choice, I'll still go with the token :-)
Try this:
[assembly: InternalsVisibleTo("UnitTests, PublicKeyToken=<123456789abcdef0>")]
However, PublicKey still works A-OK.
Use
sn -Tp assembly.dll
which will return:
PublicKey=00240000048...9
PublicKeyToken=123456789abcdef0
Then in code:
[assembly: InternalsVisibleTo("UnitTests, PublicKey=00240000048...9")]
Specifying [InternalsVisibleTo("MyDll, PublicKey=...")] worked beautifully.
Visual Studio 2008 just simply refused to accepted [InternalsVisibleTo("MyDll, PublicKeyToken=...")]
Thanks again!
Both PublicKey and PublicKeyToken didnt work for me. There seems to be an endless nesting of referenced assemblies that I have to add this attribute to. What is the workaround if any?
new -
The latest version of the Enterprise Library comes strong named. Otherwise, you just have to download the source, go into each project and sign each. Its a PITA, but once you're done, you have a nice set of signed Enterprise Library dlls.
<a href="http://www.microsoft.com/downloads/details.aspx?FamilyId=4C557C63-708F-4280-8F0C-637481C31718&displaylang=en"> latest library</a>
Is this link correct?
Thank you!
I do have one slight 'odity' in VS2008 however. I have a solution with 2 projects, one is the library code the other a unit test project for the library. When I build the library code all works fine but I don't get the internal class appearing in the intellisense when using the unit test code. It all compiles fine however - anybody else experiencing this?
<code>
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("TestTestSuiteLib, PublicKeyToken=<9018f56b77fb9f38>")]
</code>
works for me.
sn.exe -Tp assembly.dll
And copy paste the long public key.
[[assembly: InternalsVisibleTo("TestTestSuiteLib, PublicKey=00240000048000" +
"00940000000602000000240000525341310004000001000100117b0b7131311e" +
"e5a2279939d38d0fa02bddeee660a75088081110f5b65526b38dbea2e04a6ef4b28e6034068103" +
"0a4b0366d30fba3fb2cdb035a38006e11d6c41ce2a8981dc41b712c753a76d315cb64b7ba01b34" +
"edda3b549c40a32bf7cc8b6aa0afa94f7d2e11087a408cce5029cc0d041ad7c253406684ca07ab" +
"5cae8cd8")]


