Saturday, November 12, 2005

InternalsVisibleToAttribute

The new InternalsVisibleToAttribute [^] attribute in .NET 2.0 can be used to expose internal members in one assembly to another. This should be used with caution but can be useful in these circumstances:
  1. When refactoring code into multiple assemblies, it might make sense to temporarily shut the compiler up by using this attribute instead of deciding what to do with internal types.
  2. When unit testing, it might be necessary to access internal members of the assembly under test.
Today, I wanted to use this attribute for scenario 2. So I applied it to my assembly under test (names changed to protect the innocent):
  
[assembly: InternalsVisibleTo("Company.Product.UnitTest")]
But this yielded a compiler error:
  
Friend assembly reference 'Company.Product.UnitTest' is invalid.
Strong-name signed assemblies must specify a public key in their InternalsVisibleTo declarations.
Fair enough, since my assembly is strongly-named. So I take a quick squiz at the documentation for the InternalsVisibleToAttribute [^], noting in particular their example code:
  
[assembly:InternalsVisibleToAttribute("AssemblyB, PublicKey=32ab4ba45e0a69a1")]
So I alter my code to specify the public key token:
  
[assembly: InternalsVisibleTo("Company.Product.UnitTest, PublicKey=xxxxxxxxxxxxxxxx")]
But the build still failed:
  
Assembly reference 'Company.Product.UnitTest, PublicKey=xxxxxxxxxxxxxxxx' is invalid and cannot be resolved
At this point I was a little confused. Why are we specifying a public key token but calling it the public key? I googled [^] for information on the attribute and found other people specifying the public key token instead of the public key. I tried changing my code:
  
[assembly: InternalsVisibleTo("Ingenious.Mvc.UnitTest, PublicKeyToken=xxxxxxxxxxxxxxxx")]
But the build still failed:
  
Friend assembly reference 'Company.Product.UnitTest, PublicKeyToken=xxxxxxxxxxxxxxxx' is invalid.
Strong-name signed assemblies must specify a public key in their InternalsVisibleTo declarations.
The only thing left to try was specifying the whole public key, despite not finding anyone with the same problem and not being prompted by the documentation. So I used the secutil tool:
  
C:\> secutil -hex -s Company.Product.UnitTest.dll
Note that the -hex flag must be specified before the -s, otherwise secutil incorrectly treats -hex as the assembly name. Perhaps there is an easier way to extract the public key but it's a rare requirement so I'm not really sure what it is (update: see Jason's comment below for a simpler solution, or this tool). So I copied and pasted the verbose output into my attribute, removing the leading 0x hex indicator:
  
[assembly: InternalsVisibleTo("Company.Product.UnitTest, [assembly: InternalsVisibleTo("Company.Product.UnitTest, PublicKey=AE6A7182001B91...")]

Finally, things built and worked as intended. I'll drop off a note at the MS suggestion box and post a link back here once done.

Update: There is a suggestion here.

Update 2: API documentation has been updated here.

8 comments:

James Canny said...

Hi Kent,

I am trying what you have suggested here. I was trying to get the token using secutil. You stated that you used 'secutil -s -exe AssemblyName.dll'. However there is no such option -exe. I was using 'secutil -hex -s AssemblyName.dll' but this results in the error - [exec] Properties\AssemblyInfo.cs(29,31): error CS1700: Warning as Error: Assembly reference 'Assembly.Tests, PublicKeyToken=xxx' is invalid and cannot be resolved. I'm presming that the token is incorrect but I'm not sure how to get the correct one from secutil. Any help would be appreciated here!

Kent Boogaart said...

Hi James,

Where the hell did I get -exe from? I must have been tired that day. What I meant was:

secutil -hex -s Company.Product.UnitTest.dll

Note the quirk that -hex must be specified before -s otherwise secutil treats -hex as the assembly name.

Also note that you should not include the leading 0x in the InternalsVisibleTo attribute. It should wind up looking something like this (although I've truncated the public key for brevity):

[assembly: InternalsVisibleTo("Company.Product.UnitTest, PublicKey=00240A00F48000...")]

Thanks James - I'll update the post.

Kent

Anonymous said...

http://msdn2.microsoft.com/en-us/library/ms228237.aspx

Jason Poll said...

Thanks for the info, I was having the exact same problem.

It looks like you suggestion has been incorporated into the .NET 3.0 documentation: http://msdn2.microsoft.com/en-us/library/system.runtime.compilerservices.internalsvisibletoattribute.aspx

Also, another way to get the public key (and public key token) from a public key file is to use the Strong Name utility (sn.exe) like so:

sn -tp key.pub

This outputs the public key in hex form (without a leading "0x", as well as the public key token.

Kent Boogaart said...

Thanks Jason - I've updated the post accordingly.

ghkj said...

Three passions,warcraft leveling simple but wow lvl overwhelmingly strong,wow power level have governed wow power level my life: the longing wrath of the lich king power leveling for love, the search for knowledge,World of warcraft Power Leveling and unbearable pity WOTLK Power Leveling for the suffering wlk power leveling of mankind. These passions,wlk power leveling like great winds,age of conan gold have blown me hither and thither,cheap aoc gold in a wayward course,aoc power leveling over a great ocean ffxi gil of anguish, reaching final fantasy xi gil to the very verge of despair. I have sought love, first, because it brings ecstasy - ecstasy so great that I would often have sacrificed FFXI Gil all the rest of life for final fantasy gil a few hours of this joy. I have sought it, wow gold because it relieves loneliness--that terrible loneliness in which one shivering consciousness dog clothes looks over the rim of the world into the cold unfathomable lifeless abyss.

Anonymous said...

Before reading this post, I did the following in order to extract the public key from the dll which didn't work,

sn.exe -e "dllpath" "outputfile"

Using a hex editor, I opened "outputfile" and copy-pasted the hex code

This resulted in the 'invalid and cannot be resolved' error

I am surprised this didn't work. So for any one that's struggling doing the same thing, do switch to using the secutil as described.

-Vandana

dreaz said...

I am grateful to you for this great content.aöf thanks radyo dinle cool hikaye very nice ssk only cinsellik very nice ehliyet only home free kadın last go korku jomax med olsaoy hikaye lesto go müzik dinle free only film izle love aşk only mp3 indir only radyolar lest go açıköğretim free net only filmler