如何管理C和C之间的缓冲区#

Zen*_* Qu 5 c# xcode interop memory-management unity-game-engine

问题

我有一个C#脚本,通过System.Runtime.Interop调用C函数.我设法调用了C函数,但是在管理C和C#之间的缓冲区时遇到了问题.

在我的情况下,C是(数据)生产者,C#是消费者.

我的问题是当我在C#中读取数据时,有时我得到正确的值但有时我得到NULL.

这个问题已经解决了.我正在粘贴错误的方法,并在此与我分享正确的方法.

背景

C#代码是一个统一脚本(Mono开发的一部分),C代码在Xcode中,这意味着我不能在我的C代码中使用.Net框架函数.

错误的方法(不时给我NULL)

这是我的C代码(写入缓冲区并从缓冲区读取):

static char InteropBF[INTEROP_BUFFER_SIZE];
static int mutex = 0;
// for the c code to put its message in buffer
void PutToBuffer(char* name, char* content)
{
    while (mutex>0);
    mutex++;
    strcat(InteropBF, name);
    strcat(InteropBF, ",");
    strcat(InteropBF, content);
    strcat(InteropBF, ",");
    printf("Interop Buffer: %s\n", InteropBF);
    mutex--;
}


// for the C# code to poll and read from C
void* ReadFromBuffer(void* temp)
{
    while (mutex>0);
    mutex++;
    strcpy(temp, InteropBF);
    // memcpy(temp, InteropBF, INTEROP_BUFFER_SIZE);
    strcpy(InteropBF, "");
    mutex--;
    return temp;
}
Run Code Online (Sandbox Code Playgroud)

我将函数ReadFromBuffer()暴露给C#:

[DllImport ("CCNxPlugin")]
public static extern IntPtr ReadFromBuffer(IntPtr temp);
Run Code Online (Sandbox Code Playgroud)

然后,我调用这个函数:

        IntPtr temp = Marshal.AllocCoTaskMem(8912);
        CCN.ReadFromBuffer(temp);
        string news = Marshal.PtrToStringAuto(temp);
        if(news != "")
        {
            print (news);
        }
        Marshal.FreeCoTaskMem(temp);
Run Code Online (Sandbox Code Playgroud)

使用此代码我有时会获得正确的缓冲区内容,但更常见的是我从Marshal.PtrToStringAuto函数中获取NULL.

正确的方法(非常感谢大家!)

我想粘贴我在这里找到的工作代码和参考资料 -

C功能:

struct bufnode
{
    char* name;
    char* content;
    struct bufnode *next;
};

struct bufnode* bufhead = NULL;
struct bufnode* buftail = NULL;
// for the c code to put its message in buffer
void PutToBuffer(char* name, char* content)
{
    struct bufnode *temp = malloc(sizeof(struct bufnode));
    temp->name = malloc(256);
    temp->content = malloc(256);
    strcpy(temp->name,name);
    strcpy(temp->content,content);
    temp->next = NULL;

    if (bufhead == NULL && buftail == NULL) {
        bufhead = temp;
        buftail = temp;
    }
    else if(bufhead != NULL && buftail != NULL){
        buftail->next = temp;
        buftail = temp;
    }
    else {
        printf("Put to buffer error.\n");
    }    
}

// for the C# code to poll and read from C
struct bufnode* ReadFromBuffer()
{
    if (bufhead != NULL && buftail != NULL) {
        // temp->name = bufhead->name;
        // temp->content = bufhead->content;
        // temp->next = NULL;
        struct bufnode* temp = bufhead;
        if (bufhead == buftail) {
            bufhead = NULL;
            buftail = NULL;
        }
        else {
            bufhead = bufhead->next;
        }

        return temp;
    }
    else if(bufhead == NULL && buftail == NULL)
    {
        return NULL;
    }
    else {
        return NULL;
    }
}
Run Code Online (Sandbox Code Playgroud)

C#包装器:

[StructLayout (LayoutKind.Sequential)]
public struct bufnode 
{
    public string name;
        public string content;
        public IntPtr next;
}


[DllImport ("CCNxPlugin")]
public static extern IntPtr ReadFromBuffer();
Run Code Online (Sandbox Code Playgroud)

在C#中调用函数:

        CCN.bufnode BufNode;
        BufNode.name = "";
        BufNode.content = "";
        BufNode.next = IntPtr.Zero;

        IntPtr temp = CCN.ReadFromBuffer();
        if(temp != IntPtr.Zero)
        {
            BufNode = (CCN.bufnode)Marshal.PtrToStructure(temp, typeof(CCN.bufnode));
            print(BufNode.name);
            print(BufNode.content);
            Marshal.FreeCoTaskMem(temp);
        }
Run Code Online (Sandbox Code Playgroud)

摘要

  1. char []似乎不是C和C#之间的良好缓冲区(至少在我使用Unity-Mono和Xcode的情况下).我的建议是在结构中组织数据并将结构作为参数或返回值传递给C#.我已经找到了关于传递类和结构的很好的文档,但我还没有找到任何关于传递char数组的信息.所以我想在结构或类中包装char []总是更好.
  2. AC结构可以编组为C#类或C#结构.将包装作为参数传递给非托管函数将起作用.将包装结构作为参数传递给非托管函数也可以.从非托管函数返回指向结构的指针是可以的.返回一个指向的非托管函数是不是好的.(找到了一个很棒的教程:http://www.mono-project.com/Interop_with_Native_Libraries#Summary)

Pet*_*hie 3

您正在传回一个堆栈变量。该变量可以在从方法返回时在 C/C++ 中“收集”或释放——这将在到达 PtrToStringAuto 时随机导致内存损坏。这可以解释为什么它有时为空。您需要分配传回 C# 代码的内存,并且该代码需要释放内存(或者 C/C++ 需要以某种方式执行此操作)。

您可以在 c/c++ 中使用 CoTaskMemAlloc 执行此操作,在 C# 中使用 Marshal.FreeCoTaskMem 释放它。