.net 什么是(fnptr)* 类型以及如何创建它?

dsekswqp  于 2022-12-05  发布在  .NET
关注(0)|答案(4)|浏览(113)

下面的IL代码创建一个名为(fnptr)*的Type示例(标记0x 2000000-无效,模块mscorlib.dll)。

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

此类型的用途是什么?是否可以在C#中创建此类型示例而无需编写任何IL代码,或者使用反射?* 标记上的Module.ResolveType* 引发 ArgumentOutOfRangeException
编辑:
很明显,(fnptr)类型是IL方法指针类型的内部CLR类型表示,尽管在删除最后一个*时,它只返回IntPtr
编辑2:
(fnptr)来自SSCLItypestring.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;
        }
    }
}

看来他们忘了修了。

xqkwcwgp

xqkwcwgp1#

我真实的的不知道你在问什么,为什么你认为有什么不对。(fnptr)* 是指向非托管函数指针的指针的类型名。它在CLR中得到特殊处理确实很奇怪,我怀疑它是考古学上的产物。可以追溯到.NET和委托发明之前的时间。CLR在Project 42中作为“通用运行时”开始存在,一个失败的项目。
也许一些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)*

unftdfkk

unftdfkk2#

不知道你在哪里看到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?

hsgswve4

hsgswve43#

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

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是没有返回或参数的方法签名。

ckx4rj1h

ckx4rj1h4#

在C# 9的上下文中应答函数指针现在可以通过delegate*语法直接表示和使用。但是,运行时(.NET 7)仍然有7年前的限制:

Console.WriteLine(typeof(delegate*<void>)); // IntPtr
Console.WriteLine(typeof(delegate*<void>*)); // (fnptr)*

相关问题