Работа с массивом IntPtr - PullRequest
2 голосов
/ 06 января 2011

Я думаю, что я близок, и держу пари, что это глупое решение.

У меня есть собственная C ++ DLL, где я определяю следующую функцию:

DllExport bool __stdcall Open(const char* filePath, int *numFrames, void** data);
{
  //creates the list of arrays here... don't worry, lifetime is managed somewhere else

  //foreach item of the list:
  {
      BYTE* pByte = GetArray(i);

      //here's where my problem lives
      *(data + i * sizeofarray) = pByte;
  }
  *numFrames = total number of items in the list
  return true;
}

В основном, учитываяпуть к файлу, эта функция создает список байтовых массивов (BYTE *) и должна возвращать список указателей через параметр данных.Каждый указывает на отдельный байтовый массив.

Я хочу передать массив IntPtr из C # и иметь возможность упорядочить каждый отдельный массив по порядку.Вот код, который я использую:

    [DllImport("mydll.dll",EntryPoint = "Open")]
    private static extern bool MyOpen(
      string filePath, out int numFrames, out IntPtr[] ptr);

    internal static bool Open(
      string filePath, out int numFrames, out Bitmap[] images)
    {
        var ptrList = new IntPtr[512];

        MyOpen(filePath, out numFrames, out ptrList);

        images = new Bitmap[numFrames];
        var len = 100; //for sake of simplicity
        for (int i=0; i<numFrames;i++)
        {
            var buffer = new byte[len];
            Marshal.Copy(ptrList[i], buffer, 0, len);

            images[i] = CreateBitmapFromBuffer(buffer, height, width);
        }

        return true;
    }

Проблема в моем коде C ++.Когда я назначаю * (data + i * sizeofarray) = pByte;он портит массив указателей ... что я делаю не так?

ОБНОВЛЕНИЕ: Только начал создавать новое решение для изоляции понятий и уже нашел что-то очень странное.Взгляните:

C # код

class Program
{
    [DllImport("ArrayProvider.dll")]
    private static extern bool Open(out int n, ref IntPtr[] ptr);


    static void Main(string[] args)
    {
        int n;

        var pList = new IntPtr[10];

        Program.Open(out n, ref pList);

        foreach (var p in pList)
        {
            Debug.WriteLine(p.ToInt32().ToString("X"));
        }
    }
}

C ++ код

#include "stdafx.h"
#define DllExport   __declspec( dllexport )

extern "C" {

DllExport bool __stdcall Open(int *n, void** data)
{
return true;
}

}

До вызова нативного кода, pList имеет 10 элементов IntPtr.Zero.После возврата из нативного вызова у него есть только одно ... что-то не так ... и это также происходит, если я заменю void ** на BYTE **

Ответы [ 4 ]

4 голосов
/ 06 января 2011

Среда выполнения не будет иметь никакого представления о длине массива при получении неуправляемых массивов, вам нужно использовать MarshalAsAttribute в сочетании с полем SizeParamIndex , чтобы указать длину массив. Вот пример этого здесь .

Вам также не нужно определять сигнатуру метода как ref IntPtr [] для массива, используйте InAttribute и OutAttribute .

E.x:.

private static extern bool Open(out int n,[MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)] [In,Out] IntPtr[] ptr);
2 голосов
/ 06 января 2011

Как вы можете написать это, где data is void* type:

 //you cannot do such pointer arithmetic on void* type.
 *(data + i * sizeofarray) = pByte; 

Измените это на тип char* или int*.

Кроме того, используйте ref вместо out для последнего аргумента здесь:

MyOpen(filePath, out numFrames, out ptrList); //wrong

Это исправлено:

MyOpen(filePath, out numFrames, ref ptrList); //correct

Вы должны соответствующим образом изменить сигнатуру метода.

2 голосов
/ 06 января 2011

Попробуйте изменить

out IntPtr[] ptr

до

ref IntPtr[] ptr

Существует небольшая разница между 2.

Обновление:

Попробуйте:

data[i] = pByte
1 голос
/ 06 января 2011

I думаю , что вы хотите, чтобы

*(data + i * sizeofarray) = pByte;

было чем-то более похожим на

data[i] = pByte;

Так что вы будете хранить массив указателей в любом data указывает на.

...