Проблема с маршалингом массива данных структуры из C ++ в C #

Я искал несколько дней и перепробовал все, что мог найти, но все еще не могу заставить это работать.

Подробности:
У меня есть стороннее приложение для торговли акциями, которое вызывает мою неуправляемую DLL. Он предоставляет данные, которые dll обрабатывает / фильтрует, а затем сохраняет в глобальном кольцевом буфере. Кольцевой буфер представляет собой массив структур длиной 100. Все это работает в процессе торговли акциями.

У меня также есть управляемое приложение на C #, вызывающее ту же самую DLL в другом процессе, которое должно получить информацию в глобальном кольцевом буфере как можно быстрее и эффективнее. Все работает, за исключением того, что я могу получить данные только для первой структуры в массиве. Также после вызова dll из C # код C # больше не знает, что arrayMD является массивом структур, он отображается в отладчике как простая структура. Может ли это быть memcpy в DLL, вызывающей проблему? Я пробовал все виды комбинаций с комбинациями [In, Out], IntPtr и Marchal.PtrToStructure. Я очень любезен. Любая помощь будет принята с благодарностью.

Спасибо

Вот что я пытаюсь сделать.
На стороне dll:

struct stMD
{
float Price;
unsigned int  PriceDir;
unsigned int  PriceDirCnt;
};

// Global memory
#pragma data_seg (".IPC")
bool NewPoint = false;      // Flag used to signal a new point.
static stMD aryMD [100] = {{0}};
#pragma data_seg()

void __stdcall RetrieveMD (stMD *LatestMD [])
{
memcpy(*LatestMD, aryMD, sizeof(aryMD));
}

На стороне C #:

[StructLayout(LayoutKind.Sequential)]
public struct stMD
{
public float Price;
public uint PriceDir;
public uint PriceDirCnt;
};

public static stMD[] arrayMD = new stMD[100];

[DllImport(@"Market.dll")]
public static extern void RetrieveMD(ref stMD[] arrayMD);

RetrieveMD(ref arrayMD);

3

Решение

Проблема заключается в определении вашей точки входа в DLL:

void __stdcall RetrieveMD (stMDP *LatestMD [])

Вы не указываете размер массива, так как же C # должен знать, сколько элементов было скопировано в него? Это проблема и в других языках. Ваша реализация просто предполагает, что предоставленная память достаточно велика, чтобы содержать aryMD. Но что, если это не так? Вы только что создали переполнение буфера.

Если вы хотите, чтобы вызывающая сторона выделяла массив, то вызывающая сторона также должна передать количество элементов, содержащихся в массиве.

редактировать

Объявление C ++ должно выглядеть примерно так:

// On input, length should be the number of elements in the LatestMD array.
// On output, length should be the number of valid records copied into the array.
void __stdcall RetrieveMD( stMDP * LatestMD, int * length );

Объявление C # будет выглядеть примерно так:

[DllImport(@"Market.dll")]
public static extern void RetrieveMD(
[In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] ref stMD[] arrayMD,
[In, Out] ref int length);
2

Другие решения

Думаю, ваша проблема в том, что вы пытаетесь передать массив по ссылке из C # в C, и вам почти никогда не нужно этого делать. В вашем случае все, что вы хотите, — это чтобы C # выделил память для 100 ваших структур, а затем передал эту память C для заполнения. В вашем случае вы передаете указатель на массив структур, который является дополнительным уровнем косвенности, который вам на самом деле не нужен.

Вы редко видите функции C, которые принимают массив фиксированного размера; вместо этого ваша функция обычно определяется на C, чтобы принимать указатель и длину, например что-то вроде:

void __stdcall RetrieveMD (stMDP *LatestMD, int size)
{
int count = 0;
if (size < sizeof(aryMD))
count = size;
else
count = sizeof(aryMD);

memcpy(LatestMD, aryMD, count);
}

Чтобы вызвать этот метод из C #, вам нужно сделать две вещи. Во-первых, вам нужно выделить массив соответствующего размера для передачи. Во-вторых, вам нужно сообщить код маршалинга (который выполняет копирование C # -> C) как выяснить сколько данных нужно маршалировать через [MarshalAs] атрибут:

[DllImport(@"Market.dll")]
public static extern void RetrieveMD (
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] stMDP[] arrayMD,
int length
);

var x = new stMDP[100];
RetrieveMD(x, 100);
0

Я получил это работает. Я когда-нибудь пытался сделать это сложнее, чем есть.

Я перечитал главу 2 «Рецепты взаимодействия .NET 2.0: подход к решению проблем».

Вот что работает.

На стороне C ++ я убрал указатели

struct stMD
{
float Price;
unsigned int PriceDir;
unsigned int PriceDirCnt;
};

// Global memory
#pragma data_seg (".IPC")
bool NewPoint = false; // Flag used to signal a new point.
static stMD aryMD [100] = {{0}};
#pragma data_seg()

void __stdcall RetrieveMD (stMD LatestMD [100])
{
memcpy(LatestMD, aryMD, sizeof(aryMD));
}

На стороне C # я удалил оба (ref) s и добавил [In, Out]

[StructLayout(LayoutKind.Sequential)]
public struct stMD
{
public float Price;
public uint PriceDir;
public uint PriceDirCnt;
};

public static stMD[] arrayMD = new stMD[100];

[DllImport(@"Market.dll")]
public static extern void RetrieveMD([In, Out] stMD[] arrayMD);

RetrieveMD(arrayMD);

Спасибо всем, кто предложил помощь.

0
По вопросам рекламы [email protected]