Я работаю над проектом 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 Дополнительная информация: Попытка чтения или записи защищенной памяти. Это часто свидетельствует о том, что другая память повреждена.
Вы можете найти все это гораздо проще с C++/CLI, а не P/Invoke. –
'BYTE ** dest' в этом случае является указателем на массив байтов. Ответчик отвечает за выделение массива и возвращает адрес этого массива вызывающему. Это хочет быть «вне IntPtr» в вашем коде C#. Затем вы будете использовать класс 'Marshal' для копирования с этого указателя на массив C#. Неуправляемый код также должен будет экспортировать дезактиватор. –
Эта функция очень трудно назвать надежной, она хочет вернуть массив, но нет руководства, как вы должны освобождать память для массива. Если вы не должны выпускать его, то писать в буфер очень рискованно. И вы не завернули его правильно, pDest должен быть BYTE *, чтобы вызывающий мог передать буфер, который нужно заполнить. И вам нужен дополнительный аргумент, в котором говорится, насколько велик буфер, поэтому вы не можете писать за его пределами и коррумпировать память. Поговорите с программистом, который написал OriginalCPPClass, ему нужно сделать лучшую работу или дать рекомендации. –