Background (unnecessary, confusing, only for the curious)
I'm using the free version of Unity3D for Mobile and it doesn't allow me to use the System.Net.Sockets
namespace on mobile devices. The problem is that I'm using a compiled .dll
library (namely, IKVM) that references the System.Net.Sockets
. I'm not actually using the classes in IKVM that references that references System.Net.Sockets
, so instead of buying the $3000 Unity Pro mobile licenses, I made a stub library of the Sockets
namespace called dudeprgm.Net.Sockets
that just replaces all the classes and methods with stubs (I did this using the Mono source code).
My problem
I need to replace all System.Net.Sockets.*
references in my dlls to dudeprgm.Net.Sockets.*
. I know that something like this is possible and done by other people(See EDIT below, at the bottom of the page). I would like to know how to do it myself.
I was able to come up with the following code using Mono.Cecil.
It goes through all the IL instructions, checks if the operand is an InlineType
, then checks if the inline type is part of System.Net.Sockets
, then renames it to dudeprgm.Net.Sockets
and writes it. **I'm not sure if this is the right way to go about "finding-and-replacing" in Mono.Cecil. Problem is, this doesn't catch all Sockets
usages (see below).
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));
}
}
}
}
}
It works fine, but only catches "simple" instructions. Decompiled with ildasm
, I can see where it replaced the types like here:
box ['Socket Stubs'/*23000009*/]dudeprgm.Net.Sockets.SocketOptionLevel/*01000058*/
But it didn't catch these "complex" instructions:
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 */
Now the .dll
s are a jumble of dudeprgm.Net.Sockets
and System.Net.Sockets
references.
I'm pretty sure that this is happening because I'm only changing OperandType.InlineType
s, but I'm not sure on how else to do this. I've tried looking around everywhere, but it seems to me like Mono.Cecil has no way to set operands to a string
, everything seems to have to be done using the Cecil API only (https://stackoverflow.com/a/7215711/837703).
(Sorry if I'm using incorrect terms, I'm pretty new to IL in general.)
Question
How can I replace all places where System.Net.Sockets
appear in Mono.Cecil, rather than just where the operand is an InlineType
? I don't really want to go through every OperandType
there is in Cecil, I was just looking for some find-and-replace method in Cecil where I wouldn't have to work with plain IL myself.
EDIT: (also unnecessary, confusing, and only for the curious)
This person was able to do something similar for $25: http://www.reddit.com/r/Unity3D/comments/1xq516/good_ol_sockets_net_sockets_for_mobile_without/.
Automatic patcher tool that detects and fixes socket usage in scripts and .dll.
...
"DLL's are patched using Mono.Cecil. ...
You can go look at the second screenshot at https://www.assetstore.unity3d.com/en/#!/content/13166 and see that it says that it can replace namespaces.
That library doesn't fit my needs, because 1) it doesn't rename to the namespace I want (dudeprgm.Net.Sockets
), 2) the library that it is renaming to does not support all the System.Net.Sockets
classes that IKVM needs, because IKVM uses pretty much every Sockets class and 3) it costs $25 and I don't really want to buy something that I'm not going to use. I just wanted to show that replacing namespace/type references in Mono.Cecil is possible.