2009-02-22 22 views
6

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++.Manera más simple de mover una matriz de C++ a C#, modificarla y pasarla a C++

Ahora he logrado obtener un ejemplo COM muy simple trabajando entre un dll C++ y un DLL C#, pero estoy atascado en cómo puedo mover los datos de la matriz.

Esto es lo que tengo hasta ahora, al igual que un pequeño ejemplo que encontré en la red 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++.

Voy a tener que exponer una función como esta:


* calcin - referencia a la serie de dobles

* calcOut - referencia a la serie de dobles

numIN - valor de tamaño de 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 


} 

I think 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.

Sea amable, ya que soy principalmente un desarrollador de C#, pero me han echado en la parte más profunda de Interop y C++.

Respuesta

3

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.

matrices que pasan básicamente bajaron a la utilización de la familia CoTaskMemAlloc de funciones en el lado de la 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 cuenta con métodos como AllocCoTaskMem).Para C# que 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 arrojarlo algo más de código en el que: 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 a este 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 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í.

1

Creo que puedo usar una DLL de C++ \ CLI para la aplicación externa, así que si esto es más fácil que una COM recta entonces estaré dispuesto a ver eso.

Si usted no tiene mucha experiencia COM (y matrices no son significativamente simple en COM) entonces C++/CLI envoltura alrededor de la 3 ª partido probablemente será más fácil.

También solo implicará una sola etapa de clasificación (nativa < -> administrada) en lugar del paso adicional la envoltura COM necesaria obligatoria que necesitará para < gestionado - interfaz COM).

0

¿Esto también funcionaría?

En C#, 1. Llame Marshal.PtrToStructure 2. Modificar el valor 3. Llame Marshal.StructureToPtr

Cuestiones relacionadas