Ci scusiamo per l'introduzione dettagliata che segue. Ho bisogno di informazioni da qualcuno che conosce gli interni di P/Invoke meglio di me.C# P/Invoke: strutture di marshalling contenenti puntatori di funzioni
Ecco come sono le strutture di marshalling contenenti i puntatori di funzione da C a C#. Vorrei sapere se è il modo più pulito e/o più efficiente di farlo.
sto interfacciamento con una DLL nativa scritto in C che fornisce il seguente punto di ingresso:
void* getInterface(int id);
Devi passare getInterface(int)
uno dei seguenti valori enum:
enum INTERFACES
{
FOO,
BAR
};
che restituisce un puntatore a una struttura contenente i puntatori a funzioni come:
typedef struct IFOO
{
void (*method1)(void* self, int a, float b);
void (*method2)(void* self, int a, float b, int c);
} IFoo;
A ND ecco come lo si utilizza in C:
IFoo* interface = (IFoo*)getInterface(FOO);
interface->method1(obj, 0, 1.0f); // where obj is an instance of an object
// implementing the IFoo interface.
In C# ho una classe Library
che mappa il punto getInterface(int)
immissione con P/Invoke.
class Library
{
[DllImport("MyDLL"), EntryPoint="getInterface", CallingConvention=CallingConvention.Cdecl)]
public static extern IntPtr GetInterface(int id);
};
Poi ho definito:
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);
}
e sto usando in questo modo:
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.
Ho le seguenti domande:
è la mappatura del intera struttura meno efficiente rispetto alla mappatura di un singolo po inter all'interno della struttura usando
Marshal.GetDelegateForFunctionPointer()
?Dal momento che la maggior parte non hanno bisogno di tutti i metodi esposti da un'interfaccia, che posso fare (testato e funziona):
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); }
Quando la mappatura dell'intera struttura contemporaneamente utilizzando
Marshal.PtrToStructure()
, c'è un meno modo verboso di quello che ho descritto? Voglio dire meno verboso di dover definire i tipi di delegati per ogni metodo, ecc?
EDIT: Per ragioni di chiarezza e completezza, nei frammenti di codice di cui sopra, obj
è un'istanza ottenuto con il punto void* createObject(int type)
voce.
EDIT2: Uno dei vantaggi di metodo 1) è che Marshal.GetDelegateForFunctionPointer()
è disponibile solo a partire dal .NET Framework 2.0. Tuttavia, Marshal.PrtToStructure()
è sempre stato disponibile. Detto questo, non sono sicuro che valga la pena di garantire la compatibilità 1.0 al giorno d'oggi.
Edit3: ho cercato di ispezionare il codice generato usando Reflector ma non dà molte informazioni dal momento che tutti i dettagli interessanti sono fatte in funzioni di supporto come PtrToStructureHelper
e non sono esposti. Quindi, anche se potessi vedere che cosa è fatto all'interno del framework, allora il runtime ha l'opportunità di ottimizzare le cose e non so esattamente cosa, perché e quando :)
Tuttavia, ho confrontato i due approcci descritti nella mia domanda L'approccio Marshal.PtrToStructure()
è stato più lento di un fattore di circa il 10% rispetto all'approccio Marshal.GetDelegateForFunctionPointer()
; quello con una struttura contenente IntPtr
s per tutte le funzioni che non sono di interesse.
Ho anche confrontato il Marshal.GetDelegateForFunctionPointer()
con la mia marshaller laminati: Allineo un struct
rappresenta lo stack di chiamate, perno nella memoria, passare il suo indirizzo al lato nativo cui uso un trampolino codificato in asm modo che gli usi funzione chiamata l'area di memoria come stack di parametri (ciò è possibile poiché la convenzione di chiamata xpassa tutti i parametri di funzione nello stack). I tempi erano equivalenti.
Informazioni su 1) Non so davvero se sia possibile verificare cosa è veramente fatto alla fine: come l'ottimizzatore può capire che viene utilizzato solo 1 delegato della struttura. Comunque almeno sembra abbastanza pulito. –
@ Gregory Penso che sia improbabile. Ma potresti usare .NET Reflector per decompilare il codice e scoprirlo con certezza. –