76

我正在尝试使用InternalsVisibleTo程序集属性使 .NET 类库中的内部类对我的单元测试项目可见。出于某种原因,我不断收到一条错误消息,上面写着:

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

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

4

20 回答 20

103

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

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

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

于 2008-09-20T11:40:12.633 回答
43

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

就我而言,我有一个项目Thingamajig和一个配套项目ThingamajigAutoTests(更改名称以保护有罪者)都产生了未签名的程序集。我适当地将属性添加到 Thingamajig\AssemblyInfo.cs 文件中,并如上所述[assembly: InternalsVisibleTo( "ThingamajigAutoTests" )]注释掉AssemblyKeyFile和属性。AssemblyKeyNameThingamajig项目构建得很好,但其内部成员顽固地拒绝出现在自动测试项目中。

经过一番摸索,我重新检查了ThingamajigAutoTests项目属性,发现程序集名称被指定为“ThingamajigAutoTests.dll”。宾果游戏 - 我在属性中的程序集名称中添加了“.dll”扩展名,InternalsVisibleTo这些部件就位。

有时候,这是最小的事情......

于 2013-07-26T18:01:04.757 回答
37

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

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

如果存在这些行中的任何一个(或两个),属性选项卡仍会将您的程序集显示为未签名,但 InternalsVisibleTo 属性将具有这些行的程序集视为强签名。只需删除(或注释掉)这些行,它应该适合您。

于 2010-08-29T13:09:14.877 回答
12

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

#using "AssemblyUnderTest.dll" as_friend

而不是项目参考或通常的#using陈述。出于某种原因,在项目参考 UI 中无法执行此操作。

于 2009-04-20T22:50:02.560 回答
9

您可以使用AssemblyHelper 工具,该工具将为您生成 InternalsVisibleTo 语法。这是最新版本的链接。请注意,它仅适用于强命名程序集。

于 2010-05-10T01:23:47.067 回答
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
于 2012-03-22T17:36:45.837 回答
5

除了上述所有,当一切似乎都正确,但朋友程序集固执地拒绝看到任何内部时,重新加载解决方案或重新启动 Visual Studio可以解决问题。

于 2014-09-17T12:03:10.330 回答
4

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

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

于 2008-09-20T03:16:25.513 回答
3

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

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

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

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

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

在 External Tools 对话框中,单击Add并在 Title 框中输入 Get Assembly Public Key。

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

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

单击确定。新命令被添加到工具菜单中。

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

于 2015-10-18T18:13:59.380 回答
3

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

我的项目根本没有使用签名。所以我开始在我的测试库中添加一个签名密钥,并在我的项目的基础库中使用 InternalsVisibleTo-Attribute。但是 VS.Net 总是解释它无法访问朋友方法。

当我开始对基础库进行签名时(它可以是相同的或另一个签名密钥——只要你签署了基础库),VS.Net 立即能够按预期工作。

于 2015-11-25T10:47:16.457 回答
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。

虽然,在完成上述操作之后,它对我有用的最后一点是签署我的朋友库项目,就像签署要共享的库项目一样。由于它是一个新的测试库,它还没有签名。

于 2015-10-14T15:57:50.527 回答
2

另一种可能难以追踪的可能性,取决于您的代码是如何编写的。

  1. 您正在从另一个程序集 Y 调用 X 中定义的内部方法
  2. 方法签名使用 Z 中定义的内部类型
  3. 然后您必须在 X 和 Z 中添加 [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,您可能会感到困惑,为什么您的代码仍然无法编译。

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

于 2016-10-08T00:10:49.373 回答
2

我写这篇文章是出于沮丧。确保您授予访问权限的程序集按预期命名。

我重命名了我的项目,但这不会自动更新程序集名称。右键单击您的项目,然后单击Properties。在Application下,确保Assembly NameDefault Namespace是您所期望的。

于 2017-10-19T00:25:44.540 回答
1

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

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

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

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

<Assembly: InternalsVisibleTo("friend_unsigned_B")>

于 2015-11-25T11:34:43.130 回答
1

如果您有超过 1 个引用的程序集 - 检查所有必要的程序集是否具有 InternalsVisibleTo 属性。有时并不明显,也没有消息表明您必须将此属性添加到其他程序集中。

于 2018-03-29T14:00:50.667 回答
1

我有同样的问题。没有一个解决方案奏效。

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

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

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

于 2018-03-29T15:50:16.743 回答
1

1- 对测试项目进行签名:在 Visual Studio 中,转到测试项目的属性窗口,并通过在“签名”选项卡中选中具有相同短语的复选框来对程序集进行签名。

2- 为测试项目创建公钥:打开 Visual Studio 命令提示符(例如 VS 2017 的开发人员命令提示符)。转到测试项目的 .dll 文件所在的文件夹。通过 sn.exe 创建公钥:

sn -Tp TestProject.dll

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

3- 将PublicKey引入待测项目:进入待测项目的AssemblyInfo.cs文件,在上一步创建的PublicKey中加入这一行:

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

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

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

内部静态无效 DoSomething(){...}

于 2018-12-03T11:50:17.563 回答
0

附带说明一下,如果您想轻松获得公钥而无需使用 sn 并找出其选项,您可以在此处下载方便的程序。它不仅确定公钥,还创建“程序集:InternalsVisibleTo...”行,准备好复制到剪贴板并粘贴到您的代码中。

于 2012-04-26T18:25:51.517 回答
0

InternalsVisibleTo我刚刚用属性解决了一个类似的问题。一切似乎都正确,我不明白为什么我瞄准的内部课程仍然无法访问。

将键的大小写从大写更改为小写解决了该问题。

于 2013-02-26T09:20:02.417 回答
0

尽管使用AssemblyInfo文件并添加InternalsVisibleTo仍然有效,但现在(据我所知)首选方法是ItemGroup在您的项目csproj文件中使用 an ,如下所示:

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

如果需要 PublicKey,也可以添加该属性。

于 2021-11-17T15:16:55.157 回答