Подтвердить что ты не робот

Что такое тип (fnptr) * и как его создать?

Следующий код IL создает экземпляр типа с именем (fnptr)* (токен 0x2000000 - недействительный, модуль mscorlib.dll).

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

Какая цель этого типа? Возможно ли создать экземпляр этого типа в С# без написания кода IL, возможно, с отражением? Module.ResolveType на токене вызывает исключение ArgumentOutOfRangeException.

Edit:

Очистить тип (fnptr) - это внутреннее представление типа CLR типа указателя метода IL, хотя при удалении последнего * он просто возвращает 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;
        }
    }
}

Итак, похоже, они забыли исправить это.

4b9b3361

Ответ 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 начал свою жизнь как "универсальное время выполнения" в проекте 42, провал проекта до .NET.

Может быть, некоторый код С++/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)*

Ответ 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() - это операция callvirt, поскольку ToString() - это виртуальный метод.

Виртуальные функции обычно проявляются как структуры указателей функций, которые, я думаю, приводят к испусканию FNPTR. Если вы опускаете * в ()*, что приводит к (), вы теперь описываете функцию, а не указатель функции.

Какую версию .NET вы используете, когда видите FNPTR? Что вы используете для извлечения IL?