簡體   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