Я пытаюсь реализовать некоторый проект, включающий маршалинг массивов между управляемыми C# и неуправляемыми кодами C++. Я столкнулся с проблемой, и ни одно из решений, найденных мной в Интернете, похоже, не работает. Буду признателен за любые замечания по этому вопросу.Выделение массива в неуправляемом коде C++ после передачи указателя IntPtr в C#, типы маршалинга
Я не представляю полный код, но очень упрощенную часть, показывающую проблему. Хотя это выглядит как большая часть - это очень просто - просто концептуально. Просто хотел дать как можно больше полной картины.
C++, часть:
Object.h
class cObject
{
public:
//...constructor, destructor...
int Method_Known_Size(double* array, int size);
int Method_Unknown_Size(double* array);
...
void FreeArray(double* p);
}
Object.cpp
int Method_Known_Size(double* array, int size)
{
//modify array somehow..
for(int i=0; i<size; i++) array[i] = i;
}
int method_Unknown_Size(double* array)
{
int size = 9;
array = new double[size];
for(int i=0; i<size; i++) array[i] = i;
}
(пропуск Caller.h) Caller.cpp
//...callers for constructor, destructor, for releasing unmanaged memory...
extern "C" int __stdcall Run_Known_Size(cObject* pObject, double* array, int size)
{
return cObject->Method_Known_Size(array, size);
}
extern "C" int __stdcall Run_Unknown_Size(cObject* pObject, double* array)
{
return cObject->Method_Unknown_Size(array);
}
extern "C" void __stdcall Release(cObject* cObject, double* array)
{
if(cObject != NULL) cObject->FreeArray(array);
}
Таким образом, в основном Run_Known_Size
метод просто модифицирует уже выделено C# памяти и Run_Unknown_Size
создает массив и изменяет его.
C# часть
public class DllWrapper: IDisposable
{
/* Creating an object, disposing,...
[DllImport("cObject.dll")]
CreateObject();...DisposeObject(IntPtr pObject);
...CallFreeArray(IntPtr pArray);*/
[DllImport("cObject.dll")]
private static extern int CallRun_Known_Size(IntPtr pObject,
[Out] double [] arr_allocated, int size);
[DllImport("cObject.dll")]
private static extern int CallRun_Unknown_Size(IntPtr pObject,
[Out] IntPtr arr_not_allocated);
private IntPtr m_pNativeObject;
public DllWrapper()
{
this.m_pNativeObject = CreateObject();
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool bDisposing)
{
if (this.m_pNativeObject != IntPtr.Zero)
{
DisposeObject(this.m_pNativeObject);
this.m_pNativeObject = IntPtr.Zero;
}
if (bDisposing)
{
GC.SuppressFinalize(this);
}
}
~DllWrapper()
{
Dispose(false);
}
public void ReleaseUnmanAraray(IntPtr pArr)
{
CallFreeArray(pArr);
}
public int Run_Known_Size(double[] arr_allocated, int size)
{
return CallRun_Known_Size(this.m_pNativeObject, arr_allocated, size);
}
public int Run_Unknown_Size(IntPtr arr_not_allocated)
{
return CallRun_Known_Size(this.m_pNativeObject, arr_not_allocated);
}
}
static void Main(string[] args)
{
double[] alloc_arr = new double[] { 1, 5, 3, 3, 5, 5, 8, 9,1 };
int size = 9;
double[] Arr_for_Copy = new double[size];
IntPtr pArr = new IntPtr();
DllWrapper wrapper = new DllWrapper();
int res1 = Run_Known_Size(alloc_arr, size);
int res2 = Run_Unknown_size(pArr);
if (pArr != IntPtr.Zero) // pArr IS ZERO ALWAYS!!!!!!
{
Marshal.Copy(pArr, Arr_for_Copy, 0, size);
}
else
{
Console.WriteLine("Pointer was zero again");
}
wrapper.ReleaseUnmanAraray(pScores);
wrapper.Dispose();
Console.ReadLine();
}
Все прекрасно работает с массивами, выделенных в C# - они приходят редактировался C++ без ошибок. Но в случае, если я не знаю размер массива, поэтому не мог предварительно распределять массивы, единственным решением, которое я нашел, является передача [Out] IntPtr и управление C++ с помощью памяти, распределение и изменение массива. Затем возвращаемый IntPtr может быть маршалирован в массив C# double [], потому что мы уже знаем размер (для упрощения я просто поместил число 4 в качестве размера, но я передаю размер int * для определения размера).
Все мои испытания заканчиваются нулевым указателем (без ошибок) после передачи IntPtr и создания массива в C++ на основе этого указателя.
Я видел решения, связанные с COM-объектами, но я должен избегать использования этого из-за проблем с переносимостью.
Заранее спасибо.
большое спасибо за быстрый ответ. У меня есть этот код на моей рабочей машине, так что завтра я обязательно это проверю. Я пробовал двойной указатель, но несколько иначе. Надеюсь, это сработает! Просто быстрый вопрос: в части C# вы добавляете атрибуты «Out out». Что это значит? –
Добро пожаловать. Я предполагаю, что '[Out]' здесь не требуется, но я уверен в этом. Он сообщает маршаллеру вернуть изменения в переменную, но он работает только для содержимого объектов и массивов. Если объект * сам * изменяется, '' [Out] 'не поможет! Здесь используется модификатор параметра 'out'. Он обрабатывает передаваемый параметр как * reference * (или * указатель *). См. [Здесь] (http://msdn.microsoft.com/en-us/library/ee332485.aspx) для получения дополнительной информации. –
Это очень интересно. Это мой первый проект с управляемым/неуправляемым кодом, и вы определенно открыли мне глаза на некоторые вещи, которые нелегко найти в Интернете или в учебниках. Еще раз спасибо! –