2013-03-05 3 views
0

Я пытаюсь реализовать некоторый проект, включающий маршалинг массивов между управляемыми 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-объектами, но я должен избегать использования этого из-за проблем с переносимостью.

Заранее спасибо.

ответ

4

Параметр Method_Unknown_Size: double* и вы меняете параметр сам. Если вы хотите изменить исходное значение отправить вызывающий, Вы должны определить параметр как указателя на массив что означает указателя на указатель двойного или ссылку на указатель двойного

Вы должны также в некотором роде, сообщите вызывающему размеру массива (думаю, вы уже это сделали).

C++:

int method_Unknown_Size(double *&array) 
{ 
    int size = 9; 
    array = new double[size]; 
    for(int i=0; i<size; i++) array[i] = i; 
    return size; 
} 

void FreeArray(double *&p) 
{ 
    delete[] p; 
    p = NULL; 
} 

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); 
} 

C#:

[DllImport("cObject.dll")] 
private static extern int Run_Unknown_Size(IntPtr pObject, 
     out IntPtr arr_not_allocated); 

[DllImport("cObject.dll")] 
private static extern int Release(IntPtr pObject, 
     ref IntPtr arr_not_allocated); 


// to allocate the array: 
IntPtr pArr; 
int res2 = Run_Unknown_size(m_pNativeObject, out pArr); 

// to free the array: 
Release(m_pNativeObject, ref pArr); 

Это абсолютно работает! Скажи мне, если это не так!

+0

большое спасибо за быстрый ответ. У меня есть этот код на моей рабочей машине, так что завтра я обязательно это проверю. Я пробовал двойной указатель, но несколько иначе. Надеюсь, это сработает! Просто быстрый вопрос: в части C# вы добавляете атрибуты «Out out». Что это значит? –

+0

Добро пожаловать. Я предполагаю, что '[Out]' здесь не требуется, но я уверен в этом. Он сообщает маршаллеру вернуть изменения в переменную, но он работает только для содержимого объектов и массивов. Если объект * сам * изменяется, '' [Out] 'не поможет! Здесь используется модификатор параметра 'out'. Он обрабатывает передаваемый параметр как * reference * (или * указатель *). См. [Здесь] (http://msdn.microsoft.com/en-us/library/ee332485.aspx) для получения дополнительной информации. –

+0

Это очень интересно. Это мой первый проект с управляемым/неуправляемым кодом, и вы определенно открыли мне глаза на некоторые вещи, которые нелегко найти в Интернете или в учебниках. Еще раз спасибо! –