【问题标题】:What is the (fnptr)* type and how to create it?什么是 (fnptr)* 类型以及如何创建它?
【发布时间】:2015-01-27 12:24:28
【问题描述】:

以下 IL 代码创建一个名为 (fnptr)* 的 Type 实例(令牌 0x2000000 - 无效,模块 mscorlib.dll)。

ldtoken method void* ()*
call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)

这种类型的目的是什么?是否可以在不编写任何 IL 代码的情况下在 C# 中创建这种类型实例,也许使用反射?令牌上的 Module.ResolveType 抛出 ArgumentOutOfRangeException

编辑:

很明显,(fnptr) 类型是 IL 方法指针类型的内部 CLR 类型表示,尽管在删除最后一个 * 时,它只返回 IntPtr

编辑#2:

(fnptr) 来自一个可以在SSCLI 中看到的函数typestring.cpp

// ...or function pointer
else if (ty.IsFnPtrType())
{
    // Don't attempt to format this currently, it may trigger GC due to fixups.
    tnb.AddName(L"(fnptr)");
}

为什么基本的 fnptr 返回 IntPtr 可以在 typehandle.cpp 中看到:

OBJECTREF TypeHandle::GetManagedClassObject() const
{

[...]

        switch(GetInternalCorElementType()) {
        case ELEMENT_TYPE_ARRAY:
        case ELEMENT_TYPE_SZARRAY:
        case ELEMENT_TYPE_BYREF:
        case ELEMENT_TYPE_PTR:
            return ((ParamTypeDesc*)AsTypeDesc())->GetManagedClassObject();

        case ELEMENT_TYPE_VAR:
        case ELEMENT_TYPE_MVAR:
            return ((TypeVarTypeDesc*)AsTypeDesc())->GetManagedClassObject();

            // for this release a function pointer is mapped into an IntPtr. This result in a loss of information. Fix next release
        case ELEMENT_TYPE_FNPTR:
            return TheIntPtrClass()->GetManagedClassObject();

        default:
        _ASSERTE(!"Bad Element Type");
        return NULL;
        }
    }
}

所以看起来他们忘记修复它了。

【问题讨论】:

  • 有趣。你是怎么知道的?从 C++/CLI 生成的代码?
  • @leppie 是的,我只是想看看 ildasm 中的 C++/CLI 程序集,并在那里看到了 *()*。这引起了我的注意,所以我创建了上面的代码。

标签: c# .net reflection clr cil


【解决方案1】:

可以将指针的签名加载到函数指针:

public static unsafe Type GetTypeFromFieldSignature(byte[] signature, Type declaringType = null)
{
    declaringType = declaringType ?? typeof(object);
    Type sigtype = typeof(Type).Module.GetType("System.Signature");
    Type rtype = typeof(Type).Module.GetType("System.RuntimeType");
    var ctor = sigtype.GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, new[]{typeof(void*), typeof(int), rtype}, null);
    fixed(byte* ptr = signature)
    {
        object sigobj = ctor.Invoke(new object[]{(IntPtr)ptr, signature.Length, declaringType});
        return (Type)sigtype.InvokeMember("FieldType", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetProperty, null, sigobj, null);
    }
}

var fnptrPtr = GetTypeFromFieldSignature(new byte[]{6, 15, 27, 0, 0, 1});

6 是字段,15 是指针,27 是函数指针,0,0,1 是没有返回或参数的方法签名。

【讨论】:

    【解决方案2】:

    我不知道你在问什么,也不知道你为什么认为有什么问题。 (fnptr)* 是指向非托管函数指针的指针的类型名称。它在 CLR 中得到特殊处理确实很奇怪,我怀疑它是一件考古文物。可以追溯到 .NET 和委托发明之前的时间。 CLR 最初是 Project 42 中的“通用运行时”,这是 .NET 之前的一个失败项目。

    也许一些用于演示如何生成的 C++/CLI 代码很有用,向您展示如何创建它:

    #include "stdafx.h"
    
    using namespace System;
    
    typedef void (*functionPointer)(int);
    
    ref class Example {
    public:
        functionPointer* fp;
    };
    
    int main(array<System::String ^> ^args)
    {
        auto field = Example::typeid->GetField("fp");
        auto name = field->FieldType->FullName; 
        Console::WriteLine(name);
        return 0;
    }
    

    输出:(fnptr)*

    【讨论】:

    • 谢谢,在 C++/CLI 中是可能的,但是在纯 C# 中可以吗?
    • 不,你不能在 C# 中声明非托管函数指针,它只支持委托。很难猜到你为什么会问这个顺便说一句,我完全不知道你的意图是什么。
    • 我的意思是我只想获取 Type 实例。以及原因?好奇心。 ☺
    【解决方案3】:

    不确定您在哪里看到声明的 FNPTR。

    对于此代码:

    .assembly extern mscorlib {}
    
    .assembly Test
    {
        .ver 1:0:1:0
    }
    .module test.exe
    
    .method static void main() cil managed
    {
        .maxstack 1
        .entrypoint
    
        ldtoken method void* ()*
        call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
    
        ldtoken method void* ()
        call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
    
        ret
    }
    

    ILASM (4.5.22.0) 输出以下内容:

    .method privatescope static void  main$PST06000001() cil managed
    {
      .entrypoint
      // Code size       21 (0x15)
      .maxstack  1
      IL_0000:  ldtoken    method void *()*
      IL_0005:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
      IL_000a:  ldtoken    method void *()
      IL_000f:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
      IL_0014:  ret
    } // end of method 'Global Functions'::main
    

    更新 #1:

    也许我在这里很密集,但我没有看到从这段代码生成 FNPTR:

    typeof(StringBuilder).ToString();
    

    IL 看起来像这样:

    IL_0000:  nop
    IL_0001:  ldtoken    [mscorlib]System.Text.StringBuilder
    IL_0006:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
    IL_000b:  callvirt   instance string [mscorlib]System.Object::ToString()
    IL_0010:  pop
    IL_0011:  ret
    

    Type.ToString() 调用是自 ToString() is a virtual method 以来的 callvirt 操作。

    虚拟函数通常表现为函数指针的结构,我猜这会导致发出 FNPTR。如果您在 ()* 中省略 * 导致 (),那么您现在描述的是函数,而不是函数指针。

    当您看到 FNPTR 时,您使用的是什么版本的 .NET?你用什么来提取 IL?

    【讨论】:

    • CLR 从 Type.ToString 输出 (fnptr)*
    • 再一次,如果你编译我在问题中发布的代码,它会返回一个 Type,它被称为 ToString,返回 (fnptr)*
    • 我确实编译了您的代码 - 请参阅我的第一个示例和结果输出。你可以发布一个复制品吗?
    • 你会看到我的第一个和第二个答案。我想知道你在哪里找到StringBuilder
    猜你喜欢
    • 2020-12-12
    • 2014-11-15
    • 1970-01-01
    • 2016-07-13
    • 1970-01-01
    • 2021-10-25
    • 1970-01-01
    • 1970-01-01
    • 2010-12-10
    相关资源
    最近更新 更多