Skip to content

Instantly share code, notes, and snippets.

@decay88
Forked from harujoh/GetFunctionPointer.cs
Created March 9, 2020 14:46
Show Gist options
  • Save decay88/64ebb8ada827269e2e0db6582597959c to your computer and use it in GitHub Desktop.
Save decay88/64ebb8ada827269e2e0db6582597959c to your computer and use it in GitHub Desktop.
関数ポインタと呼ばれし者たちの墓
using System;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace ConsoleApp60
{
class Program
{
[DllImport(@"Python37.dll", EntryPoint = "Py_Initialize", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern void Initialize();
[DllImport("kernel32", CharSet = CharSet.Ansi, BestFitMapping = false, SetLastError = true)]
public static extern IntPtr LoadLibrary(string fileName);
[DllImport("kernel32", CharSet = CharSet.Ansi)]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procname);
[DllImport(@"Python3.dll", EntryPoint = "PyLong_FromLong", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern IntPtr PyLongFromLong(long v);
[DllImport(@"Python3.dll", EntryPoint = "PyLong_Type", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern IntPtr PyLong_Type();
[DllImport(@"Python3.dll", EntryPoint = "PyObject_IsInstance", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern int IsInstance(IntPtr inst, IntPtr cls);
[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public delegate IntPtr TypeDelegate();
static void Main(string[] args)
{
if (File.Exists(@"C:\Program Files\Python37\Python3.dll"))
{
Initialize();
var lib = LoadLibrary(@"C:\Program Files\Python37\Python3.dll");
var loadLibPtr = GetProcAddress(lib, "PyLong_Type"); // Marshal.GetDelegateForFunctionPointer 使用可
var mi = GenerateInvoke();
IntPtr propertyPtr = mi.MethodHandle.Value; // Marshal.GetDelegateForFunctionPointer不可
IntPtr unsafePtr = GetMethodAddress(mi); // Marshal.GetDelegateForFunctionPointer不可
IntPtr myInvokePtr = GetFuncPtr(mi); //Marshal.GetDelegateForFunctionPointer 使用可
IntPtr pinvokePtr = GetFuncPtr(PyLong_Type); //delegateParamPtrと同値 Marshal.GetDelegateForFunctionPointer 使用可
IntPtr delegateParamPtr = GetDelegateParam((Func<IntPtr>)PyLong_Type); //pinvokeと同値 Marshal.GetDelegateForFunctionPointer 使用可
TypeDelegate typeDelegate = PyLong_Type;
IntPtr marshalPtr = Marshal.GetFunctionPointerForDelegate(typeDelegate); //Marshal.GetDelegateForFunctionPointer 使用可
//IntPtr marshalPtr = Marshal.GetFunctionPointerForDelegate((Func<IntPtr>)PyLong_Type); //直接は不可能
IntPtr pyLong = PyLongFromLong(10);
IsInstance(pyLong, loadLibPtr); //OK
//IsInstance(pyLong, propertyPtr); // 場所 0x00000000000000A8 の読み取り中にアクセス違反が発生しました
//IsInstance(pyLong, unsafePtr); // 場所 0x00000000000000A8 の読み取り中にアクセス違反が発生しました
//IsInstance(pyLong, myInvokePtr); //場所 0x000011B0E94000A8 の読み取り中にアクセス違反が発生しました(unsafeの実行をなくすと0xFFFFFFFFFFFFFFFF)
//IsInstance(pyLong, pinvokePtr); // 場所 0xFFFFFFFFFFFFFFFF の読み取り中にアクセス違反が発生しました
//IsInstance(pyLong, delegateParamPtr); // 場所 0xFFFFFFFFFFFFFFFF の読み取り中にアクセス違反が発生しました
//IsInstance(pyLong, marshalPtr); // 場所 0xFFFFFFFFFFFFFFFF の読み取り中にアクセス違反が発生しました
}
}
public static MethodInfo GenerateInvoke()
{
AssemblyName assemblyName = new AssemblyName("InvokeTest");
AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name);
TypeBuilder typeBld = moduleBuilder.DefineType(
"Test.PInvoke",
TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit
);
MethodBuilder methodBuilder = typeBld.DefinePInvokeMethod(
"PyLong_Type", @"C:\Program Files\Python37\Python3.dll",
MethodAttributes.Static | MethodAttributes.HideBySig | MethodAttributes.PinvokeImpl | MethodAttributes.Public,
CallingConventions.Standard,
null,
new[] { typeof(long) },
CallingConvention.Cdecl,
CharSet.Ansi
);
methodBuilder.SetImplementationFlags(
methodBuilder.GetMethodImplementationFlags() | MethodImplAttributes.PreserveSig
);
Type type = typeBld.CreateType();
return type.GetMethod("PyLong_Type");
}
static IntPtr GetFuncPtr(MethodInfo methodInfo)
{
DynamicMethod dm = new DynamicMethod("", MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, typeof(IntPtr), new Type[] { }, typeof(Delegate), true);
ILGenerator ilgen = dm.GetILGenerator();
ilgen.Emit(OpCodes.Ldftn, methodInfo);
ilgen.Emit(OpCodes.Ret);
return ((Func<IntPtr>)dm.CreateDelegate(typeof(Func<IntPtr>)))();
}
static IntPtr GetFuncPtr(Func<IntPtr> func)
{
return GetFuncPtr(func.GetMethodInfo());
}
static IntPtr GetDelegateParam(Delegate input)
{
DynamicMethod method = new DynamicMethod("", MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, typeof(IntPtr), new Type[] { typeof(Delegate) }, typeof(Delegate), true);
ILGenerator ilgen = method.GetILGenerator();
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Ldfld, typeof(Delegate).GetField("_methodPtrAux", BindingFlags.NonPublic | BindingFlags.Instance));
ilgen.Emit(OpCodes.Ret);
return ((Func<Delegate, IntPtr>)method.CreateDelegate(typeof(Func<Delegate, IntPtr>)))(input);
}
public static IntPtr GetMethodAddress(MethodBase method)
{
RuntimeHelpers.PrepareMethod(method.MethodHandle);
unsafe
{
int skip = 10;
UInt64* location = (UInt64*)(method.MethodHandle.Value.ToPointer());
int index = (int)(((*location) >> 32) & 0xFF);
if (IntPtr.Size == 8)
{
ulong* classStart = (ulong*)method.DeclaringType.TypeHandle.Value.ToPointer();
ulong* address = classStart + index + skip;
return new IntPtr(address);
}
else
{
uint* classStart = (uint*)method.DeclaringType.TypeHandle.Value.ToPointer();
uint* address = classStart + index + skip;
return new IntPtr(address);
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment