简介
在Unity Editor开发过程中,我们会经常使用反射调用一些unity还没开放的接口,比如s_LastControlID
, 但每个程序集都写一边反射不免显得有些麻烦。
本篇文章将介绍注入InternalsVisibleToAttribute
注解到unity dll的方法,来帮助大家更便捷地调用unity 的内部函数。
思路
InternalsVisibleToAttribute
可以为当前程序集添加友元程序集,友元程序集可以调用当前程序集的internal函数。
Cecil
可以编辑当前程序集的IL代码和元数据。 那么如果我们在unity dll的元数据中将Assembly-Csharp
程序集添加为友元,那么就能实现Assembly-Csharp
调用对应unity dll的internal函数。
代码及使用方法
核心类是AssemblyHacker
, 通过传入目标程序集的绝对路径和友元程序集的FullName,可以实现添加友元程序集的效果。
internal class AssemblyHacker
{
private string targetAsmFileName;
private AssemblyDefinition assemblyDef;
private DefaultAssemblyResolver assemblyResolver;
//targetAsms是 需要修改的dll绝对路径的集合
//friedns是友元程序集的FullName
public static void Hack(ICollection<string> targetAsms, ICollection<string> friends)
{
foreach (var targetAsm in targetAsms)
{
var hacker = new AssemblyHacker();
hacker.SetAssembly(targetAsm);
hacker.Work(friends);
//dispose Assembly不会清除 resolver里面缓存的 引用Assembly,需要手动清理
hacker.assemblyResolver.Dispose();
}
}
private void SetAssembly(string assemblyName)
{
targetAsmFileName = assemblyName;
if (assemblyResolver == null)
{
assemblyResolver = new DefaultAssemblyResolver();
var targetDirName = Path.GetDirectoryName(targetAsmFileName);
var targetDirName2 = Path.GetDirectoryName(targetDirName);
assemblyResolver.AddSearchDirectory(targetDirName);
assemblyResolver.AddSearchDirectory(targetDirName2);
}
assemblyDef = AssemblyDefinition.ReadAssembly(targetAsmFileName,
new ReaderParameters { AssemblyResolver = assemblyResolver, ReadWrite = true });
}
private void Work(ICollection<string> friends)
{
AddFriendForTarget(friends);
}
private void AddFriendForTarget(ICollection<string> friends)
{
var friendAssemblyList = friends.ToList();
var assemblyAttrs = assemblyDef.CustomAttributes;
//如果已经是友元程序集,忽略它。
foreach (var assemblyAttr in assemblyAttrs)
{
if (assemblyAttr.AttributeType.FullName.Equals(typeof(InternalsVisibleToAttribute).FullName))
{
var oldFriendAssembly = assemblyAttr.ConstructorArguments[0].Value.ToString();
friendAssemblyList.Remove(oldFriendAssembly);
}
}
//var attrType = GetInternalsVisibleType();
var attrType = assemblyDef.MainModule.ImportReference(typeof(InternalsVisibleToAttribute));
var attCtor = attrType.Resolve().Methods.First(x => x.Name == ".ctor" && x.Parameters.Count == 1);
MethodReference attrCtorMethodRef;
attrCtorMethodRef = assemblyDef.MainModule.ImportReference(attCtor);
foreach (var friend in friendAssemblyList)
{
var attr = new CustomAttribute(attrCtorMethodRef);
attr.ConstructorArguments.Add(new CustomAttributeArgument(attrType, friend));
assemblyAttrs.Add(attr);
}
//var newAssemblyFile = targetAsmFileName.Replace(".dll", "New.dll");
assemblyDef.Write();
assemblyDef.Dispose();
//Console.WriteLine("write result to " + newAssemblyFile);
}
private TypeReference GetInternalsVisibleType()
{
foreach (var attr in assemblyDef.CustomAttributes)
{
if (attr.AttributeType.FullName.Equals("System.Runtime.CompilerServices.InternalsVisibleToAttribute"))
{
Console.WriteLine("attrType Asm is " + attr.AttributeType.Module.FileName);
return attr.AttributeType;
}
}
throw new Exception("cannot find InternalsVisibleToAttribute Type in assembly Definition");
}
}
入口函数
/// <summary>
/// 传入的参数是 需要修改的程序集的 文件名
/// 当前程序会将 对应的程序集
/// </summary>
internal class Program
{
static void Main(string[] args)
{
PrivilegeCheck.Check();
var lines=File.ReadAllLines("MonoCecilProxy.txt");
var friendlAsmStrs = lines[0].Trim().Split(' ');
List<string> targetAsms = new List<string>();
for (int i = 1; i < lines.Length; i++)
{
var line = lines[i].Trim();
if (line.Equals(string.Empty))
break;
targetAsms.Add(lines[i].Trim());
}
AssemblyHacker.Hack(targetAsms, friendlAsmStrs);
Console.WriteLine("finished");
Console.ReadKey();
}
}
Assembly-CSharp
C:\Program Files\Unity\Hub\Editor\2022.3.32f1\Editor\Data\Managed\UnityEngine\UnityEditor.CoreModule.dll
C:\Program Files\Unity\Hub\Editor\2022.3.32f1\Editor\Data\Managed\UnityEngine\UnityEngine.IMGUIModule.dll
// 这里第一行是需要添加进去的 友元程序集的 名称
// 剩下的几行是需要被修改的程序集的 文件名FullPath
程序权限
由于unity dll位于C盘,这里我们需要提升程序的权限。
csproj项目文件里添加app.manifest
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net462</TargetFramework>
<ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup>
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
</assembly>
标签:assemblyDef,UnityDll,程序,private,dll,internal,var,new,Cecil
From: https://www.cnblogs.com/dewxin/p/18679627