Convert byte array to char* in a clr wrapper

本文探讨了从C#向C++传递字符串及字节数组的方法,使用了标准模板库(STL)的数据结构,并通过Marshal类实现了托管与非托管代码间的互操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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!

Answers

>> 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/


////////////////////////////////////////////////////////////////////////////////////////////////////////////////

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.

////////////////////////////////////////////////////////////////////////////////////////////////////////////////

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).

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

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


转自: http://social.msdn.microsoft.com/Forums/no/clr/thread/82614307-f1d9-4f30-8418-541305d5f86c
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值