我试图使用InternalsVisibleTo
Assembly属性使.NET类库中的内部类对我的单元测试项目可见。由于某种原因,我不断收到一条错误消息,内容为:
“ MyClassName”由于其保护级别而无法访问
两个程序集均已签名,并且属性声明中列出了正确的键。有任何想法吗?
Answers:
您绝对确定您具有属性中指定的正确公钥吗?请注意,您需要指定完整的公共密钥,而不仅仅是公共密钥令牌。看起来像:
[assembly: InternalsVisibleTo("MyFriendAssembly,
PublicKey=0024000004800000940000000602000000240000525341310004000001000100F73
F4DDC11F0CA6209BC63EFCBBAC3DACB04B612E04FA07F01D919FB5A1579D20283DC12901C8B66
A08FB8A9CB6A5E81989007B3AA43CD7442BED6D21F4D33FB590A46420FB75265C889D536A9519
674440C3C2FB06C5924360243CACD4B641BE574C31A434CE845323395842FAAF106B234C2C140
6E2F553073FF557D2DB6C5")]
它是320左右的十六进制数字。不确定为什么需要指定完整的公共密钥-可能仅使用其他程序集引用中使用的公共密钥令牌,这样某人就可以更容易地欺骗朋友程序集的身份。
sn.exe -Tp NameOfAssembly
,或sn - Tp NameOfAssembly
按照Ian的指示输入。这使用Microsoft的“强名称”工具从程序集中查找并显示完整的公共密钥。如果只有一个包含密钥的.pub文件,则可以使用提取它sn.exe -p NameOfKeyFile.snk NameOfKeyPairFile.pub
。这将创建NameOfKeyFile.snk密钥文件,然后使您可以遵循前面的说明。
[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访问内部时,编译器会抱怨。
-T $(TargetPath)
与-Tp $(TargetPath)
另一个可能的“陷阱”:您在中指定的朋友程序集名称InternalsVisibleToAttribute
必须与朋友程序集名称(在“应用程序”选项卡中)所示的朋友程序集名称完全匹配。
就我而言,我有一个项目Thingamajig
和一个伴随项目ThingamajigAutoTests
(名称更改为保护罪名),它们都产生了未签名的程序集。我将属性适当地添加[assembly: InternalsVisibleTo( "ThingamajigAutoTests" )]
到Thingamajig \ AssemblyInfo.cs文件中,并注释了如上所述的AssemblyKeyFile
和AssemblyKeyName
属性。该Thingamajig
项目的构建还算不错,但是其内部成员却顽固地拒绝出现在自动测试项目中。
经过大量的抓挠之后,我重新检查了ThingamajigAutoTests
项目属性,发现程序集名称指定为“ ThingamajigAutoTests.dll”。宾果游戏-我在InternalsVisibleTo
属性的程序集名称中添加了“ .dll”扩展名,然后各部分就位了。
有时候这是最小的事情
如果您的程序集未签名,但是仍然遇到相同的错误,请检查AssemblyInfo.cs文件中是否存在以下任一行:
[assembly: AssemblyKeyFile("")]
[assembly: AssemblyKeyName("")]
如果存在这两个行中的一个(或两个)行,“属性”选项卡仍将您的程序集显示为未签名,但是InternalsVisibleTo属性会将具有这些行的程序集视为强签名。只需删除(或注释掉)这些行,它就可以正常工作。
您可以使用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
编译好友程序集(不包含InternalsVisibleTo属性的程序集)时,需要使用/ out:编译器开关。
编译器需要知道正在编译的程序集的名称,以确定是否应将生成的程序集视为好友程序集。
/out
,如果.msbuild文件包含以下行选项:<PropertyGroup> <AssemblyName>YourFriendAssemblyName</AssemblyName> </PropertyGroup>
。也可以在项目的属性中的“应用程序”选项卡的“程序集名称”字段中进行设置。
您需要为程序集生成新的完整公钥,然后为程序集指定属性。
[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)。选择使用输出窗口复选框。
单击确定。新命令将添加到“工具”菜单中。
每当需要开发的程序集的公钥令牌时,请单击“工具”菜单上的“获取程序集公钥”命令,该公钥令牌会出现在“输出”窗口中。
C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools\sn.exe
以前使用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,您可能会感到奇怪,为什么您的代码仍然无法编译。
在这种情况下,编译错误肯定会更有帮助。
我出于沮丧而写这本书。确保授予您访问权限的程序集按您的期望命名。
我重命名了项目,但这不会自动更新程序集名称。右键单击您的项目,然后单击属性。在“应用程序”下,确保所需的名称是“程序集名称”和“默认名称空间”。
仅当您希望将未签名的程序集保留为未签名的程序集(并且出于多种原因而不想对其进行签名)时,才适用:
还有一点:如果将VS.Net的基础库编译到本地目录,则它可能会按预期工作。
但是:将基础库编译到网络驱动器后,将立即应用安全策略,并且程序集无法成功加载。这再次导致VS.NET或编译器在检查PublicKey匹配项时失败。
最后,可以使用未签名的程序集:https : //msdn.microsoft.com/zh-cn/library/bb384966.aspx 您必须确保两个程序集均未签名,并且Assembly属性必须没有PublicKey信息:
<Assembly: InternalsVisibleTo("friend_unsigned_B")>
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(){...}