2017-02-13 13 views
0

Я работаю над проектом C# и обертываю C++ DLL для использования в проекте. Я воспринял это поведение в тестовом проекте, с вызовами функций, переименованными для защиты невинных.Использование C++ DLL в C#, когда подпись включает BYTE **

Все кажется прекрасным, за исключением одного типа функций, которые у меня возникают с трудом. Сигнатура для этой функции в заголовке DLL является:

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

Моя обертка получает массив байтов Src без проблем (легко проверить, так как это просто строка символов). Обратный деструктор не так прост.

Я пробовал разные способы передать параметр dest из C# в завернутую функцию, но когда я получу его обратно, либо массив байтов dest в C# имеет длину 1 (вместо ожидаемых 32) байтов, либо обратный сбой. Экземпляр, который у меня есть, - это крах. Мне нужно понять, как передать массив байтов в качестве ссылки, скопировать результаты в этот массив байтов и вернуть его с полным набором байтов без сбоев. Я потратил больше дня на это, смотря онлайн, и внося изменения в код, но я все еще не получаю его, чтобы он работал правильно.

Кроме того, было бы лучше, если бы я просто взял указатель, созданный в C++ DLL, полностью в функцию вызова C# вместо того, чтобы копировать значения в массив байтов C# в моей C++-оболочке? Если да, то как я могу правильно очистить эту память внутри C#?

Я использую VS2010 на Win8. Вот мой код:

** OriginalCPPClass.h для OriginalCPPDll.dll

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

** WrapperDLL.cpp (без сопутствующая .h файл)

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

Диалог ошибок:

Необработанное исключение типа «System.AccessViolationException» произошло в mscorlib.dll Дополнительная информация: Попытка чтения или записи защищенной памяти. Это часто свидетельствует о том, что другая память повреждена.

+0

Вы можете найти все это гораздо проще с C++/CLI, а не P/Invoke. –

+0

'BYTE ** dest' в этом случае является указателем на массив байтов. Ответчик отвечает за выделение массива и возвращает адрес этого массива вызывающему. Это хочет быть «вне IntPtr» в вашем коде C#. Затем вы будете использовать класс 'Marshal' для копирования с этого указателя на массив C#. Неуправляемый код также должен будет экспортировать дезактиватор. –

+1

Эта функция очень трудно назвать надежной, она хочет вернуть массив, но нет руководства, как вы должны освобождать память для массива. Если вы не должны выпускать его, то писать в буфер очень рискованно. И вы не завернули его правильно, pDest должен быть BYTE *, чтобы вызывающий мог передать буфер, который нужно заполнить. И вам нужен дополнительный аргумент, в котором говорится, насколько велик буфер, поэтому вы не можете писать за его пределами и коррумпировать память. Поговорите с программистом, который написал OriginalCPPClass, ему нужно сделать лучшую работу или дать рекомендации. –

ответ

0

Наконец-то я нашел правильный способ сделать то, что мне было нужно. Оказывается, что просто передать массив размером до размера, чтобы собрать результаты, хорошо (к сожалению, нужно передать достаточно большой буфер для обработки результата, длина которого неизвестна раньше времени, но никогда не будет больше, чем в два раза больше исходного размера). Затем в классе оболочки я получаю недавно выделенный фрагмент памяти, когда я вызываю исходную библиотеку C++, и копирую содержимое в мой параметр «BYTE * dest» (больше не передается как BYTE **) и удаляет кусок, полученный из библиотека. Я просто оставляю это до автоматического маршалинга, чтобы обрабатывать передачу массива в обоих направлениях. Хорошо работает, и строка байтов, которые я возвращаю, оказалась правильной. Спасибо за помощь.

Вот мой окончательный код:

** OriginalCPPClass.h для OriginalCPPDll.dll

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

** WrapperDLL.каст

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