2009-02-22 4 views
6

Ho una libreria di classi C# che contiene metodi che devono essere utilizzati con un'applicazione esterna. Sfortunatamente questa applicazione esterna supporta solo API esterne in C/C++.Il modo più semplice per spostare un array da C++ a C#, modificarlo e passarlo a C++

Ora sono riuscito a ottenere un esempio COM molto semplice che funziona tra una DLL C++ e una DLL C#, ma sono bloccato su come posso spostare i dati dell'array.

Questo è quello che ho finora, proprio come un piccolo esempio che ho trovato sul web di comunicare tramite 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(); 

} 

Questo funziona bene per chiamare il metodo Add della mia classe C#. Come posso modificare questo per prendere e restituire una serie di doppi? (Devo anche farlo con le corde lungo la linea).

Ho bisogno di prendere un array non gestito, passare questo array a una classe C# per alcuni calcoli, e quindi passarlo indietro ai risultati dell'array specificato nella chiamata di funzione originale (non gestita) C++.

avrò bisogno di esporre una funzione come questa:


* calcin - riferimento alla serie di doppie

* calcOut - riferimento alla serie di doppie

numIN - valore di dimensione della matrice di ingresso

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 penso Posso usare una DLL C++ \ CLI per l'applicazione esterna, quindi se questo è più semplice di COM dritto, allora sarò disposto a dare un'occhiata.

Si prega di essere gentile come io sono principalmente uno sviluppatore C# ma sono stati gettati nella parte profonda di Interop e C++.

risposta

3

Ho sperimentato un po 'di tempo fa, ma purtroppo ho dimenticato come tutto si combinava ... per il mio scopo si è rivelato terribilmente lento, quindi ho tagliato il C# e sono tornato a tutto il C++. Quando dici che sei principalmente uno sviluppatore C#, spero che tu capisca i suggerimenti perché se non lo fai non c'è modo di essere gentile.

array Passando fondamentalmente è venuto giù per utilizzare famiglia CoTaskMemAlloc delle funzioni sul lato C++ (http://msdn.microsoft.com/en-us/library/ms692727(VS.85).aspx) e la classe Marshal sul lato C# (http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal.aspx - che ha metodi come AllocCoTaskMem).Per C# ho finito con una classe di utilità:

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; 
    } 
} 

Giusto per gettare un po 'di più il codice a voi: questo 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; 
} 

chiamato questo 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; 
    } 

dispiace, ma io non ho il tempo di lavorare tutto questo e di spiegarlo correttamente, spero che questo ti fornisca indicazioni nella giusta direzione, almeno alcune cose da fare su google. Good Luck

PS: Ho ricevuto tutte le informazioni per scrivere questa roba dalla rete, quindi è là fuori.

1

Penso di poter utilizzare una DLL C++ \ CLI per l'applicazione esterna, quindi se questo è più semplice di COM dritto, allora sarò disposto a dare un'occhiata.

Se non si ha molta esperienza COM (e gli array non sono significativamente semplice nella COM), allora C++/CLI involucro intorno al 3 ° partito sarà probabilmente più facile.

Coinvolgerà anche un singolo stadio di marshalling (nativo < -> gestito) anziché il passaggio aggiuntivo necessario COM Callable Wrapper necessario per la gestione di < -> interfaccia COM).

0

Funzionerebbe anche questo?

In C#, 1. Chiamare Marshal.PtrToStructure 2. Modificare il valore 3. Chiamata Marshal.StructureToPtr