Извините за подробное введение, следующее. Мне нужно понять, что кто-то знает P/Invoke внутренности лучше, чем я.C# P/Invoke: Структуры маршаллинга, содержащие указатели на функции
Вот как я сортирую структуры, содержащие указатели функций от C до C#. Я хотел бы знать, является ли это самым чистым и/или наиболее эффективным способом этого.
Я взаимодействие с родной DLL, закодированной в C, который содержит следующую точку входа:
void* getInterface(int id);
Вы должны пройти getInterface(int)
одно из следующих значений перечислений:
enum INTERFACES
{
FOO,
BAR
};
который возвращает указатель на структуру, содержащую указатели функций, такие как:
typedef struct IFOO
{
void (*method1)(void* self, int a, float b);
void (*method2)(void* self, int a, float b, int c);
} IFoo;
A ой вот как вы используете его в C:
IFoo* interface = (IFoo*)getInterface(FOO);
interface->method1(obj, 0, 1.0f); // where obj is an instance of an object
// implementing the IFoo interface.
В C# У меня есть Library
класс, который отображает точку входа getInterface(int)
с помощью P/Invoke.
class Library
{
[DllImport("MyDLL"), EntryPoint="getInterface", CallingConvention=CallingConvention.Cdecl)]
public static extern IntPtr GetInterface(int id);
};
Тогда я определил:
struct IFoo
{
public M1 method1;
public M2 method2;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void M1(IntPtr self, int a, float b);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void M2(IntPtr self, int a, float b, int c);
}
И я использую это так:
IntPtr address = Library.GetInterface((int)Interfaces.FOO);
IFoo i = (IFoo)Marshal.PtrToStructure(address, typeof(IFoo));
i.method1(obj, 0, 1.0f): // where obj is an instance of an object
// implementing the IFoo interface.
У меня есть следующие вопросы:
Is КАРТОГРАФИРОВАНИЯ целая структура менее эффективна, чем отображение одного поля внутри структуры, используя
Marshal.GetDelegateForFunctionPointer()
?Так как я в основном не нужны все методы, предоставляемые интерфейсом, я могу сделать (проверено и работает):
unsafe { IntPtr address = Library.GetInterface(id); IntPtr m2address = new IntPtr(((void**)address.toPointer())[1]); M2 method2 = (M2)Marshal.GetDelegateForFunctionPointer(m2address, typeof(M2)); method2(obj, 0, 1.0f, 1); }
При отображении всей структуры сразу с помощью
Marshal.PtrToStructure()
, есть меньше подробный путь, чем то, что я описал? Я имею в виду меньше подробностей, чем определение типов делегатов для всех методов и т. Д.?
EDIT: Ради ясности и полноты, в фрагментов кода выше, obj
представляет собой экземпляр, полученный с точкой входа void* createObject(int type)
.
EDIT2: Одним из преимуществ метода 1) является то, что Marshal.GetDelegateForFunctionPointer()
доступна только начиная с .NET Framework 2.0. Однако Marshal.PrtToStructure()
всегда был доступен. Тем не менее, я не уверен, что сегодня стоит обеспечить совместимость 1.0.
EDIT3: Я попытался проверить сгенерированный код, используя Reflector, но это не дает много информации, так как все интересные детали сделаны в вспомогательных функциях, как PtrToStructureHelper
и не подвергаются. Затем, даже если бы я мог видеть, что сделано в рамках внутренних структур, тогда время выполнения имеет возможность оптимизировать ситуацию, и я точно не знаю, что и почему :)
Однако я сравнил два описанных подхода в моем вопросе. Подход Marshal.PtrToStructure()
был медленнее примерно на 10% по сравнению с подходом Marshal.GetDelegateForFunctionPointer()
; что структура, содержащая IntPtr
s для всех функций, которые не представляют интереса.
Я также сравнил Marshal.GetDelegateForFunctionPointer()
с моим собственным прокатываемым ИАС: Я выравнивать struct
, представляющий стек вызовов, пин-код его в памяти, передать свой адрес на родную сторону, где я использую батут, закодированный в ассемблере, так что функция вызова использует область памяти как ее стек параметров (это возможно, поскольку соглашение о вызове cdecl
x86 передает все функциональные параметры в стеке). Сроки были эквивалентными.
Около 1) Я не знаю, можно ли проверить, что действительно сделано в конце: как оптимизатор может понять, что используется только 1 делегат структуры. Во всяком случае, по крайней мере, он выглядит достаточно чистым. –
@Gregory Я думаю, что это маловероятно. Но вы можете использовать отражатель .NET, чтобы декомпилировать код и узнать наверняка. –