2013-03-05 4 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# 1 메모리에 의해 할당되고, 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#에서 당신은 속성을 넣어 [아웃] '. 무슨 뜻이에요? –

+0

당신을 진심으로 환영합니다. 나는 여기서 [Out]이 필요 없다고 생각하지만, 나는 그것을 확실하게 넣는다. 이것은 마샬 러에게 변수에 대한 변경 사항을 다시 알려주지 만 객체 및 배열의 ​​내용에 대해서만 작동합니다. 객체 * 자체가 변경되면'[Out]'이 도움이되지 않습니다! 여기에'out' 매개 변수 수정자가 사용되는 곳이 있습니다. 패스를 * reference * (또는 * pointer *)와 같은 매개 변수로 지정합니다. 자세한 내용은 [여기] (http://msdn.microsoft.com/en-us/library/ee332485.aspx)를 참조하십시오. –

+0

매우 흥미 롭습니다. 이것은 관리/비 관리 코드와 관련된 첫 번째 프로젝트이며 웹이나 교과서에서 쉽게 찾을 수없는 몇 가지 사항에 눈을 떴습니다. 다시 한 번 감사드립니다! –

관련 문제