2016-12-20 9 views
0

Я написал базовую библиотеку C++, которая получает данные с сервера OPC UA и форматирует его в массив строк (char **). Я подтвердил, что он работает автономно, но теперь я пытаюсь вызвать его из программы на C#, используя библиотеки DLL/pInvoke и сталкиваясь с серьезными ошибками памяти.(C#) AccessViolationException при получении char ** из C++ DLL

Мой C# главная:

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 были заполнены как char ** с помощью библиотеки C++ DLL, которая затем обрабатывается обратно C# и отформатирован в список. Однако AccessViolationException бросается каждый раз, когда я вызываю getTopLevelNodes из C#. Что я могу сделать, чтобы исправить эту проблему памяти? Это лучший способ передать массив строк через interop?

Спасибо заранее

Edit: Я до сих пор ищет больше ответов, если есть более простой способ реализовать массив строк Interop между C# и DLL, пожалуйста, дайте мне знать!

+0

Не ставить это как ответ, поскольку я не уверен и не могу его протестировать, но вы пытались использовать 'IntPtr []' вместо 'IntPtr', поскольку вы, в некотором смысле, передаете массив указателей, а не только один указатель. Кроме того, как замечание, я считаю, что у вас есть утечка памяти из 1 символа для вашего первого элемента массива, так как вы malloc место для него, а затем вызовите malloc снова на первой итерации вашего цикла. – pstrjds

+0

@pstrjds спасибо, я исправлю это –

+0

Я не знаю вашу ситуацию, но можно ли создать обертку C++/CLI вокруг вашего C++-вызова? Тогда у вас может быть класс, который делает вызов, который заполняет вектор, а затем выталкивает эти данные в структуру .Net для потребления на вашей стороне C#. – pstrjds

ответ

2

МЕТОД 1 - Усовершенствованная структура Маршаллинг.

В отличие от сортировочных список, попытайтесь создать с #-структуру, как это:

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

Теперь в C# Маршалловых так:

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++.

«Пакет» здесь ключевой элемент, чтобы гарантировать, что структуры будут одинаково совпадать в памяти.

МЕТОД 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 только получает символ *. Теперь вам понадобится разделитель, чтобы разделить строки. УКАЗЫВАЙТЕ, ЧТО ВАША СТРОКА ВЕРОЯТНО ДОСТАТОЧНО '\ 0' УЖЕ ИСПОЛЬЗУЕТ АЛГОРИТМ ЗАМЕНИТЬ, ЧТОБЫ ЗАМЕНИТЬ, ЧТО С ';' ИЛИ ЧТО-НИБУДЬ И ТОКЕНИЗИРУЙТЕ ЛЕГКО В ЛОПЕ В CPP, ИСПОЛЬЗУЯ STRTOK С ';' КАК ОТЛИЧИТЕЛЬ ИЛИ ИСПОЛЬЗУЙТЕ BOOST!

ИЛИ, попробуйте сделать массив указателей байтов, если это возможно.

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

Этот способ намного проще. Блок unsafe разрешает блок fixed, и исправление гарантирует, что механизм управления памятью C# не перемещает эти данные.

+0

Это выглядит многообещающим, я постараюсь реализовать и вернуться к вам. Благодаря! –

+0

@ T.Meads Так как это строки, убедитесь, что вы заметили мое редактирование, вы должны Marshal.sizeof (theStringData), так как во время выполнения будут определены размеры строк !. В идеале структуры будут детерминированными. т.е. определенное количество байтовых массивов определенного размера. байты [5] [12]. – sbail95

+0

@ T.Meads факт факта, так как вы просто используете строки Мне нравится мой «метод 2», который я добавил для вас лучше. – sbail95

 Смежные вопросы

  • Нет связанных вопросов^_^