82

I am trying to use the InternalsVisibleTo assembly attribute to make my internal classes in a .NET class library visible to my unit test project. For some reason, I keep getting an error message that says:

'MyClassName' is inaccessible due to its protection level

Both assemblies are signed and I have the correct key listed in the attribute declaration. Any ideas?

abatishchev
  • 98,240
  • 88
  • 296
  • 433
skb
  • 30,624
  • 33
  • 94
  • 146
  • 1
    Can you post what you have for the InternalsVisibleTo attribute on the class you are trying to expose? Hard to say what is wrong without seeing what you are looking at. – Eric Schoonover Sep 20 '08 at 03:04

24 Answers24

107

Are you absolutely sure you have the correct public key specified in the attribute? Note that you need to specify the full public key, not just the public key token. It looks something like:

[assembly: InternalsVisibleTo("MyFriendAssembly,
PublicKey=0024000004800000940000000602000000240000525341310004000001000100F73
F4DDC11F0CA6209BC63EFCBBAC3DACB04B612E04FA07F01D919FB5A1579D20283DC12901C8B66
A08FB8A9CB6A5E81989007B3AA43CD7442BED6D21F4D33FB590A46420FB75265C889D536A9519
674440C3C2FB06C5924360243CACD4B641BE574C31A434CE845323395842FAAF106B234C2C140
6E2F553073FF557D2DB6C5")]

It's 320 or so hex digits. Not sure why you need to specify the full public key - possibly with just the public key token that is used in other assembly references it would be easier for someone to spoof the friend assembly's identity.

Daniel A.A. Pelsmaeker
  • 47,471
  • 20
  • 111
  • 157
Joe
  • 122,218
  • 32
  • 205
  • 338
  • 53
    To get the public key of the friend assembly "sn -Tp MyFriendAssembly" – Ian G Jul 06 '09 at 09:54
  • @Tyler: This is why you should @ people. I'm adding an answer below that contains a VS macro that can be used to generate the IVT for a project in your solution. –  Mar 22 '12 at 17:33
  • 12
    To clarify @Ian G's comment, open Visual Studio Command Prompt, change directory (CD) to the relevant directory containing the 'friend' assembly dll file. In the command prompt, type `sn.exe -Tp NameOfAssembly`, or just `sn - Tp NameOfAssembly` as Ian said. This uses Microsoft's Strong Name Tool to find and display the full public key from the assembly. If you only have a .pub file containing your key, you can extract it using `sn.exe -p NameOfKeyFile.snk NameOfKeyPairFile.pub`. This will create the NameOfKeyFile.snk key file which will then enable you to follow the earlier instructions. – Sheridan Feb 08 '13 at 17:10
  • 4
    Really important, at least with VS2012, is that the `[assembly:InternalsVisibleTo("...")]` attribute **must be on a single line** despite the apparent formatting of the example here and in the MSDN documentation. You cannot just copy-paste the Public Key output from `sn.exe -Tp MyFriendAssembly` and then use a `@"multi line string"` (note the @-character) or the compiler will complain with `...does not contain a definition for 'X' and no extension method 'X' ... could be found` when trying to access your internals from MyFriendAssembly. – AlwaysLearning May 21 '15 at 05:44
  • Also, make sure you do not create a new key file in the Unit Test project. You must load the key file from the main project. I was a little slow and did that(first time) and took a little bit of cussing trying to figure out why it wasn't working still. Also, a very easy way to get the public key can be found at [MSDN BLOG](http://blogs.msdn.com/b/miah/archive/2008/02/19/visual-studio-tip-get-public-key-token-for-a-stong-named-assembly.aspx). Then replace the argument `-T $(TargetPath)` with `-Tp $(TargetPath)` – famousKaneis Jul 09 '15 at 19:13
  • I'm working with a large legacy codebase upgrading it... and getting Sgen problems..I can't build my friend assembly until I've build my main assembly, so how is it possible to get the token from my friend.dll when it can't yet build it? – GilesDMiddleton Jul 27 '15 at 14:58
  • @Gilesey, you use the `sn` tool mentioned, you can target the snk or pfx file that you use in visual studio to do extract the full public key, then use that in the attribute on the assembly. Just make sure you target the correct key file for the assembly you're referring to. If visual studio complains about the identity of the assembly the internals are visible to not being a name it can resolve, use a stand-in assembly (blank assembly that's signed) until you get to that point in development where both compile. – Allen Clark Copeland Jr Dec 22 '15 at 14:31
44

Another possible "gotcha": The name of the friend assembly that you specify in the InternalsVisibleToAttribute must exactly match the name of your friend assembly as shown in the friend's project properties (in the Application tab).

In my case, I had a project Thingamajig and a companion project ThingamajigAutoTests (names changed to protect the guilty) that both produced unsigned assemblies. I duly added the attribute [assembly: InternalsVisibleTo( "ThingamajigAutoTests" )] to the Thingamajig\AssemblyInfo.cs file, and commented out the AssemblyKeyFile and AssemblyKeyName attributes as noted above. The Thingamajig project built just fine, but its internal members stubbornly refused to show up in the autotest project.

After much head scratching, I rechecked the ThingamajigAutoTests project properties, and discovered that the assembly name was specified as "ThingamajigAutoTests.dll". Bingo - I added the ".dll" extension to the assembly name in the InternalsVisibleTo attribute, and the pieces fell into place.

Sometimes it's the littlest things...

John Beyer
  • 673
  • 7
  • 8
  • 2
    +1 as your comment helped me: in my case, I had renamed the assembly at some point, but VS had left the Assembly Name and default namespace in the Project Properties as the old values – Nij Sep 12 '13 at 15:40
  • 3
    Hmm. Just the opposite for me. I had to _remove_ the ".dll" for the solution to build. – kmote Jun 17 '14 at 21:04
  • I can confirm @kmote is right: I had to _remove_ the ".dll" for the solution to build (and Intellisense to work). (.NET 4.5, VS2013, unsigned assemblies) Very strange behavior. This question and _all_ its answers are terrific to resolve problems with this apparently problematic attribute. – davidbak Apr 30 '15 at 03:44
  • With my typing accuracy, I'm pretty much doomed when it comes to getting this attribute right. Helpful tip! – timmyl Jan 10 '16 at 20:08
  • +1 this will is also the case with spaces. Why my assembly name has a space in it i don't know but this fixed it. :D – user3797758 May 21 '17 at 12:46
37

If your assemblies aren't signed, but you are still getting the same error, check your AssemblyInfo.cs file for either of the following lines:

[assembly: AssemblyKeyFile("")]
[assembly: AssemblyKeyName("")]

The properties tab will still show your assembly as unsigned if either (or both) of these lines are present, but the InternalsVisibleTo attribute treats an assembly with these lines as strongly signed. Simply delete (or comment out) these lines, and it should work fine for you.

sth
  • 222,467
  • 53
  • 283
  • 367
Skimedic
  • 778
  • 7
  • 9
  • 1
    Thanks, this was my problem, and no where else could I find this answer. – Andre Sep 26 '11 at 17:02
  • 2
    This saved me after getting "MyAssembly.dll does not represent a strongly named assembly" when calling sn.exe -Tp MyAssembly.dll – Dunc May 09 '13 at 10:18
12

It is worth noting that if the "friend" (tests) assembly is written in C++/CLI rather than C#/VB.NET, you need to use the following:

#using "AssemblyUnderTest.dll" as_friend

instead of a project reference or the usual #using statement. For some reason, there is no way to do this in the project reference UI.

Pang
  • 9,564
  • 146
  • 81
  • 122
Colin Desmond
  • 4,824
  • 4
  • 46
  • 67
9

You can use AssemblyHelper tool that will generate InternalsVisibleTo syntax for you. Here's the link to the latest version. Just note that it only works for strongly-named assemblies.

ErikE
  • 48,881
  • 23
  • 151
  • 196
Vadim
  • 21,044
  • 18
  • 65
  • 101
7

Although using an AssemblyInfo file and adding InternalsVisibleTo still works, the preferred approach now (as far as I can tell) is to use an ItemGroup in your Project's csproj file, like so:

    <ItemGroup>
        <InternalsVisibleTo Include="My.Project.Tests" />
    </ItemGroup>

If a PublicKey is required, this attribute may also be added.

Alexander Høst
  • 918
  • 8
  • 16
6

In addition to all of the above, when everything seems to be correct, but the friend assembly stubbornly refuses to see any internals, reloading the solution or restarting Visual Studio can solve the problem.

Alex J
  • 9,905
  • 6
  • 36
  • 46
5

Here's a macro I use to quickly generate this attribute. Its a bit hacky, but it works. On my machine. When the latest signed binary is in /bin/debug. Etc equivocation etc. Anyhow, you can see how it gets the key, so that'll give you a hint. Fix/improve as your time permits.

Sub GetInternalsVisibleToForCurrentProject()
    Dim temp = "[assembly:  global::System.Runtime.CompilerServices." + _
               "InternalsVisibleTo(""{0}, publickey={1}"")]"
    Dim projs As System.Array
    Dim proj As Project
    projs = DTE.ActiveSolutionProjects()
    If projs.Length < 1 Then
        Return
    End If

    proj = CType(projs.GetValue(0), EnvDTE.Project)
    Dim path, dir, filename As String
    path = proj.FullName
    dir = System.IO.Path.GetDirectoryName(path)
    filename = System.IO.Path.GetFileNameWithoutExtension(path)
    filename = System.IO.Path.ChangeExtension(filename, "dll")
    dir += "\bin\debug\"
    filename = System.IO.Path.Combine(dir, filename)
    If Not System.IO.File.Exists(filename) Then
        MsgBox("Cannot load file " + filename)
        Return
    End If
    Dim assy As System.Reflection.Assembly
    assy = System.Reflection.Assembly.Load(filename)
    Dim pk As Byte() = assy.GetName().GetPublicKey()
    Dim hex As String = BitConverter.ToString(pk).Replace("-", "")
    System.Windows.Forms.Clipboard.SetText(String.Format(temp, assy.GetName().Name, hex))
    MsgBox("InternalsVisibleTo attribute copied to the clipboard.")
End Sub
4

You need to use the /out: compiler switch when compiling the friend assembly (the assembly that does not contain the InternalsVisibleTo attribute).

The compiler needs to know the name of the assembly being compiled in order to determine if the resulting assembly should be considered a friend assembly.

Ash
  • 60,973
  • 31
  • 151
  • 169
  • 1
    By default, the Microsoft C# targets will add the `/out` option if the .msbuild file contains the following lines: ` YourFriendAssemblyName `. This can also be set in the project's properties, in the "Application" tab, in the "Assembly name" field. – Suzanne Soy May 21 '13 at 17:15
3

You are required to generate an new full public key for the assembly and then specify the attribute to assembly.

[assembly: InternalsVisibleTo("assemblyname,
PublicKey="Full Public Key")]

Follow the below MSDN steps to generate new full public key for the assembly from visual studio.

To add a Get Assembly Public Key item to the Tools menu

In Visual Studio, click External Tools on the Tools menu.

In the External Tools dialog box, click Add and enter Get Assembly Public Key in the Title box.

Fill the Command box by browsing to sn.exe. It is typically installed at the following location: C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0a\Bin\x64\sn.exe.

In the Arguments box, type the following (case sensitive): -Tp $(TargetPath). Select the Use Output window check box.

Click OK. The new command is added to the Tools menu.

Whenever you need the Public Key Token of the assembly you are developing, click the Get Assembly Public Key command on the Tools menu, and the public key token appears in the Output window.

3

In my case using VS.Net 2015, I needed to sign BOTH assemblies (if at least 1 assembly shall be signed or you want to reference on the public key of your assembly).

My project didn't use signing at all. So I started adding a sign key to my test library and useing the InternalsVisibleTo-Attribute at my project's base library. But VS.Net always explained it couldn't access the friend methods.

When I started to sign the base library (it can be the same or another sign key - as long as you do sign the base library), VS.Net was immediately able to work as expected.

Jochen
  • 380
  • 1
  • 3
  • 9
2

Previous answers with PublicKey worked: (Visual Studio 2015: NEED to be on one line, otherwise it complains that the assembly reference is invalid or cannot referenced. PublicKeyToken didn't worked)

[assembly: InternalsVisibleTo("NameSpace.MyFriendAssembly, PublicKey=0024000004800000940000000602000000240000525341310004000001000100F73F4DDC11F0CA6209BC63EFCBBAC3DACB04B612E04FA07F01D919FB5A1579D20283DC12901C8B66A08FB8A9CB6A5E81989007B3AA43CD7442BED6D21F4D33FB590A46420FB75265C889D536A9519674440C3C2FB06C5924360243CACD4B641BE574C31A434CE845323395842FAAF106B234C2C1406E2F553073FF557D2DB6C5")]

Thanks to @Joe

To get the public key of the friend assembly:

sn -Tp path\to\assembly\MyFriendAssembly.dll

Inside a Developper command prompt (Startup > Programs > Visual Studio 2015 > Visual Studio Tools > Developer Command Prompt for VS2015). Thanks to @Ian G.

Although, the final touch that made it work for me after the above was to sign my friend library project the same way the project of the library to share is signed. Since it was a new Test library, it wasn't signed yet.

Micaël
  • 43
  • 6
2

Another possibility that may be tricky to track down, depending on how your code is written.

  1. You're invoking an internal method defined in X from another assembly Y
  2. The method signature uses internal types defined in Z
  3. You then have to add [InternalsVisibleTo] in X AND in Z

For example:

// In X
internal static class XType
{
    internal static ZType GetZ() { ... }
}

// In Y:
object someUntypedValue = XType.GetZ();

// In Z:
internal class ZType { ... }

If you have it written like above, where you're not referring to ZType directly in Y, after having added Y as a friend of X, you may be mystified why your code still doesn't compile.

The compilation error could definitely be more helpful in this case.

Tatiana Racheva
  • 1,289
  • 1
  • 13
  • 31
2

I'm writing this out of frustration. Make sure the assembly you are granting access to is named as you expect.

I renamed my project but this does not automatically update the Assembly Name. Right click your project and click Properties. Under Application, ensure that the Assembly Name and Default Namespace are what you expect.

Kameron Kincade
  • 514
  • 1
  • 7
  • 18
2

If you have more than 1 referenced assembly - check that all necessary assemblies have InternalsVisibleTo attribute. Sometimes it's not obviously, and no message that you have to add this attribute into else one assembly.

VladExL
  • 21
  • 1
  • I was banging my head against the wall until I finally found your answer. That was my problem: needed to add the InternalsVisibleTo attribute to all referenced projects. – gabnaim Dec 03 '21 at 18:38
2

1- Sign the test project

In Visual Studio go to the properties window of the test project and Sign the assembly by checking the checkbox with the same phrase in the Signing tab.

2- Create a PublicKey for the test project

Open Visual Studio Command Prompt (e.g. Developer Command Prompt for VS 2017). Go to the folder where the .dll file of the test project exists. Create a Public Key via sn.exe:

sn -Tp TestProject.dll

Note that the argument is -Tp, but not -tp.

3- Introduce the PublicKey to the project to be tested

Go to the AssemblyInfo.cs file in the project to be tested and add this line with the PublicKey created in the previous step:

[assembly: InternalsVisibleTo("TestProjectAssemblyName, PublicKey=2066212d128683a85f31645c60719617ba512c0bfdba6791612ed56350368f6cc40a17b4942ff16cda9e760684658fa3f357c137a1005b04cb002400000480000094000000060200000024000052534131000400000100010065fe67a14eb30ffcdd99880e9d725f04e5c720dffc561b23e2953c34db8b7c5d4643f476408ad1b1e28d6bde7d64279b0f51bf0e60be2d383a6c497bf27307447506b746bd2075")]

Don't forget to replace the above project name and PublicKey with yours.

4- Make the private method internal

In the project to be tested change the access modifier of the method to internal.

internal static void DoSomething(){...}
Karr
  • 395
  • 5
  • 9
1

Applies only if you like to keep unsigned assemblies as unsigned assembly (and don't want to sign it for several reasons):

There is still another point: if you compile your base library from VS.Net to a local directory, it may work as expected.

BUT: As soon as you compile your base library to a network drive, security policies apply and the assembly can't be successfully loaded. This again causes VS.NET or the compiler to fail when checking for the PublicKey match.

FINALLY, it's possible to use unsigned assemblies: https://msdn.microsoft.com/en-us/library/bb384966.aspx You must ensure that BOTH assemblies are NOT SIGNED And the Assembly attribute must be without PublicKey information:

<Assembly: InternalsVisibleTo("friend_unsigned_B")>

Jochen
  • 380
  • 1
  • 3
  • 9
1

I had the same problem. None of the solutions worked.

Eventually discovered the issue was due to class X explicitly implementing interface Y, which is internal.

the method X.InterfaceMethod was unavailable, though I have no idea why.

The solution was to cast (X as YourInterface).InterfaceMethod in the test library, and then things worked.

Sentinel
  • 3,582
  • 1
  • 30
  • 44
0

As a side note, if you want to easily get the public key without having to use sn and figure out its options you can download the handy program here. It not only determines the public key but also creates the "assembly: InternalsVisibleTo..." line ready to be copied to the clipboard and pasted into your code.

Bill W
  • 1,428
  • 1
  • 20
  • 29
0

I just resolved a similar problem with the InternalsVisibleTo Attribute. Everything seemed right and I couldn't figure out why the internal class I was aiming still wasn't accessible.

Changing the case of the key from upper to lower case fixed the problem.

Keysharpener
  • 486
  • 3
  • 14
0

InternalsVisibleTo is often used in the context of testing. If you're using a mocking framework such as Moq, you will need to expose the internals of your project to Moq as well. In the particular case of that framework, I also needed to add
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]to my AssemblyInfo.

Alexander Høst
  • 918
  • 8
  • 16
0

For what it's worth, I found that the property that was inaccessible due to it's protection level:

MyField field;

had to be changed to:

internal MyField field;

And it then compiled. I thought that internal was the default access modifier?

DevDave
  • 6,700
  • 12
  • 65
  • 99
0

In case anyone else was in the same position as me, I had assemblies which were not strongly typed.

My issue was that in the test I had declared my class as its interface not as an implementation eg.

private ISomeService _service didn't work, but private SomeService _service did. And this makes sense as the internal methods I had were not part of the interface

atamata
  • 997
  • 3
  • 14
  • 32
0

If InternalsVisibleTo is specified in the Project file, ensure that you did not also specify:

<GenerateAssemblyInfo>false</GenerateAssemblyInfo>

In order to avoid having to specify the public key but still sign the assembly, one possible way forward is to specify within the project file:

<Choose>
    <When Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
      <PropertyGroup>
          <GenerateAssemblyInfo>true</GenerateAssemblyInfo>
      </PropertyGroup>
      <ItemGroup>
          <InternalsVisibleTo Include="my.project.Test" />
      </ItemGroup>
    </When>
    <When Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
      <PropertyGroup>
          <AssemblyOriginatorKeyFile>my.project.snk</AssemblyOriginatorKeyFile>
      </PropertyGroup>
    </When>
</Choose> 

And then have the Test classes compile within the Debug version only, using:

#if DEBUG
    ...
#endif
Wolfgang Grinfeld
  • 870
  • 10
  • 11