繁体   English   中英

使用结构将 C++ 字符串转换为 C#

[英]C++ string to C# using a struct

有没有办法使用结构将字符串从 C++ 发送到 C#? 我有一个包含图像数据(数据和大小)的结构,在这个结构中我也想从图像中获取条形码数据。 我试图通过 C# 中的 IntPtr 来做到这一点,但是通过 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: "\0\0\0\0\0\0\0\0\f\0\0\0"

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

我如何尝试从 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());
}

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

C++ 代码是错误的。

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

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

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

设计互操作并非易事。 从实际设计良好的 C API 中获取示例。 一个重要的事情就是返回字符串的长度,例如(这是非常罕见的,你想通过刚才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());
}

同样,这是非常幼稚的,可能是错误的。 这只是为了展示一种可以在调用者和被调用者之间传递数据的方法。 多保重。

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() 方法。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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