Know your enemies with Friendly Assemblies

Recently we were asked to make certain members of an assembly accessible to a defined set of friendly assemblies. After a short recherche it was obvious that we need to use InternalsVisibleTo. Since there was no good guide on how to use that mighty attribute, we decided to make a small guide for our readers.

The solution and its friendly assemblies

We start with a Blank Solution and describe everything as detailed as possible.

To create a Blank Solution start your Visual Studio 2012 and wait until it is loaded. When Visual Studio is ready press [CTRL] + [SHIFT] + [n]. The New Project Dialog will open. Adjust your selection according to the next screenshot and give your Solution a name.

How to create a Blank Solution
Create a Blank Solution

Click the OK button to create your Blank Solution. Now we need some Projects in our Solution to demonstrate the behavior we’ll need at least 3 Solutions. One Console Application and two ClassLibraries. We select File – Add – New Project in Visual Studio and create Projects with the following names. Just make your selection according to the following screenshots.

  • at.automism.FriendlyAssemblies.Consumer
  • at.automism.FriendlyAssemblies.Secret
  • at.automism.FriendlyAssemblies.Public
How to create a Console Application
Create a Console Application
How to create a Class Library
Create a Class Library

Now we have the project structure for our little demo application.

Setup the Solution to use Strong-Name Signing

Its time to sign our assemblies to be able to identify the friendly assemblies.
Start the “Developer Command Prompt for VS2012” enter your Solution directory and execute the following steps.

In our example we will generate only one KeyPair that will be used to sign all Assemblies.

sn -k at.automism.FriendlyAssemblies.snk
sn -p at.automism.FriendlyAssemblies.snk at.automism.FriendlyAssemblies.pk
sn -tp at.automism.FriendlyAssemblies.pk > at.automism.FriendlyAssemblies.txt

When everything worked out you will see something similar to the following screenshot.

How to create a KeyPair for Strong-Name Signing
How to create a KeyPair for Strong-Name Signing

Now we configure Signing for our Projects, repeat this steps for all Projects in the Solution.

  1. Click the Project in the Solution Explorer.
  2. Press [ALT] + [Enter] to open the Properties Window.
  3. Click on Signing on the left side.
  4. Check Sign the assembly.
  5. Select “<Browse…>” in the now enabled Drop Down box.
  6. Select the generated .snk file in the opened File Open Dialog box.
  7. Click Open.
  8. Press [CTRL] + [s] to save the changes.

Press [CTRL] + [b] to start a build and make sure everything is still working.

Put some butter on the bread

Now that the Solution is set up, we add some Code to our projects.

Start with the “at.automism.FriendlyAssemblies.Secret” Project. Visual Studio already generated a class for us, we’re going to change it to fit our needs.

namespace at.automism.FriendlyAssemblies.Secret
{
    using System;
    using System.Collections.Generic;
    using System.Linq;

    public static class Greeter
    {
        public static void SayHello()
        {
            Console.WriteLine("The Greeter says: "Hello, World!"");
        }

        internal static void WhisperHello()
        {
            Console.WriteLine("The Greeter whispers: "Hello, Mr. Bond!"");
        }
    }
}

There is nothing spectacular about our Greeter Class. It just defines two Methods and sets the access modifier of one of the methods to internal.

Now we add the “at.automism.FriendlyAssemblies.Consumer” as friendly assembly. Open the AssemblyInfo.cs file located in the Properties folder of the Project “at.automism.FriendlyAssemblies.Secret”.

At the end of the file add the InternalsVisibleToAttribute like in the following example.

[assembly: InternalsVisibleTo("at.automism.FriendlyAssemblies.Consumer, PublicKey=002400000480000094000000060200000024000052534131000400000100010057950ce02d1832b52cb36a7eeee66218d2611176a0e430082db23d4aaa50ed3028d73be065c3f2877072ffa38737badc988c9192316da6197062c8b4908d441a693454493f260c86c911ecf0189934093d0823a8f4180053afa6e57df10b3ddca27c6237bb1afa59db5e9578d607378e512557f4fb83a8faa60f497aee4801d4")]

The Parameter for the Attribute is defined as “<name of the friendly assembly>, PublicKey=<key found in the at.automism.FriendlyAssemblies.txt file>”. Make sure to remove all line breaks from the public key.

Now we add a reference to our “at.automism.FriendlyAssemblies.Secret” in the other two Projects.

  1. Right click the References Folder of the Project.
  2. Select “Add Reference…”.
  3. On the left side of the opened window select Solution – Projects.
  4. Check the box right in front of the desired Project.
  5. Click OK when finished.

When you have added the references hit [CTRL] + [b] and make sure the build works as expected.

Now we modify the code in the “at.automism.FriendlyAssemblies.Public” Project. Just modify the already existing class to look like the snippet below.

namespace at.automism.FriendlyAssemblies.Public
{
    using at.automism.FriendlyAssemblies.Secret;
    using System;
    using System.Collections.Generic;
    using System.Linq;

    public static class Anouncer
    {
        public static void Anounce()
        {
            Greeter.SayHello();
        }
    }
}

You probably have noticed that you can’t access the WhisperHello Method from that file. That happens because the the method is only visible to friendly assemblies.

We add a reference to “at.automism.FriendlyAssemblies.Public” to the “at.automism.FriendlyAssemblies.Consumer” Project. Just follow the steps done earlier.

Adjust the Program.cs file to look like the following snippet.

namespace at.automism.FriendlyAssemblies.Consumer
{
    using at.automism.FriendlyAssemblies.Public;
    using at.automism.FriendlyAssemblies.Secret;
    using System;
    using System.Collections.Generic;
    using System.Linq;

    public class Program
    {
        public static void Main(string[] args)
        {
            Anouncer.Anounce();
            Greeter.WhisperHello();
            Console.ReadLine();
        }
    }
}

Have you noticed how WisperHello was available in Intellisense when making the adjustments?

When everything went OK you can hit F5 and will be presented with something similar to the screenshot below.

The result of the hard work
The result of the hard work

Reference

InternalsVisibleTo