Convert byte array to char* in a clr wrapper
-
Thursday, April 26, 2012 2:35 PM
I have written a C++ wrapper function in CLR that calls a C# .dll to use in unmanaged C++. After calling the C# .dll, I want to copy (Marshal) the data into a struct to use in the unmanaged C++. Here is what I have so far and have verified it works:
TCHAR *pTemp; /* Copy in the Id */ pTemp = (TCHAR *)(Marshal::StringToHGlobalUni(cSharpClass->id->ToString())).ToPointer(); structure->strId = pTemp; Marshal::FreeHGlobal(IntPtr((void *)pTemp)); /* Copy in the Store Name */ pTemp = (TCHAR *)(Marshal::StringToHGlobalUni(cSharpClass->storeName->ToString())).ToPointer(); structure->strStoreName = pTemp; Marshal::FreeHGlobal(IntPtr((void *)pTemp)); /* Copy in the Name */ pTemp = (TCHAR *)(Marshal::StringToHGlobalUni(cSharpClass->name->ToString())).ToPointer(); structure->strName = pTemp; Marshal::FreeHGlobal(IntPtr((void *)pTemp));
The last thing I have to copy to the structure is a C# byte[]. I do not have a strong opinion on which type to copy (Marshal) the byte[] to. Does anyone have any suggestions on how I could get this to work? Or is there anything that I wasn't clear enough? Thanks in advance!
All Replies
-
Friday, April 27, 2012 8:28 AMModerator
Hi Biglato,
Welcome to the MSDN Forum.
Based on your description, it seems that you need to copy a C# byte[] to a C++ char[] , am I right?
Please take a look at this thread: http://stackoverflow.com/questions/289076/how-can-i-pass-a-pointer-to-an-array-using-p-invoke-in-c
It provides a way to pass a byte[] to a char* in a C API. It may give you some ideas.
Best regards,
Mike Feng
MSDN Community Support | Feedback to us
Please remember to mark the replies as answers if they help and unmark them if they provide no help.
-
Friday, April 27, 2012 12:57 PMI'm not trying to pass the char* to the C# .dll. I have received the byte array from the managed C# and need to get it to unmanaged C++. Your suggestion is doing the opposite.
-
Saturday, April 28, 2012 1:18 PM
Hello BigLato,
1. Will the unmanaged structure contain a member field that indicates the number of elements in the byte array member field ?
2. Or is the byte array of a fixed number of elements ?
3. If there is no field that indicates the number of elements in the array, and the array is not fixed in length, then SAFEARRAY would be the best type to use because an instance of a SAFEARRAY will internally contain information on the number of elements it contains.
4. BTW, I am also curious about the type of the strId, strStoreName and strName member fields :
4.1 What type are they ?
4.2 How are they able to hold an unmanaged string which is later deleted via Marshal::FreeHGlobal() ?
4.3 Are they STL strings ?
4.4 I am just curious.
- Bio.
Please visit my blog : http://limbioliong.wordpress.com/
-
Saturday, April 28, 2012 11:00 PM
No, there is not a field that will indicate the number of elements in the array nor will it be fixed. I haven't ever worked with a SAFEARRAY but can give it a try and report back.
To try and answer your other questions, strId, strStoreName, and strName are of the type std::basic_string<TCHAR>. pTemp is holding the after it is marshaled to global memory. Then it is assigned to its respective member field. Then pTemp is released.
Is what I'm saying making sense? I can try to be clearer about something or post more code if that would be easier. Otherwise, I will try the SAFEARRAY and let you know how it worked. Thanks!
-
Sunday, April 29, 2012 1:27 AM
Hello BigLato,
>> No, there is not a field that will indicate the number of elements in the array nor will it be fixed. I haven't ever worked with a SAFEARRAY but can give it a try and report back...strId, strStoreName, and strName are of the type std::basic_string<TCHAR>...
1. Just as I thought : strId, strStoreName, and strName are indeed STL strings (as defined in the <string> header file). However, since you used Marshal::StringToHGlobalUni() to convert the managed strings to unmanaged Unicode strings, I would personally declare them as std::wstring.
2. In this case, I think it would be better if the byte array member field of the unmanaged structure be declared as of type std::vector<unsigned char>. It would be more suitable since the other fields use the STL type string/wstring.
3. Here is how I have defined the unmanaged structure in my test managed C++ code :
#include <string> #include <vector> struct Structure { std::wstring strId; std::wstring strStoreName; std::wstring strName; std::vector<unsigned char> byArray; };4. The following is how I have defined the managed equivalent of the above structure in C# :
public class CSharpClass { public int id; public string storeName; public string name; public byte[] byArray; }5. The following is a test API that I created in managed C++ to be called in an unmanaged C++ application :
extern "C" __declspec(dllexport) void __stdcall WrapperCallManagedClassMethod(Structure* structure) { CSharpClass^ cSharpClass = gcnew CSharpClass; cSharpClass -> id = 100; cSharpClass -> storeName = "My Store Name"; cSharpClass -> name = "Name"; cSharpClass -> byArray = gcnew array<Byte>(10); for (int i = 0; i < (cSharpClass -> byArray) -> Length; i++) { (cSharpClass -> byArray)[i] = (Byte)i; } TCHAR *pTemp; /* Copy in the Id */ pTemp = (TCHAR *)(Marshal::StringToHGlobalUni(cSharpClass->id.ToString())).ToPointer(); structure->strId = pTemp; Marshal::FreeHGlobal(IntPtr((void *)pTemp)); /* Copy in the Store Name */ pTemp = (TCHAR *)(Marshal::StringToHGlobalUni(cSharpClass->storeName->ToString())).ToPointer(); structure->strStoreName = pTemp; Marshal::FreeHGlobal(IntPtr((void *)pTemp)); /* Copy in the Name */ pTemp = (TCHAR *)(Marshal::StringToHGlobalUni(cSharpClass->name->ToString())).ToPointer(); structure->strName = pTemp; Marshal::FreeHGlobal(IntPtr((void *)pTemp)); // Initialize the size of the byte vector. (structure -> byArray).resize((cSharpClass -> byArray) -> Length); unsigned char* pByte = (unsigned char*)(&((structure -> byArray)[0])); // Use Marshal::Copy() to copy the byte data from // the managed byte array to our vector of unsigned char. Marshal::Copy ( cSharpClass -> byArray, 0, (IntPtr)pByte, (cSharpClass -> byArray) -> Length ); }The code above contained the code you have included in the OP. The last lines show how I have transformed the managed "byArray" into an unmanaged vector<unsigned char>.
- Bio.
Please visit my blog : http://limbioliong.wordpress.com/
- Marked As Answer by BigLato Monday, April 30, 2012 8:21 PM
-
Sunday, April 29, 2012 1:46 AM
Hello BigLato,
A few more points :
1. The std::vector also intrinsically contains information on the number of elements it holds, use : vector::size() to determine this.
2. In my example code, I first prepared the vector<unsigned char> to be able to contain the number of elements that are contained in the managed byte array :
// Initialize the size of the byte vector. (structure -> byArray).resize((cSharpClass -> byArray) -> Length);
3. Then I used the address of the first element of the vector as the target address for the later call to Marshal::Copy() :
unsigned char* pByte = (unsigned char*)(&((structure -> byArray)[0]));
Note that this (referencing vector[0]) is only possible provided the vector has been sized. The other way is to actually make the vector contain a value in vector[0] (e.g. by using push_back() to insert a value).
- Bio.
Please visit my blog : http://limbioliong.wordpress.com/
-
Monday, April 30, 2012 4:43 AM
See if this code helps. I got this from an old project. Converts C# byte array to std::string:
void Convert(cli::array<unsigned char> ^arr) { char *c_arr = NULL; try { c_arr = new char[arr->Length + 1]; System::IntPtr c_arr_ptr(c_arr); // copy from .NETs unsigned char array to char array System::Runtime::InteropServices::Marshal::Copy(arr, 0, c_arr_ptr, arr->Length); // set last char as null c_arr[arr->Length] = -1; // create a std::string buffer with the data string buf(&c_arr[0], &c_arr[arr->Length]); } finally { if(c_arr != NULL) delete c_arr; } }Click the 'Vote as Helpful' arrow if this post was helpful.
-
Monday, April 30, 2012 8:24 PMBio, it worked perfectly. I had been playing around with the Marsha::Copy but couldn't ever get it working because I didn't prepare teh vector correctly and I would get errors about it being NULL. Thank you so much!!!

