locked
P/Invoke Returning an Byte[] from C++ to C# RRS feed

  • Question

  • I'd appreciate any help, all the threads and articles I've searched say this should be possible.

    Here is a simplified example, I need to pass a string to C++ method and get back a byte[]. Upon reallocation I get an access violation. 2nd parameter is actual size newly allocated byte array.

    I have tried several variations wth and with out ref on Byte[] (Changing C++ accordingly), tried specifying sizeParameterIndex=2. If I have to I will break the call into 2, 1) to get required size and 2) to get updated buffer. The actual C++ is legacy code, so I'm specifying charset and calling convention.
     
    C#
    class Test
    {
     static
    void TestUnManaged()
     {
     int l_Ret=0;
     int BuffSize=0;
     Byte[] labData = null;

        l_Ret = TestAllocate("Fred", ref BuffSize, ref labData);
        
    return;
     }

     [DllImport("MvvTestUnManaged.dll", CharSet = CharSet.Ansi, CallingConvention =   CallingConvention.Cdecl, SetLastError = true)]
     public static extern int TestAllocate(String Data, ref int BufSize, [In, Out, MarshalAs(UnmanagedType.LPArray)] ref Byte[] Buffer);
    }

    C++

    extern "C"
    {
     EXPORT_API int
     TestAllocate(LPCSTR Data, int *BufSize, LPVOID *Buffer)
     {
     int lnRet = 0;

      if(Data != NULL)
      {
      size_t BfSize = strlen(Data);
      size_t AllocSize = BfSize + 1;

       *BufSize = BfSize;
       *Buffer = (LPVOID)::HeapAlloc(NULL, 0L, AllocSize);
       memset(*Buffer, AllocSize, 0x00);
       memcpy(*Buffer, Data, BfSize);
      }
      return(lnRet);
     }
    }

    Monday, March 16, 2009 7:39 PM

Answers

  • I think you'll have to do your own custom marshalling here :-

        __declspec( dllexport ) int   TestAllocate(LPCSTR Data, int *BufSize, LPVOID* Buffer)  
        {  
            int lnRet = 0;  

            if(Data != NULL)  
            {  
                size_t BfSize = strlen(Data);  
                size_t AllocSize = BfSize + 1;  

                *BufSize = BfSize;  
                *Buffer =  ::HeapAlloc(GetProcessHeap(), 0L, AllocSize);

                memset(*Buffer, AllocSize, 0x00);  
                memcpy(*Buffer, Data, BfSize)  ;
            }  

            return(lnRet);  
        } 

    and

            [DllImport("TestDLL.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
            public static extern int TestAllocate(String Data, ref int BufSize, out IntPtr Buffer); 

            static void Main(string[] args)
            {
                IntPtr buffer = IntPtr.Zero;
                int size = 0;
                TestAllocate("hello world", ref size, out buffer);

                Byte[] byteArray = new Byte[size];
                Marshal.Copy(buffer, byteArray, 0, size);

            }

    http://blog.voidnish.com
    • Proposed as answer by Harry Zhu Monday, March 23, 2009 11:14 AM
    • Marked as answer by Harry Zhu Tuesday, March 24, 2009 1:06 AM
    Tuesday, March 17, 2009 7:01 PM
    Moderator
  •  

    Hi,

     

    Did you mean when the proptype is  [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] Byte[] , an error will arise? What did you mean re-allocate? Is it HeapAlloc?

     

    The first parameter of heapAlloc is a handle to the heap from which the memory will be allocated. So please try to alloc memory using ::HeapAlloc(GetProcessHeap(), 0L, AllocSize) as Nishant suggested.

     

    The buffer is allocated by the marshaler using CoTaskMemAlloc. While buffer is set to point to the memory allocated by HeapAlloc, the memory it pointed to will leak.

    When the method return , the buffer which is allocated by HeapAlloc will not be marshaled back , so the c# method can’t get the value.

     

    Please try to check out the msdn sample code about pinvoke which has show how to marshal array properly.

     

    Best regards,

    Harry


    http://cfx.codeplex.com where you can find samples you are looking for
    • Proposed as answer by Harry Zhu Monday, March 23, 2009 11:14 AM
    • Marked as answer by Harry Zhu Tuesday, March 24, 2009 1:06 AM
    Friday, March 20, 2009 12:47 PM

All replies

  • You need to specify SizeParamIndex to 1 (not 2) on your array:

    [DllImport("MvvTestUnManaged.dll", CharSet = CharSet.Ansi, CallingConvention =   CallingConvention.Cdecl, SetLastError = true)]
    public static extern int TestAllocate(String Data, ref int BufSize, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] ref Byte[] Buffer);

         -Steve
    Tuesday, March 17, 2009 3:27 PM
  • Sorry the 2 was a typo.
    Found out you can't use a SizeParamIndex with a ByRef type.


    If I take the ref off Byte[], then I get an access violation when I re-allocate.
    "An unhandled exception of type 'System.AccessViolationException' occurred in TestConversion.exe"

    C#

    [DllImport("MvvTestUnManaged.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]  
    public static extern int TestAllocate(String Data, ref int BufSize, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] Byte[] Buffer);  
     

    C++
    EXPORT_API int   
    TestAllocate(LPCSTR Data, int *BufSize, LPVOID Buffer)   
    {   
    int lnRet = 0;   
       
       if(Data != NULL)   
       {   
       size_t BfSize = strlen(Data);   
       size_t AllocSize = BfSize + 1;   
       
          *BufSize = BfSize;   
          if(Buffer == NULL)   
             Buffer = (LPVOID)::HeapAlloc(NULL, 0L, AllocSize);   
          memset(Buffer, AllocSize, 0x00);   
          memcpy(Buffer, Data, BfSize)   
       }   
       return(lnRet);   
    }   
     

    Thanks,
    Mike
    Tuesday, March 17, 2009 5:16 PM
  • I think you'll have to do your own custom marshalling here :-

        __declspec( dllexport ) int   TestAllocate(LPCSTR Data, int *BufSize, LPVOID* Buffer)  
        {  
            int lnRet = 0;  

            if(Data != NULL)  
            {  
                size_t BfSize = strlen(Data);  
                size_t AllocSize = BfSize + 1;  

                *BufSize = BfSize;  
                *Buffer =  ::HeapAlloc(GetProcessHeap(), 0L, AllocSize);

                memset(*Buffer, AllocSize, 0x00);  
                memcpy(*Buffer, Data, BfSize)  ;
            }  

            return(lnRet);  
        } 

    and

            [DllImport("TestDLL.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
            public static extern int TestAllocate(String Data, ref int BufSize, out IntPtr Buffer); 

            static void Main(string[] args)
            {
                IntPtr buffer = IntPtr.Zero;
                int size = 0;
                TestAllocate("hello world", ref size, out buffer);

                Byte[] byteArray = new Byte[size];
                Marshal.Copy(buffer, byteArray, 0, size);

            }

    http://blog.voidnish.com
    • Proposed as answer by Harry Zhu Monday, March 23, 2009 11:14 AM
    • Marked as answer by Harry Zhu Tuesday, March 24, 2009 1:06 AM
    Tuesday, March 17, 2009 7:01 PM
    Moderator
  •  

    Hi,

     

    Did you mean when the proptype is  [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] Byte[] , an error will arise? What did you mean re-allocate? Is it HeapAlloc?

     

    The first parameter of heapAlloc is a handle to the heap from which the memory will be allocated. So please try to alloc memory using ::HeapAlloc(GetProcessHeap(), 0L, AllocSize) as Nishant suggested.

     

    The buffer is allocated by the marshaler using CoTaskMemAlloc. While buffer is set to point to the memory allocated by HeapAlloc, the memory it pointed to will leak.

    When the method return , the buffer which is allocated by HeapAlloc will not be marshaled back , so the c# method can’t get the value.

     

    Please try to check out the msdn sample code about pinvoke which has show how to marshal array properly.

     

    Best regards,

    Harry


    http://cfx.codeplex.com where you can find samples you are looking for
    • Proposed as answer by Harry Zhu Monday, March 23, 2009 11:14 AM
    • Marked as answer by Harry Zhu Tuesday, March 24, 2009 1:06 AM
    Friday, March 20, 2009 12:47 PM