使用结构的 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 秒,type
和 data
被销毁。由 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());
}
同样,这是非常幼稚的,而且可能是错误的。它只是展示一种方法,您可以在一般情况下在调用者和被调用者之间传递数据。保重。
有没有办法使用结构将字符串从 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 秒,type
和 data
被销毁。由 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());
}
同样,这是非常幼稚的,而且可能是错误的。它只是展示一种方法,您可以在一般情况下在调用者和被调用者之间传递数据。保重。