You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity Mod开发:预公开DLL时如何避免FieldAccessException?

解决预公开DLL引用导致的运行时FieldAccessException问题

问题根源

直接引用预公开DLL编译时,你的模组IL代码会把目标成员标记为public访问,但运行时游戏加载的是保留原私有成员权限的原始程序集。虽然Mono默认权限检查宽松,但静态构造函数(.cctor)这类场景下仍会触发严格校验,导致FieldAccessException。而BepInEx.AssemblyPublicizer.MSBuild除了生成公开DLL,还在编译阶段做了关键处理:仅用公开DLL通过编译校验,不复制到输出目录,同时配合BepInEx运行时钩子自动绕过权限检查。

不用MSBuild包的替代方案

你可以手动模拟MSBuild包的核心逻辑,分三步实现相同效果:

1. 配置编译引用,避免复制公开DLL到输出

修改项目csproj配置,确保预公开DLL仅用于编译,运行时加载游戏原DLL:

<!-- 引用预公开的游戏程序集 -->
<Reference Include="$(GameSolutionFolder)GameAssemblyPublicizer\out\**\*.dll">
  <Private>false</Private>
  <HintPath>%(FullPath)</HintPath>
</Reference>

Private="false"会阻止VS将公开DLL复制到模组输出目录,确保运行时加载游戏自带的原DLL。

2. 运行时绕过私有成员权限检查

直接编译的IL会带着public访问标记,与原成员实际权限冲突,必须用BepInEx工具绕过检查:

方法一:用AccessTools直接访问(推荐)

放弃直接字段/方法调用,改用BepInEx内置的AccessTools类获取成员,这是最稳定的方式:

// 获取静态字段
var targetField = AccessTools.Field(typeof(GameClassName), "GameMemberName");
var fieldValue = targetField.GetValue(null); // 静态字段传null,实例字段传对应对象

// 调用私有方法
var targetMethod = AccessTools.Method(typeof(GameClassName), "GameMethodName");
targetMethod.Invoke(null, new object[] { param1, param2 });

方法二:动态添加InternalsVisibleTo特性(进阶)

如果想保留直接调用的语法,可以在模组启动时给原游戏程序集动态添加InternalsVisibleTo特性,让它信任你的模组:

private void Awake()
{
    var gameAssembly = typeof(GameClassName).Assembly;
    var myAssemblyName = Assembly.GetExecutingAssembly().GetName().Name;

    // 反射获取程序集的自定义特性列表
    var customAttrField = typeof(Assembly).GetField("m_CustomAttributes", 
        BindingFlags.NonPublic | BindingFlags.Instance);
    var customAttrs = (List<CustomAttributeData>)customAttrField.GetValue(gameAssembly);

    // 添加InternalsVisibleTo特性
    var ivtCtor = typeof(InternalsVisibleToAttribute).GetConstructor(new[] { typeof(string) });
    customAttrs.Add(CustomAttributeData.GetCustomAttributeData(
        ivtCtor,
        new[] { CustomAttributeTypedArgument.FromConstant(myAssemblyName) }
    ));
}

注意:此方法仅对internal成员有效,private成员仍需用AccessTools或Harmony修改可见性。

3. 确保自定义公开脚本的正确性

你的公开脚本必须完全对齐BepInEx.AssemblyPublicizer的逻辑:

  • 将所有private/internal的字段、方法、属性、嵌套类型改为public
  • 保留原程序集的名称、版本、强名称(如果有),避免运行时程序集不匹配
  • 不要修改程序集的其他元数据(如文化信息、公钥)

关键注意事项

  • 绝对不要把预公开DLL放到游戏目录或模组输出目录,必须依赖游戏自带的原DLL
  • 如果游戏是IL2CPP编译的,Mono的权限检查会更严格,必须用AccessTools或Harmony处理所有私有成员访问
  • 尽量避免在静态构造函数中访问私有成员,这类场景最容易触发权限检查,建议移到模组的Awake/Start方法中

内容的提问来源于stack exchange,提问作者Luke Vo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 20:54:52