Как получить доступ к указателю на основе C по ссылке в вызове взаимодействия C # с использованием массивов? - PullRequest
2 голосов
/ 27 июня 2011

Я довольно новичок в P / Invoke (и очень ржавый на C), и я потратил немало времени, пытаясь решить проблему фоллинга:

Я хочу вызвать неуправляемую функцию C из управляемого C #. Функция C должна создать массив со значениями и вернуть его, чтобы я мог использовать эти значения в C #. (Обратное работает нормально, то есть передавая массив из C # в C.) Т.е. Я хочу сделать параметры передачи в качестве ссылок с массивами. Вот простой пример кода, который, по моему мнению, должен работать (но не работает):

C:

int __declspec(dllexport) __stdcall AllocateMemory(int **values, const unsigned int N)
{
  int sum = 0;
  free(*values);
  *values = (int*) malloc(N*sizeof(int));
  for(unsigned int i = 0; i < N; i++)  { 
  (*values)[i] = i;    
  sum += (*values)[i];
}

Это отлично работает прямо из консольного тестового приложения C:

int main( int argc, char** argv) 
{
  int* values;
  int sum = AllocateMemory(&values, 4);
  printf ("Sum: %d\n", sum);

}

Однако выполнение этого вызова непосредственно из C # приводит к (памяти) System.AccessVioaltionException:

[DllImport("Test.dll", CharSet = CharSet.Ansi, SetLastError = true, CallingConvention = CallingConvention.Winapi)]
public static extern int AllocateMemory([Out, MarshalAs(UnmanagedType.LPArray, SizeConst = 10)] out int[] someValues, int N);

private static void RunCalculation(string message)
{
  int[] someValues; 
  int sum = AllocateMemory(out someValues, 4);
}

Я безуспешно пробовал разные версии подписи DllImport, и сейчас я продолжу свое расследование. Однако, если у кого-то есть подсказка, я буду очень признателен!


(Ниже добавлено примерно час спустя (после некоторых комментариев ниже), поскольку мне не разрешено оставлять ответ сам до завтра)

Я обнаружил, что смог использовать следующий код C #:

[DllImport("FrontAuction2.dll", CharSet = CharSet.Ansi, SetLastError = true, CallingConvention = CallingConvention.Winapi)]
public static extern float AllocateCudaMemory(out IntPtr someValues, int N);

private static IntPtr AllocateCudaMemory()
{ 
  IntPtr temp; 
  float sum = AllocateCudaMemory(out temp, 4);  

  //NOTE: If you'd actually want to get the value and just not want to pass the IntPtr to another P/Invoke method (as I do), this is one way: 
  //var values = new int[4];
  //Marshal.Copy(temp, values, 0, 4);

  return temp;
} 

Однако в комментариях ниже есть несколько интересных моментов, которые я прокомментировал.

Цель всего этого в моей учетной записи - разрешить передачу ссылки на память графического процессора CUDA в C # для дальнейшего использования. Я думаю, что лучший способ сделать это, вероятно, состоит в том, чтобы сохранить ссылку на сервере состояний COM C ++ вместо передачи ее в C #. Но решение этой проблемы, по крайней мере, позволит мне сосредоточиться на вещах на GPU прямо сейчас, а не на взаимодействии.

Ответы [ 2 ]

1 голос
/ 27 июня 2011

Ваш код C имеет утечку памяти, память для массива не освобождается. Маршаллеру пинвока не нравится утечка памяти. Он попытается освободить массив, он будет использовать CoTaskMemFree (). Это не приведет к хорошему концу, вы не выделяли CoTaskMemAlloc ().

Не пишите код, который требует от вызывающей стороны освободить память, выделенную вызываемой стороной. Это очень редко получается хорошо. Просто позвольте вызывающей стороне передать массив, который вы заполняете:

int __declspec(dllexport) __stdcall AllocateMemory(int *values, const unsigned int N)
{
  int sum = 0;
  for(unsigned int i = 0; i < N; i++)  { 
    values[i] = i;    
    sum += values[i];
  }
}

[DllImport("Test.dll")]
public static extern int AllocateMemory(int[] someValues, int N);
...
  int[] values = new int[10];
  int sum = AllocateMemory(values, values.Length);

Вам нужно придумать другое имя для этой функции:)

0 голосов
/ 27 июня 2011

Лучше всего, если вы можете сделать это, выделить память в C # и позволить вашему C-коду заполнить массив:

C

__declspec(dllexport) void __stdcall PopulateArray(int N, int values[])
{
    int i;
    for (i = 0; i < N; i++)
        values[i] = i;    
}

C #

[DllImport(@"test.dll")]
public static extern void PopulateArray(int N, int[] values);

static void Main(string[] args)
{
    int N = 5;
    int[] values = new int[N];
    PopulateArray(N, values);
}

Кажется, что ваш вопрос пропустил важную информацию, которая необходима для выделения памяти в вашем коде C, а затем удерживайте указатель в вашем коде C #, чтобыв свою очередь вы можете передать его CUDA.

Сделайте это так:

C

__declspec(dllexport) int* __stdcall AllocateIntArray(int N)
{
    int i;
    int *values = malloc(N*sizeof(int));
    for (i = 0; i < N; i++)
        values[i] = i;    
    return values;
}

__declspec(dllexport) void __stdcall FreeMemory(void* ptr)
{
    free(ptr);
}

C #

[DllImport(@"test.dll")]
public static extern IntPtr AllocateIntArray(int N);

[DllImport(@"test.dll")]
public static extern void FreeMemory(IntPtr ptr);

static void Main(string[] args)
{
    IntPtr ptr = AllocateIntArray(5);
    //...do something with the memory
    FreeMemory(ptr);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...