2017-02-13 1 views
0

Ich arbeite an einem C# -Projekt und binde eine C++ - DLL für die Verwendung im Projekt ein. Ich habe dieses Verhalten in einem Testprojekt erfasst, bei dem Funktionsaufrufe umbenannt wurden, um Unschuldige zu schützen.Verwenden einer C++ - DLL in C#, wenn die Signatur BYTE enthält **

Alles scheint gut, außer für eine Art von Funktion, die ich habe ein schweres Verständnis. Die Signatur für diese Funktion in dem DLL-Header ist:

int32_t DoTheWork(BYTE **dest, BYTE *Src, int32_t szSrc); 

Mein Wrapper empfängt die Src Byte-Array ohne Probleme (leicht getestet, da dies nur ein Zeichen String). Der return dest parameter ist nicht ganz so einfach.

Ich habe verschiedene Möglichkeiten versucht, den Parameter dest von C# an die Wrapped-Funktion zu übergeben, aber wenn ich es zurück erhalte, hat das Byte-Array in C# die Länge 1 (statt der erwarteten 32) oder Die Rückkehr stürzt ab. Die Instanz, die ich unten habe, ist ein Absturz. Ich muss verstehen, wie man ein Byte-Array als Referenz übergibt, Ergebnisse in dieses Byte-Array kopiert und es mit dem vollen Byte-Satz zurückgibt, ohne abzustürzen. Ich habe mehr als einen Tag damit verbracht, online zu schauen und Änderungen am Code vorzunehmen, aber ich bekomme immer noch nicht, dass es richtig funktioniert.

Wäre es auch besser für mich, den in der C++ - DLL erstellten Zeiger ganz in die aufrufende C# -Funktion zu bringen, anstatt die Werte in das C# -Byte-Array in meinem C++ - Wrapper zu kopieren? Wenn ja, wie kann ich diesen Speicher innerhalb von C# richtig aufräumen?

Ich verwende VS2010 auf Win8. Hier ist mein Code:

** OriginalCPPClass.h für OriginalCPPDll.dll

class OriginalCPPClass { 
public: 
    OriginalCPPDLLClass(); 
    virtual ~OriginalCPPDLLClass(); 
    int32_t DoTheWork(BYTE **dest, BYTE *Src, int32_t szSrc); 
}; 

** WrapperDLL.cpp (keine Begleit .h-Datei)

#include "CytoCrypto.h" 

extern "C" 
{ 

#define WRAPPERCLASS_EXPORT __declspec(dllexport) 

WRAPPERCLASS_EXPORT OriginalCPPClass* Wrap_Create() 
{ 
    return new OriginalCPPClass(); 
} 

WRAPPERCLASS_EXPORT void Wrap_Destroy(OriginalCPPClass* pObj) 
{ 
    delete pObj; 
} 

WRAPPERCLASS_EXPORT int32_t __cdecl Wrap_DoTheWork(OriginalCPPClass* pObj, BYTE **pDest, BYTE *pSrc, int32_t szSrc) 
{ 
    BYTE *result = NULL; 
    int32_t sz = pObj->DoTheWork(&result, pSrc, szSrc); 
    *(result+sz) = '\0'; 
    if (sz > 0) 
    { 
     memcpy(pDest, result, sz); 
    } 
    return (sz >= 0) ? sz : 0; 
} 

} 

** Program.cs

using System; 
using System.Text; 
using System.Runtime.InteropServices; 


namespace ConsoleApplication1 
{ 
    class Program 
    { 
     [DllImport("OriginalCPPDll.dll", CallingConvention = CallingConvention.Cdecl)] 
     public static extern IntPtr Wrap_Create(); 

     [DllImport("OriginalCPPDll.dll", CallingConvention = CallingConvention.Cdecl)] 
     public static extern void Wrap_Destroy(IntPtr pObj); 

     [DllImport("OriginalCPPDll.dll", CallingConvention = CallingConvention.Cdecl)] 
     public static extern Int32 Wrap_DoTheWork(IntPtr pObj, out IntPtr pDest, byte[] src, Int32 szSrc); 

     static void Main(string[] args) 
     { 
      string src = "this is the source string"; 
      IntPtr pnt = Marshal.AllocHGlobal(1000); 
      byte[] bytearray = new byte[1000]; 
      byte[] srcBytes = Encoding.ASCII.GetBytes(src); 
      Int32 szSrc = srcBytes.Length; 

      IntPtr obj = Wrap_Create(); 
      Int32 size = Wrap_DoTheWork(obj, out pnt, srcBytes, szSrc); 
      Marshal.Copy(pnt, bytearray, 0, size); 
      Wrap_Destroy(obj); 

      Marshal.Copy(pnt, bytearray, 0, size); 
     } 
    } 
} 

Fehler Dialog sagt:

Eine nicht behandelte Ausnahme vom Typ 'System.AccessViolationException' ist in mscorlib.dll aufgetreten. Weitere Informationen: Es wurde versucht, geschützten Speicher zu lesen oder zu schreiben. Dies ist oft ein Hinweis darauf, dass anderer Speicher beschädigt ist.

+0

Sie könnten dies alles viel einfacher, mit C++/CLI anstatt P/Invoke finden. –

+0

'BYTE ** dest 'ist in diesem Fall ein Zeiger auf ein Byte-Array. Der Angerufene ist verantwortlich für die Zuweisung des Arrays und gibt die Adresse dieses Arrays an den Aufrufer zurück. Dies möchte in Ihrem C# -Code "out IntPtr" sein. Sie verwenden dann die Marshal-Klasse, um von diesem Zeiger in ein C# -Array zu kopieren. Der nicht verwaltete Code muss auch einen Deallocator exportieren. –

+1

Diese Funktion ist sehr schwer zuverlässig zu benennen, sie möchte ein Array zurückgeben, aber es gibt keine Anleitung, wie Sie den Speicher für das Array freigeben sollen. Wenn Sie es nicht freigeben sollten, ist das Schreiben in den Puffer sehr riskant. Und Sie haben es nicht richtig umgebrochen, pDest muss BYTE * sein, damit der Aufrufer einen zu füllenden Puffer übergeben kann. Und Sie benötigen ein zusätzliches Argument, das angibt, wie groß der Puffer ist, so dass Sie nicht darüber hinaus schreiben und den Speicher beschädigen können. Sprechen Sie mit dem Programmierer, der OriginalCPPClass geschrieben hat, er muss einen besseren Job machen oder Anleitung geben. –

Antwort

0

Ich fand endlich den richtigen Weg, um das zu tun, was ich brauchte. Stellt sich heraus, dass nur eine vor-Größe Byte-Array übergeben, um die Ergebnisse zu sammeln ist gut (leider müssen einen Puffer groß genug übergeben, um das Ergebnis, deren Länge ist unbekannt unbekannt ist aber nie größer als das Doppelte der ursprünglichen Größe). Dann erhalte ich in der Wrapperklasse einen neu zugewiesenen Speicherabschnitt, wenn ich die ursprüngliche C++ - Bibliothek aufruft, und kopiere den Inhalt in meinen "BYTE * dest" -Parameter (nicht mehr als BYTE ** übergeben) und lösche den empfangenen Chunk die Bibliothek. Ich überlasse es nur dem automatischen Marshalling, um die Übertragung des Arrays in beiden Richtungen zu handhaben. Funktioniert perfekt, und die Bytefolge, die ich zurückbekomme, hat sich als richtig erwiesen. Danke für die Hilfe.

Hier ist meine letzte Code:

** OriginalCPPClass.h für OriginalCPPDll.dll

class OriginalCPPClass { 
public: 
    OriginalCPPDLLClass(); 
    virtual ~OriginalCPPDLLClass(); 
    int32_t DoTheWork(BYTE **dest, BYTE *Src, int32_t szSrc); 
}; 

** WrapperDLL.cav

#include "CytoCrypto.h" 

extern "C" 
{ 

#define WRAPPERCLASS_EXPORT __declspec(dllexport) 

WRAPPERCLASS_EXPORT OriginalCPPClass* Wrap_Create() 
{ 
    return new OriginalCPPClass(); 
} 

WRAPPERCLASS_EXPORT void Wrap_DestroyPtr(BYTE* ptr) 
{ 
    HeapFree(GetProcessHeap(), 0, ptr); 
} 

WRAPPERCLASS_EXPORT int32_t __cdecl Wrap_DoTheWork(OriginalCPPClass* pObj, BYTE *pDest, BYTE *pSrc, int32_t szSrc) 
{ 
    BYTE *result = NULL; 
    int32_t sz = pObj->DoTheWork(&result, pSrc, szSrc); 
    if (sz > 0) 
    { 
     memcpy(pDest, result, ret+1); 
    } 
    if (result) 
     pObj->DestroyPtr(result); 
    return (sz >= 0) ? sz : 0; 
} 

} 

** Program.cs

using System; 
using System.Text; 
using System.Runtime.InteropServices; 


namespace ConsoleApplication1 
{ 
    class Program 
    { 
     [DllImport("OriginalCPPDll.dll", CallingConvention = CallingConvention.Cdecl)] 
     public static extern IntPtr Wrap_Create(); 

     [DllImport("OriginalCPPDll.dll", CallingConvention = CallingConvention.Cdecl)] 
     public static extern void Wrap_Destroy(IntPtr pObj); 

     [DllImport("OriginalCPPDll.dll", CallingConvention = CallingConvention.Cdecl)] 
     public static extern Int32 Wrap_DoTheWork(IntPtr pObj, byte[] dest, byte[] src, Int32 szSrc); 

     static void Main(string[] args) 
     { 
      string srcStr = "this is the source string"; 

      byte[] resBytes = new byte[srcStr.Length*2]; 
      byte[] srcBytes = Encoding.ASCII.GetBytes(srcStr); 
      Int32 srcSize = srcBytes.Length; 

      IntPtr obj = Wrap_Create(); 

      Int32 size = Wrap_DoTheWork(obj, resBytes, srcBytes, srcSize); 

      Wrap_Destroy(obj); 
     } 
    } 
}