InternalsVisibleTo attribute isn't working
Asked Answered
Z

24

85

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?

Zareba answered 20/9, 2008 at 2:59 Comment(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.Trypanosome
C
109

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.

Costar answered 20/9, 2008 at 11:40 Comment(7)
To get the public key of the friend assembly "sn -Tp MyFriendAssembly"Gaidano
@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.Paramnesia
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.Contrariwise
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.Maisonette
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. Then replace the argument -T $(TargetPath) with -Tp $(TargetPath)Ludvig
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?Renayrenckens
@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.Fibula
B
45

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...

Barcarole answered 26/7, 2013 at 18:1 Comment(5)
+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 valuesAngrist
Hmm. Just the opposite for me. I had to remove the ".dll" for the solution to build.Truesdale
I can confirm @Truesdale 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.Cauca
With my typing accuracy, I'm pretty much doomed when it comes to getting this attribute right. Helpful tip!Millham
+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. :DMacready
M
38

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.

Mistletoe answered 29/8, 2010 at 13:9 Comment(2)
Thanks, this was my problem, and no where else could I find this answer.Myrtice
This saved me after getting "MyAssembly.dll does not represent a strongly named assembly" when calling sn.exe -Tp MyAssembly.dllSummon
S
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.

Stridulate answered 20/4, 2009 at 22:50 Comment(0)
A
10

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.

Amalbergas answered 17/11, 2021 at 15:16 Comment(2)
This works for .NET 5 and newer.Tenorite
For some reason it does not work when build with msbuild passing the solution file as parameterPutput
P
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.

Pluton answered 10/5, 2010 at 1:23 Comment(1)
Weird even using this it still isn't working in my .NET core 2.2 test applicationContinuum
D
7

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.

Debauch answered 17/9, 2014 at 12:3 Comment(1)
Reloading the two projects fixed it for me in Visual Studio 2017.Anglicism
P
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
Paramnesia answered 22/3, 2012 at 17:36 Comment(0)
E
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.

Eleni answered 20/9, 2008 at 3:16 Comment(1)
By default, the Microsoft C# targets will add the /out option if the .msbuild file contains the following lines: <PropertyGroup> <AssemblyName>YourFriendAssemblyName</AssemblyName> </PropertyGroup>. This can also be set in the project's properties, in the "Application" tab, in the "Assembly name" field.Velar
P
4

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.

Paeon answered 18/10, 2015 at 18:13 Comment(2)
Visual studio 2019 path is C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools\sn.exeSikko
and you can download the sdk here developer.microsoft.com/en-us/windows/downloads/windows-10-sdkSimplicity
W
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.

Wring answered 25/11, 2015 at 10:47 Comment(0)
O
3

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.

Olindaolinde answered 19/10, 2017 at 0:25 Comment(0)
W
3

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.

Walcoff answered 29/3, 2018 at 14:0 Comment(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.Touching
D
3

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(){...}
Dysuria answered 3/12, 2018 at 11:50 Comment(0)
S
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.

Sympetalous answered 14/10, 2015 at 15:57 Comment(0)
E
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.

Expatiate answered 8/10, 2016 at 0:10 Comment(0)
A
2

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.

Amalbergas answered 14/5, 2022 at 15:51 Comment(0)
W
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")>

Wring answered 25/11, 2015 at 11:34 Comment(0)
M
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.

Masoretic answered 29/3, 2018 at 15:50 Comment(0)
D
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.

Davis answered 26/4, 2012 at 18:25 Comment(0)
I
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.

Illume answered 26/2, 2013 at 9:20 Comment(0)
F
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?

Fournier answered 29/11, 2022 at 7:20 Comment(1)
the default access modifier is private.Antares
Q
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

Quintuplet answered 10/3, 2023 at 16:40 Comment(0)
O
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
Orthoclase answered 31/8, 2023 at 7:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.