Skip to content

C# 使用友元程序集访问引用中的 internal 类型及成员

🏷️ C#

新建两个工程

  • AccessFriendAssembly 控制台工程
  • FriendAssembly 类库工程

在 FriendAssembly 工程添加一个只允许工程内部访问的类 SomeInternalType

csharp
namespace FriendAssembly
{
    internal sealed class SomeInternalType
    {
        internal int SomeInternalProperty { get; set; }
    }
}

在控制台程序中尝试使用该类型:

csharp
using FriendAssembly;
using System;

namespace AccessFriendAssembly
{
    class Program
    {
        static void Main(string args)
        {
            SomeInternalType it = new SomeInternalType();
            it.SomeInternalProperty = 1;
            Console.WriteLine(it.SomeInternalProperty);
            Console.ReadLine();
        }
    }
}

编译会直接报错:

“FriendAssembly.SomeInternalType”不可访问,因为它受保护级别限制

FriendAssembly->Properties->AssemblyInfo.cs 中使用 InternalsVisibleTo 特性,将 AccessFriendAssembly 程序集设置为友元程序集,则在 AccessFriendAssembly 工程中,则可以像访问 public 成员一样访问 FriendAssembly 程序集中的 internal 成员。

cs
[assembly: InternalsVisibleTo("AccessFriendAssembly")]

下面给 AccessFriendAssembly 工程添加签名,使其变为强命名程序集。

  1. 在工程属性->签名里,勾上【为程序集签名】,在【选择强名称秘钥文件】下拉框中选择【<新建...>】

  2. 假设秘钥文件名称为【MySign】,并设置密码保护秘钥文件;算法这里使用的【sha256RSA】。(记住设置的密码,后面要用到)

  3. 再保存工程即可。

再次编译会显示如下错误:

程序集生成失败 -- 引用的程序集“FriendAssembly”没有强名称 AccessFriendAssembly

需要为程序集“FriendAssembly”也添加强名称,否则无法引用。

同样的步骤为“FriendAssembly”添加签名文件。

再次编译还是出错:

友元程序集引用“AccessFriendAssembly”无效。强名称签名的程序集必须在其 InternalsVisibleTo 声明中指定一个公钥。FriendAssembly\Properties\AssemblyInfo.cs 39 31 FriendAssembly

需要为友元程序集指定公钥,公钥的格式如下:

cs
[assembly: InternalsVisibleTo("AccessFriendAssembly, PublicKey=xxxxxx")]

指定了公钥后,编译就能通过了并正确执行了。

剩下的就是如何取得公钥了。按照搜索到的结果使用 sn 命令获取公钥,结果如下:

bash
>sn -Tp AccessFriendAssembly.vshost.exe

Microsoft(R) .NET Framework 强名称实用工具 版本 4.0.30319.17929
版权所有(C) Microsoft Corporation。保留所有权利。

标识公钥(哈希算法: sha1):
002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9
f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad2361321
02900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93
c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc09334
4d5ad293

签名公钥(哈希算法: sha256):
002400000c800000140100000602000000240000525341310008000001000100613399aff18ef1
a2c2514a273a42d9042b72321f1757102df9ebada69923e2738406c21e5b801552ab8d200a65a2
35e001ac9adc25f2d811eb09496a4c6a59d4619589c69f5baf0c4179a47311d92555cd006acc8b
5959f2bd6e10e360c34537a1d266da8085856583c85d81da7f3ec01ed9564c58d93d713cd0172c
8e23a10f0239b80c96b07736f5d8b022542a4e74251a5f432824318b3539a5a087f8e53d2f135f
9ca47f3bb2e10aff0af0849504fb7cea3ff192dc8de0edad64c68efde34c56d302ad55fd6e80f3
02d5efcdeae953658d3452561b5f36c542efdbdd9f888538d374cef106acf7d93a4445c3c73cd9
11f0571aaf3d54da12b11ddec375b3

公钥标记为 b03f5f7f11d50a3a

把上面的三个值都试了一遍,使用标志公钥和签名公钥时,FriendAssembly 能编译通过,但 AccessFriendAssembly 仍然编译不通过。

这说明指定的公钥不对。

又看到个方法,从秘钥文件获取公钥(需要输入之前设定的密码):

bash
>sn -p MySign.pfx MySign.PK.pfx

Microsoft(R) .NET Framework 强名称实用工具 版本 4.0.30319.17929
版权所有(C) Microsoft Corporation。保留所有权利。

请输入 PKCS#12 密钥文件的密码:
公钥已写入到 MySign.PK.pfx
打开输出文件MySign.PK.pfx,内容如下:
0024 0000 0480 0000 9400 0000 0602 0000
0024 0000 5253 4131 0004 0000 0100 0100
8155 3695 beee e766 5242 da1c eca4 a9f1
9223 afe7 25ca a012 790f 16b0 a70d 4350
5cc7 b3a0 0d89 fd3d f0e2 e71a 7318 b3cc
7c66 66db 03e9 8e97 ab03 79e4 d944 cdda
3fd9 78fd ff02 03f2 c24a 8fa8 cb78 4636
e392 082b c99d 8905 2eeb 8709 48c3 370e
cba4 93ee 738b cf20 f11e 054c 89d1 9961
5be6 1d9f 3925 1d96 7547 5b45 7d49 699d

将上面的空格去掉,改成一行后设定到 PublicKey 中,程序终于正常运行了。

cs
[assembly: InternalsVisibleTo("AccessFriendAssembly, PublicKey=002400000480000094000000060200000024000052534131000400000100010081553695beeee7665242da1ceca4a9f19223afe725caa012790f16b0a70d43505cc7b3a00d89fd3df0e2e71a7318b3cc7c6666db03e98e97ab0379e4d944cdda3fd978fdff0203f2c24a8fa8cb784636e392082bc99d89052eeb870948c3370ecba493ee738bcf20f11e054c89d199615be61d9f39251d9675475b457d49699d")]

参考:

  1. http://www.cnblogs.com/lmule/archive/2010/08/15/1800227.html

  2. http://www.cnblogs.com/artech/archive/2010/10/06/1844721.html