繁体   English   中英

签名包含BYTE **时在C#中使用C ++ DLL

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM