Non c'è alcun problema nel condividere la memoria tra C# e C++ usando P/Invoke una volta sapere come funziona Suggerirei di leggere sul marshalling in MSDN. Si potrebbe anche voler leggere sull'utilizzo della parola chiave non sicura e sulla risoluzione della memoria.
Ecco un esempio che assume che le variabili possono essere descritti come una semplice struct:
In C++ dichiarare la funzione come segue:
#pragma pack(1)
typedef struct VARIABLES
{
/*
Use simple variables, avoid pointers
If you need to use arrays use fixed size ones
*/
}variables_t;
#pragma pack()
extern "C"
{
__declspec(dllexport) int function1(void * variables)
{
// some work depending on random and on the "variables"
}
}
In C# fare qualcosa di simile:
[StructLayout(LayoutKind.Sequential, Pack=1)]
struct variables_t
{
/*
Place the exact same definition as in C++
remember that long long in c++ is long in c#
use MarshalAs for fixed size arrays
*/
};
[DllExport("YourDll.dll", CallingConvention=CallingConvention.Cdecl)]
static extern int function(ref variables_t variables);
E nella tua classe:
variables_t variables = new variables_t();
//Initialize variables here
for(int i=0; i<many_times; i++)
{
int[] result = new int[number_of_parallel_tasks];
Parallel.For(0, number_of_parallel_tasks, delegate(int j)
{
result[j] = function1(ref variables)
});
// choose best result
// then update "variables"
}
È possibile utilizzare scenari più complessi, ad esempio allocare e rilasciare la struttura in C++, utilizzando altre forme di marshalling per ripristinare i dati come se si stesse costruendo la propria classe per leggere e scrivere direttamente nella memoria non gestita. Ma se puoi usare una semplice struttura per contenere le tue variabili, il metodo sopra è il più semplice.
EDIT: indicazioni su come gestire i dati in modo corretto più complessi
Così il campione di cui sopra è a mio parere il modo corretto di dati "condivisione" tra C# e C++, se si tratta di semplici dati ad es. una struttura che contiene tipi primitivi o matrici di dimensioni fisse di tipi primitivi.
Detto questo ci sono in realtà pochissime limitazioni sul modo in cui è possibile accedere alla memoria tramite C#. Per ulteriori informazioni, esaminare la parola chiave non sicura, la parola chiave fissa e la struttura GCHandle. Eppure se hai una struttura di dati molto complessa che contiene array di altre strutture ecc., Allora hai un lavoro più complicato.
Nel caso sopra suggerirei di spostare la logica su come aggiornare le "variabili" in C++. Aggiungere in una funzione C++ a cercare qualcosa di simile:
extern "C"
{
__declspec(dllexport) void updateVariables(int bestResult)
{
// update the variables
}
}
Vorrei ancora suggerire di non utilizzare le variabili globali in modo propongo il seguente schema. In C++:
typedef struct MYVERYCOMPLEXDATA
{
/*
Some very complex data structure
*/
}variables_t;
extern "C"
{
__declspec(dllexport) variables_t * AllocVariables()
{
// Alloc the variables;
}
__declspec(dllexport) void ReleaseVariables(variables_t * variables)
{
// Free the variables;
}
__declspec(dllexport) int function1(variables_t const * variables)
{
// Do some work depending on variables;
}
__declspec(dllexport) void updateVariables(variables_t * variables, int bestResult)
{
// update the variables
}
};
In C#:
[DllExport("YourDll.dll", CallingConvention=CallingConvention.Cdecl)]
static extern IntPtr AllocVariables();
[DllExport("YourDll.dll", CallingConvention=CallingConvention.Cdecl)]
static extern void ReleaseVariables(IntPtr variables);
[DllExport("YourDll.dll", CallingConvention=CallingConvention.Cdecl)]
static extern int function1(IntPtr variables);
[DllExport("YourDll.dll", CallingConvention=CallingConvention.Cdecl)]
static extern void updateVariables(IntPtr variables, int bestResult);
Se si vuole ancora mantenere voi logica in C# si dovrà fare qualcosa di simile al seguente: Creare una classe per tenere la memoria restituita da C++ e scrivi la tua logica di accesso alla memoria. Esporre i dati a C# usando la semantica della copia. Quello che voglio dire è il seguente, Diciamo che avete in C++ una struttura come questa:
#pragma pack(1)
typedef struct SUBSTRUCT
{
int subInt;
double subDouble;
}subvar_t;
typedef struct COMPLEXDATA
{
int int0;
double double0;
int subdata_length;
subvar_t * subdata;
}variables_t;
#pragma pack()
in C# si fa qualcosa di simile
[DllImport("kernel32.dll")]
static extern void CopyMemory(IntPtr dst, IntPtr src, uint size);
[StructLayout((LayoutKind.Sequential, Pack=1)]
struct variable_t
{
public int int0;
public double double0;
public int subdata_length;
private IntPtr subdata;
public SubData[] subdata
{
get
{
SubData[] ret = new SubData[subdata_length];
GCHandle gcH = GCHandle.Alloc(ret, GCHandleType.Pinned);
CopyMemory(gcH.AddrOfPinnedObject(), subdata, (uint)Marshal.SizeOf(typeof(SubData))*subdata_length);
gcH.Free();
return ret;
}
set
{
if(value == null || value.Length == 0)
{
subdata_length = 0;
subdata = IntPtr.Zero;
}else
{
GCHandle gcH = GCHandle.Alloc(value, GCHandleType.Pinned);
subdata_length = value.Length;
if(subdata != IntPtr.Zero)
Marshal.FreeHGlobal(subdata);
subdata = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SubData))*subdata_length);
CopyMemory(subdata, gcH.AddrOfPinnedObject(),(uint)Marshal.SizeOf(typeof(SubData))*subdata_length);
gcH.Free();
}
}
}
};
[StructLayout((LayoutKind.Sequential, Pack=1)]
sturct SubData
{
public int subInt;
public double subDouble;
};
Nell'esempio sopra la struttura può ancora essere passato come nel primo campione. Questo, naturalmente, è solo uno schema su come gestire dati complessi con araay di strutture e array di strutture all'interno di matrici di strutture. Come puoi vedere, avrai bisogno di molte copie per proteggerti dalla corruzione della memoria. Inoltre, se la memoria viene allocata tramite C++, sarà molto brutto se si utilizza FreeHGlobal per liberarlo. Se vuoi evitare di copiare la memoria e mantenere la logica all'interno di C#, puoi scrivere un wrapper di memoria nativo con accessor per qualsiasi cosa desideri. Ad esempio, avrai un metodo per impostare direttamente o ottenere il subInt del membro dell'array Nth - This in questo modo manterrai le tue copie esattamente a ciò che accederai.
Un'altra opzione potrebbe essere quella di scrivere specifiche funzioni C++ per gestire i dati difficili per te e chiamarli da C# in base alla tua logica.
E, ultimo ma non meno importante, è sempre possibile utilizzare C++ con l'interfaccia CLI. Comunque io stesso lo faccio solo se devo - non mi piace il gergo ma per dati molto complessi devi certamente considerarlo.
EDIT
ho aggiunto la convenzione di chiamata corretto al DllImport per completezza. Si noti che la convenzione di chiamata predefinita utilizzata dall'attributo DllImport è Winapi (che in Windows si traduce in __stdcall) mentre la convenzione di chiamata predefinita in C/C++ (a meno che non si cambino le opzioni del compilatore) è __cdecl.
Ho upvoted la tua domanda ma dovresti davvero specificare la natura delle "variabili" per ottenere una buona risposta –
Ho appena modificato. Grazie. – 888