【发布时间】:2014-09-24 12:22:21
【问题描述】:
背景(不必要的,令人困惑的,仅供好奇的人使用)
我正在使用免费版 Unity3D for Mobile,它不允许我在移动设备上使用 System.Net.Sockets 命名空间。问题是我正在使用引用System.Net.Sockets 的已编译.dll 库(即IKVM)。我实际上并没有使用 IKVM 中引用 System.Net.Sockets 的类,因此我没有购买 3000 美元的 Unity Pro 移动许可证,而是制作了一个名为 dudeprgm.Net.Sockets 的 Sockets 命名空间的存根库,它只是替换了所有类和带有存根的方法(我使用 Mono 源代码完成此操作)。
我的问题
我需要将我的 dll 中的所有 System.Net.Sockets.* 引用替换为 dudeprgm.Net.Sockets.*。 我知道这样的事情是可能的并且由其他人完成(请参阅下面的编辑,在页面底部)。我想知道如何自己做。
我能够使用 Mono.Cecil 编写以下代码。
它遍历所有 IL 指令,检查操作数是否为 InlineType,然后检查内联类型是否是 System.Net.Sockets 的一部分,然后将其重命名为 dudeprgm.Net.Sockets 并写入。 **我不确定这是否是在 Mono.Cecil 中进行“查找和替换”的正确方法。问题是,这并不能捕获所有 Sockets 用法(见下文)。
private static AssemblyDefinition stubsAssembly;
static void Main(string[] args) {
AssemblyDefinition asm = AssemblyDefinition.ReadAssembly(args[0]);
stubsAssembly = AssemblyDefinition.ReadAssembly("Socket Stubs.dll");
// ...
// Call ProcessSockets on everything
// ...
asm.Write(args[1]);
}
/*
* This will be run on every property, constructor and method in the entire dll given
*/
private static void ProcessSockets(MethodDefinition method) {
if (method.HasBody) {
Mono.Collections.Generic.Collection<Instruction> instructions = method.Body.Instructions;
for (int i = 0; i < instructions.Count; i++) {
Instruction instruction = instructions[i];
if (instruction.OpCode.OperandType == OperandType.InlineType) {
string operand = instruction.Operand.ToString();
if (operand.StartsWith("System.Net.Sockets")) {
Console.WriteLine(method.DeclaringType + "." + method.Name + "(...) uses type " + operand);
Console.WriteLine("\t(Instruction: " + instruction.OpCode.ToString() + " " + instruction.Operand.ToString() + ")");
instruction.Operand = method.Module.Import(stubsAssembly.MainModule.GetType("dudeprgm.Net.Sockets", operand.Substring(19)));
Console.WriteLine("\tReplaced with type " + "dudeprgm.Net.Sockets" + operand.Substring(18));
}
}
}
}
}
它工作正常,但只能捕获“简单”指令。用ildasm 反编译,我可以看到它在哪里替换了类似这里的类型:
box ['Socket Stubs'/*23000009*/]dudeprgm.Net.Sockets.SocketOptionLevel/*01000058*/
但它没有捕捉到这些“复杂”的指令:
callvirt instance void [System/*23000003*/]System.Net.Sockets.Socket/*0100003F*/::SetSocketOption(valuetype [System/*23000003*/]System.Net.Sockets.SocketOptionLevel/*01000055*/,
valuetype [System/*23000003*/]System.Net.Sockets.SocketOptionName/*01000056*/,
int32) /* 0A000094 */
现在.dlls 是dudeprgm.Net.Sockets 和System.Net.Sockets 引用的混乱。
我很确定这种情况正在发生,因为我只是在更改 OperandType.InlineTypes,但我不确定如何做到这一点。我试过到处看看,但在我看来,Mono.Cecil 无法将操作数设置为string,一切似乎都只能使用 Cecil API (https://stackoverflow.com/a/7215711/837703) 来完成。
(对不起,如果我使用了不正确的术语,总的来说我对 IL 很陌生。)
问题
如何替换所有在 Mono.Cecil 中出现 System.Net.Sockets 的位置,而不仅仅是操作数是 InlineType 的位置?我真的不想遍历 Cecil 中的每个 OperandType,我只是在 Cecil 中寻找一些 find-and-replace 方法,我不必使用普通的 IL我自己。
编辑:(也是不必要的,令人困惑的,仅供好奇者使用)
这个人能够以 25 美元的价格做类似的事情:http://www.reddit.com/r/Unity3D/comments/1xq516/good_ol_sockets_net_sockets_for_mobile_without/。
自动修补工具,可检测并修复脚本和 .dll 中的套接字使用情况。
...
“DLL 是使用 Mono.Cecil 修补的。...
您可以查看https://www.assetstore.unity3d.com/en/#!/content/13166 的第二个屏幕截图,看到它说它可以替换命名空间。
那个库不符合我的需要,因为 1) 它没有重命名为我想要的命名空间 (dudeprgm.Net.Sockets),2) 它重命名的库不支持所有 System.Net.Sockets 类IKVM 需要,因为 IKVM 几乎使用每个 Sockets 类,并且 3) 它的成本为 25 美元,我真的不想购买我不会使用的东西。我只是想表明在 Mono.Cecil 中替换命名空间/类型引用是可能的。
【问题讨论】:
-
...有人吗?请帮忙。
-
如果你能得到统一的 dll,你可以反汇编它们看看发生了什么
-
@PhilipPittle 好的,但我不确定这会有所帮助。我认为我无法编辑它们以允许 Unity Free 编译引用
System.Net.Sockets的代码(另外,认为统一的那部分只是在一个大的 .exe 中)。我一直在寻找一种编辑 IKVM dll 以不包含System.Net.Sockets的方法:基本上,只需在 dll 中将一个命名空间替换为另一个命名空间。 :)
标签: c# replace types il mono.cecil