使用结构的 C++ 字符串到 C#

C++ string to C# using a struct

有没有办法使用结构将字符串从 C++ 发送到 C#?我有一个包含图像数据(数据和大小)的结构,在这个结构中我也想从图像中获取条形码数据。我试图通过 C# 中的 IntPtr 来完成它,但是 return 乱码并通过 Stringbuilder,但随后无法编组结构。请参阅下面的所有相关代码

C# 结构

public struct ImageInfo
{
    public IntPtr data; // image data, works
    public int size;    // image data, works

    // string
    //public IntPtr barcodeType; // return gibberish
    //public IntPtr barcodeData; // return gibberish

    public StringBuilder barcodeType; //Cannot marshal field 'barcodeType' of type 'ImageProcessingOpenCv.ImageInfo'
    public StringBuilder barcodeData; //Cannot marshal field 'barcodeData' of type 'ImageProcessingOpenCv.ImageInfo'
}

C# 导入:

[DllImport("AlgorithmsCpp.dll", EntryPoint = "ScanBarcode", ExactSpelling = false, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern void ScanBarcode(byte[] data, int dataLen, ref ImageInfo imTemplate);

调用 C++ 以及我如何尝试从 IntPtr 获取字符串:

ImageInfo imInfo = new ImageInfo();
AlgorithmCpp.ScanBarcode(sourceImagePixels, sourceImagePixels.Length, ref imInfo); // wont work with StringBuilder.

string stringa = Marshal.PtrToStringAnsi(imInfo.barcodeType); // result: gibberish
stringa = Marshal.PtrToStringAuto(imInfo.barcodeType);        // result: gibberish
stringa = Marshal.PtrToStringBSTR(imInfo.barcodeType);        // result: gibberish
stringa = Marshal.PtrToStringUni(imInfo.barcodeType);         // result: gibberish
stringa = Marshal.PtrToStringUTF8(imInfo.barcodeType);        // result: gibberish

// example what is returned: "[=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=]\f[=12=][=12=][=12=]"

C++ 结构:

struct ImageInfo
{
    unsigned char* data;
    int size;

    // String
    //unsigned char* barcodeType; // for IntPtr, return gibberish
    //unsigned char* barcodeData; // for IntPtr, return gibberish

    char* barcodeType; // for StringBuilder
    char* barcodeData; // for StringBuilder
};

我如何尝试 return 来自 C++ 的字符串

#define DllExport extern "C" __declspec(dllexport)

DllExport void ScanBarcode(unsigned char* data, int dataLen, ImageInfo& imInfo)
{
    // do stuff to get barcode

    // for IntPtr
    std::string type = symbol->get_type_name();
    std::string data = symbol->get_data();

    imInfo.barcodeType = (unsigned char*)type.c_str();
    imInfo.barcodeData = (unsigned char*)data.c_str();

    // For StringBuilder
    strcpy(imInfo.barcodeData, symbol->get_data().c_str());
    strcpy(imInfo.barcodeType, symbol->get_type_name().c_str());
}

我希望通过上面的代码,您可以了解我尝试过的内容以及我想要的内容。如果您需要任何进一步的信息,请告诉我,我会尽力提供。

一个StringBuilder不是一个字符串。

将您的结构更改为:

public struct ImageInfo
{
    public IntPtr data; // image data, works
    public int size;    // image data, works

    // string
    //public IntPtr barcodeType; // return gibberish
    //public IntPtr barcodeData; // return gibberish

    public string barcodeType;
    public string barcodeData;
}

应该可以工作,因为编译器现在有一个已知值(某种)类型,它可以传递给您的 C++ 结构。 StringBuilder 将不起作用,因为这是一个无法转换为字符串的对象,编译器必须知道调用 .ToString() 方法。

C++ 代码错误。

imInfo.barcodeType = (unsigned char*)type.c_str();
imInfo.barcodeData = (unsigned char*)data.c_str();

c_str() 给你一个指向字符串开头的指针,是的。你可以把它传递给一个函数,很好。但是你不能return它!谁拥有记忆?它的寿命是多少?它什么时候被释放? C++ 没有自动内存管理——您不能只是 return 对象和指针并期望它们神奇地工作。您甚至不能依赖这种不良行为来产生错误,正如您刚刚看到的那样——您得到的是垃圾;有时你做对了,有时做错了,有时你引入了可怕的安全漏洞。

ScanBarcode return 秒,typedata 被销毁。由 c_str() 编辑的指针 return 并未指向您认为它指向的数据(无论 c_str() 的内部实现如何)。您需要将该数据复制到共享内存,并确保它已正确释放。在不受管理的世界中,传递内存不是一件容易的事。这是首先设计托管系统和 C# 等语言的重要原因之一。

设计互操作性并非易事。从设计良好的实际 C API 中获取示例。一件重要的事情是 return 字符串的长度,例如(很少有​​人想传递 just char*,而不指定长度;如果您不拥有所指向的内存,那么这样做简直是疯了)。首先,决定谁应该分配和释放内存(这通常应该是相同的)。

您已经可以看出这是一个问题。 ImageInfo 由调用者分配。但是char*字段是被调用者分配的!没有办法明智地将对象的生命周期限制在 C++ 或 C# 端(同样,这不是 C# 的问题——C++ 调用者也会有同样的问题)。在过去,通常的处理方式是使用预先分配的缓冲区——也就是说,imInfo.barcodeType 不是指向字符串的指针,而是一个数组。 C# 结构将如下所示:

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
struct ImageInfo
{
  [MarshalAs(UnmanagedType.ByValTStr, SizeConst=255 /* this must be synchronized with the C++ code! */)]
  public string barcodeType;
}

C++ 方面会做这样的事情(不要在生产代码中使用它,它几乎肯定在某些方面是错误的——我的 C++ 很生疏;咨询 C++程序员):

struct ImageInfo
{
  char barcodeType[255];
}

if (type.barcodeType.length() < 254) 
{
  strcpy(imInfo.barcodeType, type.c_str());
}

同样,这是非常幼稚的,而且可能是错误的。它只是展示一种方法,您可以在一般情况下在调用者和被调用者之间传递数据。保重。