我正在尝试使用InternalsVisibleTo
程序集属性使 .NET 类库中的内部类对我的单元测试项目可见。出于某种原因,我不断收到一条错误消息,上面写着:
'MyClassName' 由于其保护级别而无法访问
两个程序集都已签名,并且我在属性声明中列出了正确的密钥。有任何想法吗?
我正在尝试使用InternalsVisibleTo
程序集属性使 .NET 类库中的内部类对我的单元测试项目可见。出于某种原因,我不断收到一条错误消息,上面写着:
'MyClassName' 由于其保护级别而无法访问
两个程序集都已签名,并且我在属性声明中列出了正确的密钥。有任何想法吗?
您绝对确定您在属性中指定了正确的公钥吗?请注意,您需要指定完整的公钥,而不仅仅是公钥令牌。它看起来像:
[assembly: InternalsVisibleTo("MyFriendAssembly,
PublicKey=0024000004800000940000000602000000240000525341310004000001000100F73
F4DDC11F0CA6209BC63EFCBBAC3DACB04B612E04FA07F01D919FB5A1579D20283DC12901C8B66
A08FB8A9CB6A5E81989007B3AA43CD7442BED6D21F4D33FB590A46420FB75265C889D536A9519
674440C3C2FB06C5924360243CACD4B641BE574C31A434CE845323395842FAAF106B234C2C140
6E2F553073FF557D2DB6C5")]
它是 320 左右的十六进制数字。不确定为什么需要指定完整的公钥 - 可能只使用其他程序集引用中使用的公钥令牌,有人会更容易欺骗朋友程序集的身份。
另一个可能的“陷阱”:您在 中指定的朋友程序集的名称InternalsVisibleToAttribute
必须与朋友程序集的名称完全匹配,如朋友的项目属性中所示(在“应用程序”选项卡中)。
就我而言,我有一个项目Thingamajig
和一个配套项目ThingamajigAutoTests
(更改名称以保护有罪者)都产生了未签名的程序集。我适当地将属性添加到 Thingamajig\AssemblyInfo.cs 文件中,并如上所述[assembly: InternalsVisibleTo( "ThingamajigAutoTests" )]
注释掉AssemblyKeyFile
和属性。AssemblyKeyName
该Thingamajig
项目构建得很好,但其内部成员顽固地拒绝出现在自动测试项目中。
经过一番摸索,我重新检查了ThingamajigAutoTests
项目属性,发现程序集名称被指定为“ThingamajigAutoTests.dll”。宾果游戏 - 我在属性中的程序集名称中添加了“.dll”扩展名,InternalsVisibleTo
这些部件就位。
有时候,这是最小的事情......
如果您的程序集未签名,但您仍然收到相同的错误,请检查您的 AssemblyInfo.cs 文件中的以下任一行:
[assembly: AssemblyKeyFile("")]
[assembly: AssemblyKeyName("")]
如果存在这些行中的任何一个(或两个),属性选项卡仍会将您的程序集显示为未签名,但 InternalsVisibleTo 属性将具有这些行的程序集视为强签名。只需删除(或注释掉)这些行,它应该适合您。
值得注意的是,如果“friend”(测试)程序集是用 C++/CLI 而不是 C#/VB.NET 编写的,则需要使用以下内容:
#using "AssemblyUnderTest.dll" as_friend
而不是项目参考或通常的#using
陈述。出于某种原因,在项目参考 UI 中无法执行此操作。
您可以使用AssemblyHelper 工具,该工具将为您生成 InternalsVisibleTo 语法。这是最新版本的链接。请注意,它仅适用于强命名程序集。
这是我用来快速生成此属性的宏。它有点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
除了上述所有,当一切似乎都正确,但朋友程序集固执地拒绝看到任何内部时,重新加载解决方案或重新启动 Visual Studio可以解决问题。
在编译友元程序集(不包含 InternalsVisibleTo 属性的程序集)时,您需要使用 /out: 编译器开关。
编译器需要知道正在编译的程序集的名称,以确定生成的程序集是否应被视为友元程序集。
您需要为程序集生成一个新的完整公钥,然后为程序集指定属性。
[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)。选中使用输出窗口复选框。
单击确定。新命令被添加到工具菜单中。
每当您需要正在开发的程序集的公钥令牌时,单击工具菜单上的获取程序集公钥命令,公钥令牌就会出现在输出窗口中。
在我使用 VS.Net 2015 的情况下,我需要对两个程序集进行签名(如果至少要对 1 个程序集进行签名,或者您想在程序集的公钥上引用)。
我的项目根本没有使用签名。所以我开始在我的测试库中添加一个签名密钥,并在我的项目的基础库中使用 InternalsVisibleTo-Attribute。但是 VS.Net 总是解释它无法访问朋友方法。
当我开始对基础库进行签名时(它可以是相同的或另一个签名密钥——只要你签署了基础库),VS.Net 立即能够按预期工作。
以前使用 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。
虽然,在完成上述操作之后,它对我有用的最后一点是签署我的朋友库项目,就像签署要共享的库项目一样。由于它是一个新的测试库,它还没有签名。
另一种可能难以追踪的可能性,取决于您的代码是如何编写的。
例如:
// 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,您可能会感到困惑,为什么您的代码仍然无法编译。
在这种情况下,编译错误肯定会更有帮助。
我写这篇文章是出于沮丧。确保您授予访问权限的程序集按预期命名。
我重命名了我的项目,但这不会自动更新程序集名称。右键单击您的项目,然后单击Properties。在Application下,确保Assembly Name和Default Namespace是您所期望的。
仅当您希望将未签名的程序集保留为未签名的程序集时才适用(并且出于多种原因不想对其进行签名):
还有一点:如果您将基础库从 VS.Net 编译到本地目录,它可能会按预期工作。
但是:一旦您将基础库编译到网络驱动器,就会应用安全策略,并且无法成功加载程序集。这再次导致 VS.NET 或编译器在检查 PublicKey 匹配时失败。
最后,可以使用未签名的程序集: https ://msdn.microsoft.com/en-us/library/bb384966.aspx 您必须确保两个程序集都没有签名并且程序集属性必须没有 PublicKey 信息:
<Assembly: InternalsVisibleTo("friend_unsigned_B")>
如果您有超过 1 个引用的程序集 - 检查所有必要的程序集是否具有 InternalsVisibleTo 属性。有时并不明显,也没有消息表明您必须将此属性添加到其他程序集中。
我有同样的问题。没有一个解决方案奏效。
最终发现问题是由于 X 类显式实现了内部接口 Y。
X.InterfaceMethod 方法不可用,但我不知道为什么。
解决方案是在测试库中强制转换 (X as YourInterface).InterfaceMethod,然后一切正常。
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(){...}
附带说明一下,如果您想轻松获得公钥而无需使用 sn 并找出其选项,您可以在此处下载方便的程序。它不仅确定公钥,还创建“程序集:InternalsVisibleTo...”行,准备好复制到剪贴板并粘贴到您的代码中。
InternalsVisibleTo
我刚刚用属性解决了一个类似的问题。一切似乎都正确,我不明白为什么我瞄准的内部课程仍然无法访问。
将键的大小写从大写更改为小写解决了该问题。
尽管使用AssemblyInfo
文件并添加InternalsVisibleTo
仍然有效,但现在(据我所知)首选方法是ItemGroup
在您的项目csproj
文件中使用 an ,如下所示:
<ItemGroup>
<InternalsVisibleTo Include="My.Project.Tests" />
</ItemGroup>
如果需要 PublicKey,也可以添加该属性。