簡體   English   中英

(C#)從C ++ DLL中獲取char **時出現AccessViolationException

[英](C#) AccessViolationException when getting char ** from C++ DLL

我已經編寫了一個基本的C ++庫,該庫從OPC UA服務器獲取數據並將其格式化為字符串數組(char **)。 我已經確認它可以獨立運行,但是現在我試圖使用DLLs / pInvoke從C#程序中調用它,並遇到嚴重的內存錯誤。

我的C#main:

List<String> resultList = new List<string>();
IntPtr inArr = new IntPtr();
inArr = Marshal.AllocHGlobal(inArr);
resultList = Utilities.ReturnStringArray(/*data*/,inArr);

C#輔助函數:

public class Utilities{

    [DllImport(//DllArgs- confirmed to be correct)]
    private static extern void getTopLevelNodes(/*data*/, IntPtr inArr);

    public static List<String> ReturnStringArray(/*data*/,IntPtr inArr)
    {

       getTopLevelNodes(/*data*/,inArr); // <- this is where the AccessViolationException is thrown
       //functions that convert char ** to List<String>
       //return list
    }

最后,我的C ++ DLL實現:

extern "C" EXPORT void getTopLevelNodes(*/data*/,char **ret){

std::vector<std::string> results = std::vector<std::string>();
//code that fills vector with strings from server

ret = (char **)realloc(ret, sizeof(char *));
ret[0] = (char *)malloc(sizeof(char));
strcpy(ret[0], "");
int count = 0;
int capacity = 1;

for (auto string : results){
        ret[count] = (char*)malloc(sizeof(char) * 2048);
        strcpy(ret[count++], string.c_str());
        if (count == capacity){
                capacity *= 2;
                ret = (char **)realloc(ret, sizeof(char *)*capacity + 1);
        }
}

這應該做的是,初始化一個列表以保存最終結果,然后將IntPtr初始化為由C ++ DLL填充為char **,然后將其用C#處理並格式化為一個List。 但是,每次我從C#調用getTopLevelNodes時,都會引發AccessViolationException。 我該怎么做才能解決此內存問題? 這是通過互操作傳遞字符串數組的最佳方法嗎?

先感謝您

編輯:我仍在尋找更多的答案,如果有一種更簡單的方法來實現C#和DLL之間的字符串數組互操作,請讓我知道!

方法1-高級結構編組。

與封送列表不同,嘗試創建如下的ac#結構:

[StructLayout(LayoutKind.Sequential, Pack = 2)]
public struct StringData
{
    public string [] mylist; /* maybe better yet byte[][] (never tried)*/
};

現在在c#marshall中是這樣的:

IntPtr pnt = Marshal.AllocHGlobal(Marshal.SizeOf(StringData)); // Into Unmanaged space

獲取指向結構的指針。

StringData theStringData = /*get the data*/;
Marshal.StructureToPtr(theStringData, pnt, false);
                                    // Place structure into unmanaged space.

getTopLevelNodes(/* data */, pnt); // call dll

theStringData =(StringData)Marshal.PtrToStructure(pnt,typeof(StringData));
                                    //get structure back from unmanaged space.
Marshal.FreeHGlobal(pnt); // Free shared mem

現在在CPP中:

#pragma pack(2)
/************CPP STRUCT**************/
struct StringDataCpp
{
    char * strings[]
}; 

和功能:

extern "C" EXPORT void getTopLevelNodes(/*data*/,char *ret){ //just a byte pointer.   

struct StringDataCpp *m = reinterpret_cast<struct StringDataCpp*>(ret);

//..do ur thing ..//

}

我也將這種模式用於更復雜的結構。 關鍵是您只是從c#中逐字節復制並在c ++中逐字節解釋。

此處的“ pack”是關鍵,以確保結構在內存中的對齊方式相同。

方法2- fixed簡單字節數組

    //USE YOUR LIST EXCEPT List<byte>. 
        unsafe{
           fixed (byte* cp = theStringData.ToArray)
             {

                getTopLevelNodes(/* data */, cp)
    /////...../////

//SNIPPET TO CONVERT STRING ARRAY TO BYTE ARRAY
    string[] stringlist = (/* get your strings*/);
    byte[] theStringData = new stringlist [stringlist .Count()];
     foreach (string b in parser)
     {
// ADD SOME DELIMITER HERE FOR CPP TO SPLIT ON?
          theStringData [i] = Convert.ToByte(stringlist [i]);
          i++;
     }

現在

CPP剛收到char *。 您現在需要一個定界符來分隔字符串。 注意您的字符串可能已使用'\\ 0'代替,已使用替換算法替換為';' 或帶有';'的STRTOK在CPP循環中輕松進行某些標記和標記 作為定界符或使用提升!

或者,如果可能,嘗試制作一個字節指針數組。

Byte*[i] theStringStartPointers = &stringList[i]/* in a for loop*/
fixed(byte* *cp = theStringStartPointers) /// Continue

這種方式要簡單得多。 unsafe塊允許使用fixed塊,並且fixed塊可確保c#內存管理機制不會移動該數據。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM