[英]Using a C++ DLL in C# when signature includes BYTE**
我正在研究C#项目,并且正在包装要在该项目中使用的C ++ DLL。 我已经在测试项目中捕获了此行为,并已重命名了函数调用以保护无辜者。
一切似乎都很好,除了我很难理解的一种功能。 DLL标头中该函数的签名为:
int32_t DoTheWork(BYTE **dest, BYTE *Src, int32_t szSrc);
我的包装器毫无问题地接收到Src字节数组(易于测试,因为这只是一个char字符串)。 返回目标参数并不是那么简单。
我尝试了不同的方法将dest参数从C#传递给包装函数,但是当我收到它时,C#中的dest字节数组的长度为1(而不是预期的32)字节,否则返回崩溃。 我在下面的实例是崩溃。 我需要了解如何传递字节数组作为参考,将结果复制到该字节数组中,并以完整的字节补全形式返回它而不会崩溃。 我花了一天多的时间在网上查找和修改代码,但仍然无法正常工作。
另外,对我来说,将C ++ DLL中创建的指针一直带到C#调用函数中,而不是将值复制到C ++包装器中的C#字节数组中,对我来说会更好吗? 如果是这样,我该如何正确清理C#内部的内存?
我在Win8上使用VS2010。 这是我的代码:
**适用于OriginalCPPDll.dll的OriginalCPPClass.h
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);
}
}
}
错误对话框显示:
mscorlib.dll中发生了类型为'System.AccessViolationException'的未处理异常。其他信息:尝试读取或写入受保护的内存。 这通常表明其他内存已损坏。
我终于找到了做我所需要的正确方法。 事实证明,仅传递一个预先设置大小的字节数组来收集结果是很好的(不幸的是,必须传递足够大的缓冲区来处理结果,该缓冲区的长度事先未知,但永远不会大于原始大小的两倍) )。 然后在包装器类中,当我调用原始C ++库时,我会收到一个新分配的内存块,并将内容复制到我的“ BYTE * dest”参数(不再作为BYTE **传递),并删除从图书馆。 我只是将其留给自动封送处理来处理双向阵列的传输。 完美运行,并且我返回的字节串被证明是正确的。 感谢您的所有帮助。
这是我的最终代码:
**适用于OriginalCPPDll.dll的OriginalCPPClass.h
class OriginalCPPClass {
public:
OriginalCPPDLLClass();
virtual ~OriginalCPPDLLClass();
int32_t DoTheWork(BYTE **dest, BYTE *Src, int32_t szSrc);
};
** WrapperDLL.cpp
#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);
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.