2012-04-02 2 views
5

У меня есть 3 участника C библиотека, которая одна из его экспортировали метода заключается в следующем:итерация над выделенной памяти с Marshal.AllocHGlobal()

#define MAX_INDEX 8 
int GetStuff(IN char* index[MAX_INDEX], OUT char* buf, IN size_t size); 

Первый аргумент заполняется с указателями в Buf аргумент где конкретные строки сохраняются. Размер указывает, как долго ожидается, что каждая строка будет в буфере буфф. Мой C# P/метод Invoke для этого в настоящее время выглядит следующим образом:

[DllImport("Path/To/Dll", CharSet = CharSet.Ansi)] 
private static extern int GetStuff(IntPtr indecies, IntPtr buf, Int32 size); 

C# P/Invoke метода является частным, потому что я оборачивать его функциональность в публичном «геттере» метод, который обрабатывает выделение/освобождение памяти для вызывающего абонента. Когда я использую этот метод в C++, итерация на самом деле довольно проста. Я просто сделать что-то вроде:

char* pIndecies[MAX_INDEX]; 
char* pBuffer = new char[MAX_INDEX * (256 + 1)]; // +1 for terminating NULL 

GetStuff(pIndecies, pBuffer, 256); 

// iterate over the items 
for(int i(0); i < MAX_INDEX; i++) { 
    if(pIndecies[i]) { 
     std::cout << "String for index: " << i << " " << pIndecies[i] << std::endl; 
    } 
} 

Из-за того, как они используются в C++, я решил, что я должен использовать объекты IntPtr и просто выделить память, что я буду нуждаться в куче, позвонить в машинный код, и перебирать его так, как я бы в C++. Затем я вспомнил, что символы в C# являются символами Unicode, а не символами ASCII. Будет ли итерация в C# работать одинаково, даже если я поместил итерацию в небезопасный блок кода? Моя первая мысль была сделать следующее: «Как я должен перебрать, что это родной метод возвращает код»

IntPtr pIndecies = Marshal.AllocHGlobal(MAX_INDEX * 4); // the size of a 32-pointer 
IntPtr pBuffer = Marshal.AllocHGlobal(MAX_INDEX * (256 + 1)); // should be the same 
NativeMethods.GetStuff(pIndecies, pBuffer, 256); 

unsafe { 
    char* pCStrings = (char*)pIndecies.ToPointer(); 
    for(int i = 0; i < MAX_INDEX; i++) { 
     if(pCStrings[i]) 
      string s = pCStrings[i]; 
    } 
} 

Мой вопрос заключается в том, Правильно ли это способ маршалировать эту функцию? Должен ли я использовать объект StringBuilder для второго аргумента? Одна сдерживающая проблема состоит в том, что первый аргумент представляет собой 1: 1 отображение структуры за методом GetStuff(). Значение каждого индекса имеет решающее значение для понимания того, что вы смотрите во втором аргументе буфера.

Я ценю любые предложения.

Спасибо, Энди

ответ

3

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

[DllImport("Path/To/Dll", CharSet = CharSet.Ansi)] 
private static extern int GetStuff(IntPtr[] index, IntPtr buf, Int32 size); 
.... 
IntPtr[] index = new IntPtr[MAX_INDEX]; 
IntPtr pBuffer = Marshal.AllocHGlobal(MAX_INDEX * 256 + 1); 
try 
{ 
    int res = NativeMethods.GetStuff(index, pBuffer, 256); 
    // check res for errors? 
    foreach (IntPtr item in index) 
    { 
     if (item != IntPtr.Zero) 
      string s = Marshal.PtrToStrAnsi(item); 
    } 
} 
finally 
{ 
    Marshal.FreeHGlobal(pBuffer); 
} 

Это довольно прямой перевод вашей версии на C++.

+0

Мне понравилось это предложение и попробуете, когда какое-то оборудование освободится (эта сторонняя библиотека работает против оборудования, которое я и двое - другие). Я обязательно буду отмечать как ответ, если он соответствует законопроекту. Думаю, так и будет. Знаешь, я не думал о создании массива IntPtr. Это довольно пятно. –

+0

Большое спасибо за решение. В цикле foreach я не смог проверить элемент item.Zero, и поэтому условие должно быть, если (item! = IntPtr.Zero). Я редактировал код, но он должен быть проверен экспертом (разумная предосторожность). Еще раз спасибо. –

+0

@AndrewFalanga Это решение в порядке. Это означает то же самое. Я думаю, что были внесены некоторые изменения в IntPtr с .net 4. Вы используете более низкую версию? –

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