InternalsVisibleTo属性不起作用


73

我试图使用InternalsVisibleToAssembly属性使.NET类库中的内部类对我的单元测试项目可见。由于某种原因,我不断收到一条错误消息,内容为:

“ MyClassName”由于其保护级别而无法访问

两个程序集均已签名,并且属性声明中列出了正确的键。有任何想法吗?


1
您可以在要公开的类上发布InternalsVisibleTo属性的内容吗?很难说出什么毛病而没有看到自己在看什么。
Eric Sc​​hoonover,

Answers:


102

您绝对确定您具有属性中指定的正确公钥吗?请注意,您需要指定完整的公共密钥,而不仅仅是公共密钥令牌。看起来像:

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

它是320左右的十六进制数字。不确定为什么需要指定完整的公共密钥-可能仅使用其他程序集引用中使用的公共密钥令牌,这样某人就可以更容易地欺骗朋友程序集的身份。


50
获取朋友程序集“ sn -Tp MyFriendAssembly”的公钥
Ian G

@泰勒:这就是为什么你应该@人。我在下面添加一个答案,其中包含一个VS宏,可用于为您解决方案中的项目生成IVT。

12
为了阐明@Ian G的注释,请打开Visual Studio命令提示符,将目录(CD)更改为包含“ friend”程序集dll文件的相关目录。在命令提示符下,键入sn.exe -Tp NameOfAssembly,或sn - Tp NameOfAssembly按照Ian的指示输入。这使用Microsoft的“强名称”工具从程序集中查找并显示完整的公共密钥。如果只有一个包含密钥的.pub文件,则可以使用提取它sn.exe -p NameOfKeyFile.snk NameOfKeyPairFile.pub。这将创建NameOfKeyFile.snk密钥文件,然后使您可以遵循前面的说明。
谢里登

4
至少在VS2012中,真正重要的是,尽管此处和MSDN文档中的示例格式明显,该[assembly:InternalsVisibleTo("...")]属性也必须位于一行上。您不能只是从中复制粘贴公钥输出sn.exe -Tp MyFriendAssembly,然后使用@"multi line string"(请注意@字符),否则...does not contain a definition for 'X' and no extension method 'X' ... could be found当尝试从MyFriendAssembly访问内部时,编译器会抱怨。
AlwaysLearning 2015年

另外,请确保未在单元测试项目中创建新的密钥文件。您必须从主项目中加载密钥文件。我有点慢,(第一次)这样做了,并花了点时间试图弄清楚为什么它仍然无法正常工作。另外,可以在MSDN BLOG中找到一种非常简单的获取公钥的方法。然后更换参数-T $(TargetPath)-Tp $(TargetPath)
famousKaneis

40

另一个可能的“陷阱”:您在中指定的朋友程序集名称InternalsVisibleToAttribute必须与朋友程序集名称(在“应用程序”选项卡中)所示的朋友程序集名称完全匹配。

就我而言,我有一个项目Thingamajig和一个伴随项目ThingamajigAutoTests(名称更改为保护罪名),它们都产生了未签名的程序集。我将属性适当地添加[assembly: InternalsVisibleTo( "ThingamajigAutoTests" )]到Thingamajig \ AssemblyInfo.cs文件中,并注释了如上所述的AssemblyKeyFileAssemblyKeyName属性。该Thingamajig项目的构建还算不错,但是其内部成员却顽固地拒绝出现在自动测试项目中。

经过大量的抓挠之后,我重新检查了ThingamajigAutoTests项目属性,发现程序集名称指定为“ ThingamajigAutoTests.dll”。宾果游戏-我在InternalsVisibleTo属性的程序集名称中添加了“ .dll”扩展名,然后各部分就位了。

有时候这是最小的事情


2
+1为您的评论提供了帮助:就我而言,我在某个时候已重命名了程序集,但是VS在“项目属性”中保留了“程序集名称”和默认名称空间作为旧值
Nij

2
嗯 对我来说正好相反。我必须删除“ .dll”才能构建解决方案。
kmote

我可以确认@kmote是正确的:我必须删除“ .dll”才能构建解决方案(并使Intellisense正常工作)。(.NET 4.5,VS2013,未签名的程序集)非常奇怪的行为。这个问题及其所有答案对于解决具有这种明显有问题的属性的问题来说都是非常棒的。
davidbak

以我输入的准确性,在正确设置此属性时,我几乎注定了失败。有用的提示!
timmyl

+1也会出现空格。为什么我的程序集名称中有空格,我不知道,但这已解决。:D
user3797758

37

如果您的程序集未签名,但是仍然遇到相同的错误,请检查AssemblyInfo.cs文件中是否存在以下任一行:

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

如果存在这两个行中的一个(或两个)行,“属性”选项卡仍将您的程序集显示为未签名,但是InternalsVisibleTo属性会将具有这些行的程序集视为强签名。只需删除(或注释掉)这些行,它就可以正常工作。


1
谢谢,这是我的问题,没有其他地方可以找到此答案。
安德烈(Andre)

1
这在调用sn.exe -Tp MyAssembly.dll
Dunc

12

值得注意的是,如果“朋友”(测试)程序集是用C ++ / CLI而不是C#/ VB.Net编写的,则需要使用以下代码:

#using "AssemblyUnderTest.dll" as_friend

而不是项目参考或通常的#using声明。由于某些原因,在项目参考UI中无法执行此操作。



5

这是我用来快速生成此属性的宏。它有点hacky,但它可以工作。在我的机器上。当最新的签名二进制文件位于时/bin/debug。等等,无论如何,您可以看到它如何获取密钥,以便为您提供提示。在时间允许的情况下修复/改善。

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

5

除了上述所有内容之外,当一切似乎都正确时,但好友程序集顽固地拒绝查看任何内部信息,重新加载解决方案或重新启动Visual Studio即可解决问题。


重新加载这两个项目在Visual Studio 2017年固定对我来说
鲍勃Lokerse

4

编译好友程序集(不包含InternalsVisibleTo属性的程序集)时,需要使用/ out:编译器开关。

编译器需要知道正在编译的程序集的名称,以确定是否应将生成的程序集视为好友程序集。


默认情况下,微软C#的目标将增加/out,如果.msbuild文件包含以下行选项:<PropertyGroup> <AssemblyName>YourFriendAssemblyName</AssemblyName> </PropertyGroup>。也可以在项目的属性中的“应用程序”选项卡的“程序集名称”字段中进行设置。
苏珊·杜彭(SuzanneDupéron)

3

您需要为程序集生成新的完整公钥,然后为程序集指定属性。

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

请按照下面的MSDN步骤从Visual Studio中为程序集生成新的完整公钥。

要将“获取程序集公钥”项添加到“工具”菜单中

在Visual Studio中,在“工具”菜单上单击“外部工具”。

在“外部工具”对话框中,单击“添加”,然后在“标题”框中输入“获取装配公钥”。

通过浏览到sn.exe来填充“命令”框。通常将其安装在以下位置:C:\ Program Files(x86)\ Microsoft SDKs \ Windows \ v7.0a \ Bin \ x64 \ sn.exe

在“参数”框中,键入以下内容(区分大小写):- Tp $(TargetPath)。选择使用输出窗口复选框。

单击确定。新命令将添加到“工具”菜单中。

每当需要开发的程序集的公钥令牌时,请单击“工具”菜单上的“获取程序集公钥”命令,该公钥令牌会出现在“输出”窗口中。


Visual Studio 2019的路径是C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools\sn.exe
Vishal


3

在使用VS.Net 2015的情况下,我需要对两个程序集都进行签名(如果至少要签名1个程序集,或者要在程序集的公钥上引用)。

我的项目根本没有使用签名。因此,我开始在测试库中添加符号键,并在项目的基础库中使用InternalsVisibleTo-Attribute。但是VS.Net总是解释说它无法访问朋友方法。

当我开始对基础库进行签名时(只要您对基础库进行签名,它可以是相同的符号,也可以是另一个签名密钥),VS.Net可以立即按预期工作。


2

以前使用PublicKey的答案有效:(Visual Studio 2015:需要在一行上,否则它将抱怨程序集引用无效或无法引用。PublicKeyToken不起作用)

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

感谢@Joe

获取朋友程序集的公钥:

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

在Developper命令提示符(启动>程序> Visual Studio 2015> Visual Studio工具> VS2015的开发人员命令提示符)内。感谢@Ian G.

虽然,在上述操作之后对我有用的最后一点是对我的朋友库项目进行签名,就像对共享库的项目进行签名一样。由于它是一个新的测试库,因此尚未签名。


2

根据代码的编写方式,可能很难追踪的另一种可能性。

  1. 您正在从另一个程序集Y调用X中定义的内部方法
  2. 方法签名使用Z中定义的内部类型
  3. 然后,您必须在X AND中添加[InternalsVisibleTo]

例如:

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

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

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

如果您是像上面那样编写的,那么在将Y添加为X的朋友之后,您并没有直接在Y中引用ZType,您可能会感到奇怪,为什么您的代码仍然无法编译。

在这种情况下,编译错误肯定会更有帮助。


2

我出于沮丧而写这本书。确保授予您访问权限的程序集按您的期望命名。

我重命名了项目,但这不会自动更新程序集名称。右键单击您的项目,然后单击属性。在“应用程序”下,确保所需的名称是“程序集名称”和“默认名称空间”


1

仅当您希望将未签名的程序集保留为未签名的程序集(并且出于多种原因而不想对其进行签名)时,才适用:

还有一点:如果将VS.Net的基础库编译到本地目录,则它可能会按预期工作。

但是:将基础库编译到网络驱动器后,将立即应用安全策略,并且程序集无法成功加载。这再次导致VS.NET或编译器在检查PublicKey匹配项时失败。

最后,可以使用未签名的程序集:https : //msdn.microsoft.com/zh-cn/library/bb384966.aspx 您必须确保两个程序集均未签名,并且Assembly属性必须没有PublicKey信息:

<Assembly: InternalsVisibleTo("friend_unsigned_B")>


1

我有同样的问题。这些解决方案均无效。

最终发现该问题是由于类X显式实现了内部接口Y所致。

方法X.InterfaceMethod不可用,尽管我不知道为什么。

解决的方法是在测试库中强制转换(X为YourInterface).InterfaceMethod,然后一切正常。


0

附带说明一下,如果您想轻松获取公共密钥而不必使用sn并弄清其选项,则可以在此处下载方便的程序。它不仅确定公钥,还创建“ assembly:InternalsVisibleTo ...”行,准备将其复制到剪贴板并粘贴到您的代码中。


0

我刚刚解决了与InternalsVisibleTo属性类似的问题。一切似乎都是正确的,我无法弄清楚为什么我瞄准的内部课程仍然无法访问。

将键的大小写从大写改为小写可解决此问题。


0

如果引用的程序集超过1个,请检查所有必需的程序集是否具有InternalsVisibleTo属性。有时情况不是很明显,也没有消息表明您必须将此属性添加到另一个程序集中。


0

1 -登录测试项目:在Visual Studio中去的属性窗口的测试项目,并签署组件通过检查与在同一短语的复选框签名选项卡。

2-为测试项目创建一个PublicKey:打开Visual Studio命令提示符(例如VS 2017的Developer Command Prompt)。转到测试项目的.dll文件所在的文件夹。通过sn.exe创建公用密钥:

sn -Tp TestProject.dll

请注意,该参数是-Tp,而不是-tp。

3-介绍公钥对项目进行测试:转到AssemblyInfo.cs文件中的项目进行测试,并在上一步中创建的公钥加入这一行:

[组件:InternalsVisibleTo(” TestProjectAssemblyName公钥= 2066212d128683a85f31645c60719617ba512c0bfdba6791612ed56350368f6cc40a17b4942ff16cda9e760684658fa3f357c137a1005b04cb002400000480000094000000060200000024000052534131000400000100010065fe67a14eb30ffcdd99880e9d725f04e5c720dffc561b23e2953c34db8b7c5d4643f476408ad1b1e28d6bde7d64279b0f51bf0e60be2d383a6c497bf27307447506b746bd2075" )]

不要忘记用您的替换上面的PublicKey。

4-将私有方法设为内部:在要测试的项目中,将方法的访问修饰符更改为内部。

内部静态void DoSomething(){...}

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.