Что такое тип (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;
}
}
}
Итак, похоже, они забыли исправить это.
Ответы
Ответ 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?