llenar - La forma más simple de mover una matriz de C++ a C#, modificarla y pasarla de nuevo a C++
llenar un arreglo en c# (3)
Tengo una biblioteca de clase C # que contiene métodos que deben usarse con una aplicación externa. Lamentablemente, esta aplicación externa solo admite API externas en C / C ++.
Ahora he logrado obtener un ejemplo COM muy simple trabajando entre un dll de C ++ y una DLL de C #, pero estoy atascado en cómo puedo mover los datos de la matriz.
Esto es lo que tengo hasta ahora, solo como un pequeño ejemplo que encontré en la web de comunicación a través de COM:
DLL_EXPORT(void) runAddTest(int add1,long *result) {
// Initialize COM.
HRESULT hr = CoInitialize(NULL);
// Create the interface pointer.
IUnitModelPtr pIUnit(__uuidof(UnitModel));
long lResult = 0;
// Call the Add method.
pIUnit->Add(5, 10, &lResult);
*result = lResult;
// Uninitialize COM.
CoUninitialize();
}
Esto funciona bien para llamar al método add en mi clase C #. ¿Cómo puedo modificar esto para tomar y devolver una matriz de dobles? (También necesitaré hacerlo con cuerdas en la línea).
Necesito tomar una matriz no administrada, pasar esta matriz a una clase C # para algunos cálculos, y luego devolver los resultados a la referencia de matriz especificada en la llamada a función original (no administrada) C ++.
Tendré que exponer una función como esta:
* calcin - referencia a una matriz de dobles
* calcOut: referencia a la matriz de dobles
numIN - valor del tamaño de la matriz de entrada
DLL_EXPORT(void) doCalc(double *calcIn, int numIn, double *calcOut)
{
//pass the calcIn array to C# class for the calcuations
//get the values back from my C# class
//put the values from the C# class
//into the array ref specified by the *calcOut reference
}
Creo que puedo usar una DLL C ++ / CLI para la aplicación externa, así que si esto es más fácil que COM directo entonces estaré dispuesto a ver eso.
Por favor, sea amable ya que soy principalmente un desarrollador de C #, pero me han echado en el fondo de Interop y C ++.
Creo que puedo usar una DLL C ++ / CLI para la aplicación externa, así que si esto es más fácil que COM directo entonces estaré dispuesto a ver eso.
Si no tienes mucha experiencia COM (y las matrices no son sencillas en COM), entonces la envoltura C ++ / CLI alrededor de la tercera parte probablemente será más fácil.
También solo incluirá una única etapa de clasificación (administrada por <-> nativo) en lugar del paso adicional, la envoltura COM que se puede llamar necesaria que necesitará para la interfaz administrada <-> COM).
Experimenté con esto hace un tiempo, pero desafortunadamente he olvidado cómo encajaba todo ... para mi propósito, resultó ser terriblemente lento, así que corté el C # y volví a todo C ++. Cuando dices que eres principalmente un desarrollador de C #, espero que entiendas los indicadores, porque si no lo haces no hay forma de ser gentil.
El paso de las matrices básicamente se reducía al uso de la familia de funciones CoTaskMemAlloc en el lado de C ++ ( http://msdn.microsoft.com/en-us/library/ms692727(VS.85).aspx ) y la clase Marshal en el lado C # ( http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal.aspx - que tiene métodos como AllocCoTaskMem). Para C # terminé con una clase de utilidad:
public class serviceUtils
{
unsafe public long stringToCoTaskPtr( ref str thestring )
{
return (long)Marshal.StringToCoTaskMemAnsi(thestring.theString).ToPointer();//TODO : what errors occur from here? handle them
}
unsafe public long bytesToCoTaskPtr( ref bytes thebytes, ref short byteCnt)
{
byteCnt = (short)thebytes.theArray.Length;
IntPtr tmpptr = new IntPtr();
tmpptr = Marshal.AllocCoTaskMem(byteCnt);
Marshal.Copy(thebytes.theArray, 0, tmpptr, byteCnt);
return (long)tmpptr.ToPointer();
}
public void freeCoTaskMemPtr(long ptr)
{
Marshal.FreeCoTaskMem(new IntPtr(ptr));//TODO : errors from here?
}
public string coTaskPtrToString(long theptr)
{
return Marshal.PtrToStringAnsi(new IntPtr(theptr));
}
public byte[] coTaskPtrToBytes(long theptr, short thelen)
{
byte[] tmpbytes = new byte[thelen];
Marshal.Copy(new IntPtr(theptr), tmpbytes, 0, thelen);
return tmpbytes;
}
}
Solo para arrojar algo más de código sobre ti: este c ++
#import "../COMClient/bin/Debug/COMClient.tlb" named_guids raw_interfaces_only
int _tmain(int argc, _TCHAR* argv[])
{
CoInitialize(NULL); //Initialize all COM Components
COMClient::IComCalculatorPtr pCalc;
// CreateInstance parameters
HRESULT hRes = pCalc.CreateInstance(COMClient::CLSID_ComCalculator);
if (hRes == S_OK) {
long size = 5;
LPVOID ptr = CoTaskMemAlloc( size );
if( ptr != NULL )
{
memcpy( ptr, "12345", size );
short ans = 0;
pCalc->changeBytes( (__int64*)&ptr, &size, &ans );
CoTaskMemFree(ptr);
}
}
CoUninitialize (); //DeInitialize all COM Components
return 0;
}
llamado esto c #
public short changeBytes(ref long ptr, ref int arraysize)
{
try
{
IntPtr interopPtr = new IntPtr(ptr);
testservice.ByteArray bytes = new testservice.ByteArray();
byte[] somebytes = new byte[arraysize];
Marshal.Copy(interopPtr, somebytes, 0, arraysize);
bytes.theArray = somebytes;
CalculatorClient client = generateClient();
client.takeArray(ref bytes);
client.Close();
if (arraysize < bytes.theArray.Length)
{
interopPtr = Marshal.ReAllocCoTaskMem(interopPtr, bytes.theArray.Length);//TODO : throws an exception if fails... deal with it
}
Marshal.Copy(bytes.theArray, 0, interopPtr, bytes.theArray.Length);
ptr = interopPtr.ToInt64();
arraysize = bytes.theArray.Length;
//TODO : do we need to free IntPtr? check all code for memory leaks... check for successful allocation
}
catch(Exception e)
{
return 3;
}
return 2;
}
Lo siento, pero no tengo el tiempo para resolver todo esto y explicarlo correctamente, con suerte esto te dará indicaciones en la dirección correcta, al menos algunas cosas para google. Buena suerte
PD: Tengo toda la información para escribir estas cosas de la red, por lo que está allí.
¿Esto también funcionaría?
En C #, 1. Llame a Marshal.PtrToStructure 2. Modifique el valor 3. Llame a Marshal.StructureToPtr