2009-02-22 2 views
6

Ich habe eine C# -Klassenbibliothek, die Methoden enthält, die mit einer externen Anwendung verwendet werden müssen. Leider unterstützt diese externe Anwendung nur externe APIs in C/C++.Einfachste Möglichkeit, ein Array von C++ nach C# zu verschieben, zu modifizieren und an C++ zurückzugeben

Jetzt habe ich es geschafft, ein sehr einfaches COM-Beispiel zu bekommen, das zwischen einer C++ - DLL und einer C# -DLL funktioniert, aber ich stehe fest, wie ich Array-Daten bewegen kann. Diese

ist, was ich so weit gekommen, wie ein kleines Beispiel, das ich auf dem Netz der Kommunikation über COM gefunden:

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(); 

} 

Dies funktioniert die Add-Methode in meiner C# Klasse zu nennen. Wie kann ich dies ändern, um ein Double-Array aufzunehmen und zurückzugeben? (Es muss auch mit Strings auf der ganzen Linie gemacht werden).

Ich muss ein nicht verwaltetes Array nehmen, dieses Array an eine C# -Klasse für einige Berechnungen übergeben und dann die Ergebnisse an den Array-Verweis übergeben, der im ursprünglichen Funktionsaufruf (nicht verwaltet) C++ angegeben wurde.

Ich werde eine Funktion wie folgt aussetzen müssen:


* calcin - Verweis auf Array von Doppel

* calcOut - Verweis auf Array von Doppel

NumIn - Wert Größe des Eingangsfeldes

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 denke, Ich kann eine C++ \ CLI-DLL für die externe Anwendung verwenden, wenn dies einfacher als gerade COM ist dann werde ich bereit sein, darauf zu schauen.

Bitte seien Sie vorsichtig, da ich in erster Linie ein C# -Entwickler bin, aber in das tiefe Ende von Interop und C++ geworfen wurde.

Antwort

3

Ich habe vor einiger Zeit damit experimentiert, aber leider vergessen, wie alles zusammenpasst ... zu meinem Zweck stellte sich heraus, dass es schrecklich langsam war, also schneide ich das C# aus und ging zurück zu allem C++. Wenn du sagst, dass du in erster Linie ein C# -Entwickler bist, hoffe ich, dass du Hinweise verstehst, denn wenn du es nicht tust, gibt es keine Möglichkeit, sanft zu sein.

Passing Arrays kamen im Allgemeinen nach unten auf der Verwendung CoTaskMemAlloc Familie von Funktionen auf der C++ Seite (http://msdn.microsoft.com/en-us/library/ms692727(VS.85).aspx) und die Marshal-Klasse auf der C# Seite (http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal.aspx - die Methoden, wie AllocCoTaskMem hat).Für C# endete ich mit einem Utility-Klasse bis:

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

einfach etwas mehr Code auf Sie zu werfen: dieses 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; 
} 

diese C# genannt

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

Sorry, aber ich habe nicht die Zeit, all das auszuarbeiten und es richtig zu erklären, hoffentlich gibt dir das Hinweise in die richtige Richtung, zumindest einige Dinge zu googeln. Viel Glück

PS: Ich habe alle Informationen, um dieses Zeug aus dem Netz zu schreiben, so ist es da draußen.

1

Ich denke, ich kann eine C++ \ CLI-DLL für die externe Anwendung verwenden, wenn dies einfacher als gerade COM ist dann werde ich bereit sein, darauf zu schauen.

Wenn Sie nicht viel COM Erfahrung haben (und Arrays sind deutlich nicht einfach in COM) dann C++/CLI-Wrapper um die 3 rd Partei wahrscheinlich leichter sein wird.

Es wird auch nur eine einzige Marshalling-Stufe (native < -> verwaltet) anstelle der zusätzlichen Schritt der erforderliche COM Callable Wrapper Sie für Managed < -> COM-Schnittstelle benötigt werden.

0

Würde das auch funktionieren?

In C# 1. Rufen Sie Marshal.PtrToStructure 2. Ändern Sie den Wert 3. Rufen Sie Marshal.StructureToPtr