如何正确编组从Unity到C / C ++的字符串? [英] How to properly marshal strings from Unity to C/C++?

查看:203
本文介绍了如何正确编组从Unity到C / C ++的字符串?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下代码段来自Unities Bonjour客户端示例,该示例演示了如何与C#中的本机代码进行交互。这是一个简单的C函数,它将字符串返回给C#(Unity):

The following code snippet is from Unities Bonjour client example, which demonstrates how to interact with native code from C#. It's a simple C function that returns a string back to C# (Unity):

char* MakeStringCopy (const char* string)
{
    if (string == NULL)
        return NULL;

    char* res = (char*)malloc(strlen(string) + 1);
    strcpy(res, string);
    return res;
}

const char* _GetLookupStatus ()
{
    // By default mono string marshaler creates .Net string for returned UTF-8 C string 
    // and calls free for returned value, thus returned strings should be allocated on heap
    return MakeStringCopy([[delegateObject getStatus] UTF8String]);
}

该函数的C#声明如下:

The C# declaration of the function looks like:

[DllImport ("__Internal")]
private static extern string _GetLookupStatus ();

这里有一些令我困惑的事情:

There are a few things that puzzle me here:


  1. 这是将字符串从iOS本机代码返回到C#的正确方法吗?

  2. 如何释放返回的字符串?

  3. 有更好的方法吗?

对此问题的任何见解表示赞赏。
谢谢。

Any insights in this matter are appreciated. Thank you.

推荐答案

1 .No。

2 。你必须自己动手。

2.You have to do that yourself.

3 。是的

如果在C上的函数内部分配内存或者C ++方面,你必须释放它。我没有看到任何代码分配内存,但我假设你离开了那一部分。另外,不要将堆栈上声明的变量返回给C#。最终会出现未定义的行为,包括崩溃。

If you allocate memory inside a function on the C or C++ side, you must free it. I don't see any code allocating memory on the side but I assume you left that part. Also, do not return a variable declared on the stack to C#. You will end up with undefined behavior including crashes.

这里是一个C ++解决方案。

Here is a C++ solution for this.

对于 C 解决方案:

char* getByteArray() 
{
    //Create your array(Allocate memory)
    char * arrayTest = malloc( 2 * sizeof(char) );

    //Do something to the Array
    arrayTest[0]=3;
    arrayTest[1]=5;

    //Return it
    return arrayTest;
}


int freeMem(char* arrayPtr){
    delete[] arrayPtr;
    free(arrayPtr);
    return 0;
}

唯一的区别是C版本使用 malloc 免费分配和取消分配内存的功能。

The only difference is that the C version uses malloc and free function to allocate and de-allocate memory.

C#

[DllImport("__Internal", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr getByteArray();

[DllImport("__Internal", CallingConvention = CallingConvention.Cdecl)]
public static extern int freeMem(IntPtr ptr);

//Test
void Start() 
{
 //Call and return the pointer
 IntPtr returnedPtr = getIntArray();

 //Create new Variable to Store the result
 byte[] returnedResult = new byte[2];

 //Copy from result pointer to the C# variable
 Marshal.Copy(returnedPtr, returnedResult, 0, 2);

 //Free native memory
 freeMem(returnedPtr);

 //The returned value is saved in the returnedResult variable
 byte val1 = returnedResult[0];
 byte val2 = returnedResult[1];
}

请注意,这只是一个使用 2个字符的char的测试仅。您可以通过向C#函数添加 out int outValue 参数然后添加 int * outValue C函数的参数。然后,您可以在C侧写入此参数,该字符的大小为C#,并从C#侧访问该大小。

Note that this is only a test that uses char with 2 characters only. You can make the size of the string dynamic by adding a out int outValue parameter to the C# function then adding int* outValue parameter to the C function. You can then write to this parameter on the C side the size of the character is and access that size from the C# side.

然后可以将此大小传递给 Marshal.Copy 函数的最后一个参数,并删除当前的硬件编码 2 值限制。我会留下这个让你这样做,但如果感到困惑,请参阅这篇帖子。

This size can then be passed to the last argument of the Marshal.Copy function and remove the current hard-coded 2 value limit. I will leave this for you to do but if confused, see this post for example of that.

更好的解决方案是将 StringBuilder 传递给本机端然后写信给它。不好的一面是你必须按时声明 StringBuilder 的大小。

The better solution is to pass StringBuilder to the native side then write to it. The bad side is that you have to declare the size of the StringBuilder on time.

C ++

void __cdecl  _GetLookupStatus (char* data, size_t size) 
{ 
    strcpy_s(data, size, "Test"); 
}

C#

[DllImport("__Internal", CallingConvention = CallingConvention.Cdecl)]
public static extern int _GetLookupStatus(StringBuilder data, int size);

//Test
void Start() 
{
    StringBuilder buffer = new StringBuilder(500);
    _GetLookupStatus (buffer, buffer.Capacity);
    string result = buffer.ToString();
}






如果您正在寻找最快的方法是你应该在C#端使用char数组,将它固定在C#端然后将它作为 IntPtr 发送给C.在C端,您可以使用 strcpy_s 来修改char数组。这样,在C端没有分配内存。您只是从C#重用char数组的内存。您可以在答案结尾处看到 float [] 示例此处


If you are looking for the fastest way then you should use char array on the C# side, pin it on C# side then send it to C as IntPtr. On the C side, you can use strcpy_s to modify the char array. That way, no memory is allocated on the C side. You are just re-using the memory of the char array from C#. You can see the float[] example at the end of the answer here.

这篇关于如何正确编组从Unity到C / C ++的字符串?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆