В приведенном ниже примере dataArray, определенный в C++, работает, если он определен как массив, но не как указатель (просто выводит мусорные данные). Есть ли другой способ маршалировать массив C#, чтобы он считывал указатель как массив?
C#
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct CSharpFoo{
int alpha;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5]
public int[] dataArray;
int beta;
}
C++
struct CPPFoo{
int alpha;
//int* dataArray; //Doesn't work, even though initialized to an array elsewhere
int dataArray[5];
int beta;
}
Проходит через функцию вроде этой
C#
[DllImport("MyDll.dll", CallingConvention = CallingConvention.Cdecl, BestFitMapping = false, ThrowOnUnmappableChar = true)]
public extern static bool InitializeDLL([MarshalAs(UnmanagedType.FunctionPtr)] ResultCallback callbackPointer);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void ResultCallback(CSharpFoo value);
C++
//Callback
typedef void(__stdcall * ResultCallback)(CPPFoo);
__declspec(dllexport) bool InitializeDLL(ResultCallback callback);
Заранее спасибо!
Редактировать:: Потому что "инициализирован массивом в другом месте" неясно:
CPPFoo(int dummy){ //Constructor
alpha = 32;
dataArray = new int[5];
for (int i = 0; i < 5; i++){
dataArray[i] = i;
}
beta = 13;
}
//dataArray C++ {0,1,2,3,4}
//alpha C# 32
//dataArray C# {Total random garbage} (dataArray[3] is 13!)
//beta C# 0
PS, структура CPPFoo - это сложная структура, полученная из библиотеки DLL, поэтому я не могу ее изменить. На данный момент, чтобы все заработало, я копирую его в более подходящий массив, как в ответе NonCreature0714, но это приводит к тому, что все данные копируются - дважды. Я стараюсь избегать этой двойной копии.
Другое редактирование: Хотя кажется, что для структуры, содержащей один массив, значения передаются правильно, для сложной структуры выбрасывается мусор. Я обновил код, чтобы отразить более сложную структуру!
Мусорные данные. Сейчас буду редактировать
int* dataArray; - Для нас это мусор. Мы не знаем, на что указывает этот указатель, поскольку он неинициализирован. Возможно, то, что вы думали, это локальная переменная (поэтому больше не существует).
Инициализирован массивом в другом месте? Я получаю его из отдельной DLL, и в документации он упоминается как указатель на массив.
Я считаю, что он все равно не удастся, даже если вы объявите его 'int * dataArray = new int [5]'
int* dataArray = new int[5]. Это динамически создает массив неинициализированный из 5 целых чисел. Итак, как узнать, работает ли это, если вы ничего не устанавливаете для элементов?
Извините, привел пример. Я точно знаю, что DLL возвращает действительные данные и что данные в полном порядке до момента перехода от C++ к C#.
@ EmrahSüngü Извините, я использую DllImport, его просто не было в фиктивном коде. Я добавлю это сейчас
Почему бы просто не использовать vector<int> dataArray; и передать ссылку на вектор?
@ NonCreature0714, потому что это не просто 1 массив, это сложная структура, содержащая данные, полученные из DLL (поэтому я не могу ее изменить)
@Mars, ох, понял, это было мне непонятно. Это важная деталь, не могли бы вы отредактировать свой ответ, чтобы указать это?





Мой опыт работы с C# очень ограничен, но кажется, что он работает с int dataArray [5];, потому что код соответствует типу CSharpFoo и CPPFoo. Размер обоих - пять ints. Когда вы меняете тип на указатель, тогда размер одного указателя составляет пять целых чисел, а другого - одного указателя, который будет меньше пяти целых чисел.
Скорее всего, для передачи массива динамического размера потребуется размер, и для получения указателя вам может потребоваться что-то сделать с небезопасным кодом, но я не пишу на C#, так что это всего лишь предположение.
struct CPPFoo {
int* dataArray = nullptr;
int size = 0;
}
Спасибо. Передача его как массива фиксированного размера не является невозможной, но это означает двойное копирование данных, что далеко не идеально.
Я думал, что вы можете передать адрес исходного массива, поэтому я думал о небезопасном блоке C#. Можно ли присвоить адрес и размер массива. Затем код C++ может передать его в тип vector <int>. Я немного догадываюсь, если честно. Я думаю, что копия нужна только для права собственности. Если копирование не производится, должна быть какая-то гарантия, что массив целых чисел останется до тех пор, пока код C++ не перестанет ссылаться на него.
Вариант для рассмотрения, но в настоящее время unsafe не включен, и я не знаю, разрешит ли это клиент
что-то вроде int [] = new int [size];
Извините, я нажал Enter на последнем комментарии, прежде чем был готов. Я думаю, что [MarshalAs (UnmanagedType.ByValArray, SizeConst = 5] может быть тем, что нужно изменить. Может ли что-то вроде следующего работать на C#? Что происходит без маршала ...? Public struct CSharpFoo {public int [] dataArray; public int dataSize;}
Извините за путаницу, я пытался помочь, но я думаю, что усугубил ситуацию. Я новичок в этой публикации.
Код будет легче понять, если вы вместо этого «отредактируете» свой ответ :)
Но для справки, без маршала приложение вылетает!
Используйте вектор.
#include <vector>
struct CPPFoo {
std::vector<int> dataArray;
};
Вектор более читабелен и безопасен. Только хилый увеличивает размер кода и лишь немного медленнее, чем использование примитивного массива. Вам также не нужно беспокоиться о том, чтобы поддерживать переменную для размера и обновлять ее каждый раз, когда она изменяется.
Я провел пару тестов на своей локальной машине, насколько я понял ваш код. У меня не было проблем со следующим кодом
C# сторона
struct Foo
{
public int alpha;
public IntPtr Data;
public int beta;
public void GetData(ref int[] buffer,int length)
{
Marshal.Copy(Data,buffer,0,length);
}
}
программа класса {
[DllImport("MyPtr.dll",EntryPoint = "InitializeDLL", CallingConvention = CallingConvention.Cdecl)]
public static extern bool InitializeDLL([MarshalAs(UnmanagedType.FunctionPtr)]ResultCallback callbackPointer);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void ResultCallback(ref Foo value);
static void CallBackMe(ref Foo value)
{
var buffer = new int[5];
value.GetData(ref buffer,buffer.Length);
}
static void Main(string[] args)
{
InitializeDLL(CallBackMe);
}
}
Сторона C++
struct CPPFoo {
int* dataArrayPtr;
CPPFoo()
{
dataArrayPtr = new int[5];
for (int i = 0; i < 5; i++) {
dataArrayPtr[i] = i;
}
}
};
typedef void(__stdcall * ResultCallback)(CPPFoo);
extern "C" __declspec(dllexport) bool InitializeDLL(ResultCallback callback)
{
CPPFoo f;
callback(f);
return true;
}
Это интересно! Но это не сработало. Подожди, пока я обновляю вопрос
@Mars, подскажите, пожалуйста, что именно не сработало? У меня этот код работает без проблем
Я обновил код. Пожалуйста, попробуйте еще раз с более сложной структурой :)
@Mars, да, массив стал мусорным значением
@Mars, привет я обновил свой ответ рабочей копией
Хм ... есть ли причина, по которой Foo стал эталоном?
@Mars, может быть, вы правы, может быть, небольшая разница. Я поспешно проверил, прежде чем покинуть офис. Но снимок экрана - это последний код, и он работает. Нет никакой конкретной причины, по которой я сделал это ссылкой. Личные предпочтения, при пинвокинге передаю как ref. С реф работает или без него
Я думаю, что использование IntPtr позволит мне, по крайней мере, избежать копирования вещей один раз на стороне C++, а затем еще раз при переходе с C++ на C#, но на данный момент все работает грубо, поэтому проект двинулся дальше. Я тоже скоро ухожу из офиса, так что проверю позже!
@Mars,then お疲れ様です
お疲れ様です!To you to!
Кстати, ты до чертиков напугал меня, когда я говорю по-японски, ха-ха
@ Марс, я просто нажал на ваш профиль, надеясь получить некоторую информацию о том, где вы живете, потому что вы сказали, что покидаете офис, когда я уходил ? Оказывается, мы оба в Японии
Пожалуйста, определите «не работает».