Извините за подробное введение, которое следует. Мне нужно понять, кто-то знает P/Invoke внутренности лучше, чем я.
Вот как я сортирую структуры, содержащие указатели на функции от 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;
И вот как вы используете его в C:
IFoo* interface = (IFoo*)getInterface(FOO);
interface->method1(obj, 0, 1.0f); // where obj is an instance of an object
// implementing the IFoo interface.
В С# у меня есть класс 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.
У меня есть следующие вопросы:
-
Является ли отображение всей структуры менее эффективным, чем отображение одного указателя внутри структуры с помощью
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
для всех функций, которые не представляют интереса.
Я также сравнил Marshal.GetDelegateForFunctionPointer()
с моим собственным прокатанным маршаллером: я выровняю struct
, представляющий стек вызовов, запишу его в память, передаю его адрес на родную сторону, где я использую батут, закодированный в asm, так что функция вызова использует область памяти как ее стек параметров (это возможно, поскольку соглашение о вызове cdecl
x86 передает все функциональные параметры в стеке). Сроки были эквивалентными.