背景(非必要,容易混淆,只针对好奇者)
我正在使用免费的Unity3D移动版,它不允许我在移动设备上使用System.Net.Sockets
命名空间。问题是我正在使用一个已编译的.dll
库(即IKVM),它引用了System.Net.Sockets
。实际上我并没有使用IKVM中引用System.Net.Sockets
的类,所以我制作了一个叫做dudeprgm.Net.Sockets
的Sockets
命名空间的存根库,它只是用存根替换了所有的类和方法(我使用了Mono源代码来完成这个工作)。
我的问题
我需要将我的dlls中的所有System.Net.Sockets.*
引用替换为dudeprgm.Net.Sockets.*
。我知道其他人已经做到了这样的事情(请参见本页底部的编辑)。我想知道如何自己完成这个操作。
它遍历所有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 */
现在,
.dll
中混杂着 dudeprgm.Net.Sockets
和 System.Net.Sockets
引用。我相信这是因为我只改变了 OperandType.InlineType
,但我不确定还有其他方法。我尝试到处查找,但似乎 Mono.Cecil 没有办法将操作数设置为 string
,似乎必须仅使用 Cecil API 完成所有操作(https://dev59.com/gFrUa4cB1Zd3GeqPmLDK#7215711)。对不起,如果我使用的术语不正确,我对 IL 一般都很陌生。问题:我如何替换 Mono.Cecil 中所有出现
System.Net.Sockets
的地方,而不仅仅是操作数为 InlineType
的地方?我不想逐个检查 Cecil 中的每个 OperandType
,我只是想在 Cecil 中找到一些“查找和替换”方法,而不必自己处理纯 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)它正在重命名的库不支持 IKVM 需要的所有 System.Net.Sockets
类,因为 IKVM 使用几乎每一个 Sockets 类,以及 3)它需要花费 $25,而我并不真正想购买我不会使用的东西。我只是想展示在 Mono.Cecil 中替换命名空间/类型引用是可能的。
System.Net.Sockets
的代码(而且,我认为Unity的这部分只在一个大的.exe文件中)。我更多地是在寻找一种方法来编辑我的IKVM dlls,以便不包括System.Net.Sockets
:基本上,只需在dll中用另一个命名空间替换一个命名空间。 :) - user837703