というのをやってみたら、動いちゃったので、共有です。
サンプル
using UnityEngine;
using System;
using System.Collections;
using System.Reflection;
public class ReplaceMethod1 : MonoBehaviour
{
static void ExchangeFunctionPointer(MethodInfo method0, MethodInfo method1)
{
unsafe
{
var functionPointer0 = method0.MethodHandle.Value.ToPointer();
var functionPointer1 = method1.MethodHandle.Value.ToPointer();
var tmpPointer = *((int*)new IntPtr(((int*)functionPointer0 + 1)).ToPointer());
*((int*)new IntPtr(((int*)functionPointer0 + 1)).ToPointer()) = *((int*)new IntPtr(((int*)functionPointer1 + 1)).ToPointer());
*((int*)new IntPtr(((int*)functionPointer1 + 1)).ToPointer()) = tmpPointer;
}
}
void Awake()
{
MethodInfo onGuiMethod = this.GetType().GetMethod("OnGUI", BindingFlags.NonPublic | BindingFlags.Instance);
MethodInfo onGuiV2Method = this.GetType().GetMethod("OnGUIv2", BindingFlags.NonPublic | BindingFlags.Instance);
ExchangeFunctionPointer(onGuiMethod, onGuiV2Method);
}
void OnGUI()
{
if (GUILayout.Button("This is OnGUIv1"))
{
}
}
void OnGUIv2()
{
if (GUILayout.Button("This is OnGUIv2"))
{
}
}
}
実行結果
OnGUIの代わりに、OnGUIv2が呼び出されていますね。
てきとーな解説
やっている事は、
- AwakeでリフレクションでOnGUI,OnGUIv2メソッドを取得して、ExchangeFunctionPointerメソッドに二つを投げ込む
- ExchangeFunctionPointerメソッド内では、てきとーに実際の処理の関数ポインタを付け替え
- おわり
ポインタをごにょる関係上、unsafeだったりします。
この例では、二つのメソッドの処理を入れ替えていますが、これは絶対ではなく、ポインタを共有する事もできます。
iOS実機でも動いたのが、わりと衝撃でした。
ただし、一度でも実際に呼び出したメソッドは、変更不能
これが致命傷...
using UnityEngine;
using System;
using System.Collections;
using System.Reflection;
public class ReplaceMethod1 : MonoBehaviour
{
static void ExchangeFunctionPointer(MethodInfo method0, MethodInfo method1)
{
unsafe
{
var functionPointer0 = method0.MethodHandle.Value.ToPointer();
var functionPointer1 = method1.MethodHandle.Value.ToPointer();
var tmpPointer = *((int*)new IntPtr(((int*)functionPointer0 + 1)).ToPointer());
*((int*)new IntPtr(((int*)functionPointer0 + 1)).ToPointer()) = *((int*)new IntPtr(((int*)functionPointer1 + 1)).ToPointer());
*((int*)new IntPtr(((int*)functionPointer1 + 1)).ToPointer()) = tmpPointer;
}
}
void OnGUI()
{
if (GUILayout.Button("This is OnGUIv1"))
{
MethodInfo onGuiMethod = this.GetType().GetMethod("OnGUI", BindingFlags.NonPublic | BindingFlags.Instance);
MethodInfo onGuiV2Method = this.GetType().GetMethod("OnGUIv2", BindingFlags.NonPublic | BindingFlags.Instance);
ExchangeFunctionPointer(onGuiMethod, onGuiV2Method); }
}
void OnGUIv2()
{
if (GUILayout.Button("This is OnGUIv2"))
{
MethodInfo onGuiMethod = this.GetType().GetMethod("OnGUI", BindingFlags.NonPublic | BindingFlags.Instance);
MethodInfo onGuiV2Method = this.GetType().GetMethod("OnGUIv2", BindingFlags.NonPublic | BindingFlags.Instance);
ExchangeFunctionPointer(onGuiMethod, onGuiV2Method); }
}
}
本来は、こうしたかったんですが、どうにも、一度ロードされたメソッドは、後からポインタを付け替えても意味がないようです?
Debug.LogをApplication.RegisterLogCallback以外の方法でフックする用途など
#if UNITY_EDITOR
using UnityEditor;
using System;
using System.Collections;
using System.Reflection;
[InitializeOnLoad]
public class ReplaceMethod2
{
static ReplaceMethod2()
{
MethodInfo logMethod = typeof(Debug).GetMethod("Log", new System.Type[] { typeof(object) });
MethodInfo logWarningMethod = typeof(Debug).GetMethod("LogWarning", new System.Type[] { typeof(object) });
ExchangeFunctionPointer(logMethod, logWarningMethod);
Debug.LogWarning("this is Warning");
Debug.Log("this is Log");
}
static void ExchangeFunctionPointer(MethodInfo method0, MethodInfo method1)
{
unsafe
{
var functionPointer0 = method0.MethodHandle.Value.ToPointer();
var functionPointer1 = method1.MethodHandle.Value.ToPointer();
var tmpPointer = *((int*)new IntPtr(((int*)functionPointer0 + 1)).ToPointer());
*((int*)new IntPtr(((int*)functionPointer0 + 1)).ToPointer()) = *((int*)new IntPtr(((int*)functionPointer1 + 1)).ToPointer());
*((int*)new IntPtr(((int*)functionPointer1 + 1)).ToPointer()) = tmpPointer;
}
}
}
#endif