locked
Fill C# byte array in CPP RRS feed

  • Question

  • Hi!

    I have a c# project which calls a c++ COM dll. The c# app needs to pre allocate a byte array which the c++ lib then fills. The code has the following structure:

    CPP:

    getarray(LPBYTE byte, LONG size, LONG *ret) {

    for (int i = 0; i < 50; i++)

    byte[i] = 98;

    *ret = S_OK;

    }

     

    C#:

    byte[] bt = new byte[1000];

    cppclass.getarray(out bt[0], 1000);

     

    The problem is that I'm only getting the first element of the byte array in C#. How can I fix this?

     

     

    Friday, January 28, 2011 2:56 PM

Answers

  • Hello crogger,

     

    1. It is best that you show us the IDL method signature of getarray().

     

    2. However, after observing your code (most notably the fact that the second parameter of getarray() is named "size" which implies the size of the byte array pointed to by the first parameter), I made a few conjectures which may help explain your problem :

    2.1 Is the getarray() method declared something like the following ? :

    [helpstring("method getarray")] HRESULT getarray([out] BYTE* byArrayReceiver, LONG lSizeOfArray, [out, retval] LONG* plRet);

    2.2 If so, then you need to specify the size of the "out" buffer for the bytes to be returned to the client application. This "out" buffer is allocated by the COM proxy/stub marshaler as your getarray() method is invoked at runtime.

    2.3 Modify the getarray() IDL method declaration to the following :

    [helpstring("method getarray")] HRESULT getarray([out, size_is(lSizeOfArray)] BYTE* byArrayReceiver, LONG lSizeOfArray, [out, retval] LONG* plRet);

    This will indicate to the COM proxy/stub marshaler that "byArrayReceiver" must point to a byte array and that the size of this byte array is indicated by the value of the "lSizeOfArray" parameter.

    Without the size_is() attribute, the proxy/stub marshaler assumes that "byArrayReceiver" is simply a pointer to one single byte.

    2.4. In fact, by not using the size_is() attribute, your C++ code runs the big risk of assigning byte values beyond the allocated memory buffer of the "byte" parameter. That is, since the proxy/stub marshaler has only allocated one byte for the "byte" parameter, your current "for" loop has assigned values well beyond this single byte buffer, overwriting memory values along the way and creating the risk of a crash (try changing the number "50" to "1000" and your app may experience a crash).

     

    3. It is also better that the loop be coded as follows :

    for (int i = 0; i < size; i++)  // use "size" instead of a hard-coded number.
    {
      byte[i] = 98;
    }

    This way, you can be sure that you will not assign values beyond the allocated size of the "byte" array.

    - Bio.

     

    • Marked as answer by crogger Monday, January 31, 2011 9:34 AM
    Sunday, January 30, 2011 11:31 AM

All replies

  • How does your DLLImport look like?

    Why are you using "out" instead of "ref"?

    "getarray" in CPP has 3 Parameter, the call in C# has only 2 parameters, how could this work?

    Addionial/Edit:

    You are using only bt[0] as parameter, in this case, only ONE element is marshalled, isn't it?

    • Edited by GreatVolk Friday, January 28, 2011 3:13 PM typo
    Friday, January 28, 2011 3:10 PM
  • "How does you DLLImport look like?"

    The C# application is just a tester; it's a 2nd project included in the same solution. I just referenced the the c++ COM object. Anyway, I've got a number of other functions which are working fine.

     

    "Why are you using "out" instead of "ref"?"

    Because in c++ in the idl section it is referenced as [out] instead of [in,out]. Tried changing that, but the result didn't change.

     

    ""getarray" in CPP has 3 Parameter, the call in C# has only 2 parameters, how could this work?"

    The 3rd parameter is a [out, retval] value. Thus in C#, it will be seen as int ret = getarray(byte, long).

     

    "You are using only bt[0] as parameter, in this case, only ONE element is marshalled, isn't it?"

    I'm not very familiar to marshalling. What do I need to do to marshall all the elements?

    Any help will be greatly appreciated!

    Friday, January 28, 2011 3:26 PM
  • ... Oh You are using IDL. I'm not that familiar with that... For me, it looks like you defined the first parameter as "out byte" instead of "ref byte[]" or just "byte[]". In C#-manners the method should look like "ref byte[]" here.

    The LPBYTE in C++ seemes to be an "out byte", which it isn't. In reality it is an array definition and it is used like this. the C#-"byte[]" is a Managed-C++-Structure of type "array<byte>^", which has to be changed into an C-array on calling, an it has to be changed back on returning. Usually C# is doing that automatically, if it is knowing, that it has an array... That means, your IDL may be wrong (but remember, i'm not familiar with IDL), because C# is not able to recognize the array.

    You can also use the attribute System.Runtime.InteropServices.DllImportAttribute to connect to any DLL... Something like that:

    [DllImport(@"MyDLL.dll")]
    public static extern int getArray(ref byte[] array, int len);
    

    This always works fine on my problems.... 

    Friday, January 28, 2011 3:54 PM
  • Thankyou for the clarification! Unfortunately in idl the format "array<byte>^" seems not to be recognized. Is there a way to keep the C++ declaration as LPBYTE and declare the C# variable as otherwise somehow? The important part is to be able to allocate a number of elements in C# which will be filled in C++ and read in C#. How these elements are read in C# is not that important.

     

     

    Friday, January 28, 2011 4:44 PM
  • The first parameter should be an array byval (pointer to byte), not an array byref (pointer to a pointer to byte).

    • Proposed as answer by Paul Zhou Monday, January 31, 2011 7:12 AM
    Saturday, January 29, 2011 5:05 AM
  • Hello crogger,

     

    1. It is best that you show us the IDL method signature of getarray().

     

    2. However, after observing your code (most notably the fact that the second parameter of getarray() is named "size" which implies the size of the byte array pointed to by the first parameter), I made a few conjectures which may help explain your problem :

    2.1 Is the getarray() method declared something like the following ? :

    [helpstring("method getarray")] HRESULT getarray([out] BYTE* byArrayReceiver, LONG lSizeOfArray, [out, retval] LONG* plRet);

    2.2 If so, then you need to specify the size of the "out" buffer for the bytes to be returned to the client application. This "out" buffer is allocated by the COM proxy/stub marshaler as your getarray() method is invoked at runtime.

    2.3 Modify the getarray() IDL method declaration to the following :

    [helpstring("method getarray")] HRESULT getarray([out, size_is(lSizeOfArray)] BYTE* byArrayReceiver, LONG lSizeOfArray, [out, retval] LONG* plRet);

    This will indicate to the COM proxy/stub marshaler that "byArrayReceiver" must point to a byte array and that the size of this byte array is indicated by the value of the "lSizeOfArray" parameter.

    Without the size_is() attribute, the proxy/stub marshaler assumes that "byArrayReceiver" is simply a pointer to one single byte.

    2.4. In fact, by not using the size_is() attribute, your C++ code runs the big risk of assigning byte values beyond the allocated memory buffer of the "byte" parameter. That is, since the proxy/stub marshaler has only allocated one byte for the "byte" parameter, your current "for" loop has assigned values well beyond this single byte buffer, overwriting memory values along the way and creating the risk of a crash (try changing the number "50" to "1000" and your app may experience a crash).

     

    3. It is also better that the loop be coded as follows :

    for (int i = 0; i < size; i++)  // use "size" instead of a hard-coded number.
    {
      byte[i] = 98;
    }

    This way, you can be sure that you will not assign values beyond the allocated size of the "byte" array.

    - Bio.

     

    • Marked as answer by crogger Monday, January 31, 2011 9:34 AM
    Sunday, January 30, 2011 11:31 AM
  • Thx Lim! The "size_is" did the trick!

    Monday, January 31, 2011 9:35 AM