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

C# 如何管理C和C之间的缓冲区# 问题,c#,xcode,memory-management,interop,unity3d,C#,Xcode,Memory Management,Interop,Unity3d,我有一个C#脚本,它通过System.Runtime.Interop调用C函数。我设法调用了C函数,但在管理C和C#之间的缓冲区时遇到了问题 在我的情况下,C是(数据)生产者,C#是消费者 我的问题是,当我在C#中读取数据时,有时会得到正确的值,但有时会得到NULL 这个问题已经解决了。我把我的错误方法和我的正确方法贴在这里与大家分享 背景 C#代码是unity脚本(Mono开发的一部分),C代码在Xcode中,这意味着我不能在我的C代码中使用.Net framework函数。 错误的方法(有时

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

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

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

这个问题已经解决了。我把我的错误方法和我的正确方法贴在这里与大家分享

背景 C#代码是unity脚本(Mono开发的一部分),C代码在Xcode中,这意味着我不能在我的C代码中使用.Net framework函数。

错误的方法(有时给我空值) 这是我的C代码(写入缓冲区和从缓冲区读取):

我将函数ReadFromBuffer()公开给C#:

然后,我调用如下函数:

        IntPtr temp = Marshal.AllocCoTaskMem(8912);
        CCN.ReadFromBuffer(temp);
        string news = Marshal.PtrToStringAuto(temp);
        if(news != "")
        {
            print (news);
        }
        Marshal.FreeCoTaskMem(temp);
// for the C# code to poll and read from C
unsigned int ReadFromBuffer(char* pstrRet, unsigned int cbSize)
{
    unsigned int uiRet;

    while (mutex>0);
    mutex++;

    uiRet=strlen(InteropBF);

    if (uiRet > 0 && cbSize > uiRet) 
    {    
        strcpy(pstrRet, InteropBF);
    }
    else //error
    {        
        uiRet=0;
    }

    strcpy(InteropBF, "");
    mutex--;

    return uiRet;
}


//calling from c# 
[DllImport ("CCNxPlugin")]
public static extern UInt32 ReadFromBuffer(IntPtr pData, UInt32 cbData);
.
.
. 

String news;
UInt32 cbData=INTEROP_BUFFER_SIZE; //you need to define this in c# ;)
Byte[]  abyData=new byte[cbData];

try
{     
    //kindly request GC gives us a data address & leaves this memory alone for a bit
    GCHandle oGCHData = GCHandle.Alloc(abyData, GCHandleType.Pinned);
    IntPtr pbyData = oGCHData.AddrOfPinnedObject();

    UInt32 cbCopied=ReadFromBuffer(pbyData, cbData);

    oGCHData.Free();

    if(cbCopied > 0)
    { 
        System.Text.Encoding enc = System.Text.Encoding.ASCII;
        news = enc.GetString(abyData,0,cbCopied);
    }
}
catch (Exception e)
{
    System.Diagnostics.Debug.WriteLine(e);
}
使用这段代码,我有时会获得正确的缓冲区内容,但更频繁的是,我会从Marshal.PtrToStringAuto函数中获得空值

正确的方法(非常感谢大家!) 我想粘贴我在这里找到的工作代码和引用--

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;
    }
}
C#包装:

在C#中调用函数:

总结
  • char[]似乎不是C和C#之间的好缓冲区(至少在我使用Unity Mono和Xcode的情况下)。我的建议是在结构中组织数据,并将结构作为参数或返回值传递给C#。我已经找到了关于传递类和结构的优秀文档,但我还没有找到任何关于仅仅传递char数组本身的文档。所以我想最好将char[]包装在结构或类中
  • C结构可以封送为C#类或C#结构。将包装类作为参数传递给非托管函数将起作用。将包装结构作为参数传递给非托管函数也会起作用。从非托管函数返回指向结构的指针是可以的。从非托管函数返回指向类的指针是不正常的。(找到了一个关于此的很棒的教程:)

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

    可以用C/C++在CoTaskMemAlloc中进行,将它与McHal.FeleCasaskMeM.< /P> < P>免费。在C++中:尝试传递C字符串类型,并使用<代码> SysLoStrys< /C> > C++中的< /P>

    // for the C# code to poll and read from C
    void ReadFromBuffer(BSTR* pstrRet)
    {
        while (mutex>0);
        mutex++;
    
        *pstrRet=SysAllocString(InteropBF)
    
        strcpy(InteropBF, "");
        mutex--;
    }
    
    //calling from c# 
    string news ="";
    ReadFromBuffer(out news);
    if(news != "")
    {
        print (news);
    }
    

    否则,您可以尝试为c#中的返回值创建一个足够大的字节数组&将指向内存的底层指针传递给xcode。像这样:

            IntPtr temp = Marshal.AllocCoTaskMem(8912);
            CCN.ReadFromBuffer(temp);
            string news = Marshal.PtrToStringAuto(temp);
            if(news != "")
            {
                print (news);
            }
            Marshal.FreeCoTaskMem(temp);
    
    // for the C# code to poll and read from C
    unsigned int ReadFromBuffer(char* pstrRet, unsigned int cbSize)
    {
        unsigned int uiRet;
    
        while (mutex>0);
        mutex++;
    
        uiRet=strlen(InteropBF);
    
        if (uiRet > 0 && cbSize > uiRet) 
        {    
            strcpy(pstrRet, InteropBF);
        }
        else //error
        {        
            uiRet=0;
        }
    
        strcpy(InteropBF, "");
        mutex--;
    
        return uiRet;
    }
    
    
    //calling from c# 
    [DllImport ("CCNxPlugin")]
    public static extern UInt32 ReadFromBuffer(IntPtr pData, UInt32 cbData);
    .
    .
    . 
    
    String news;
    UInt32 cbData=INTEROP_BUFFER_SIZE; //you need to define this in c# ;)
    Byte[]  abyData=new byte[cbData];
    
    try
    {     
        //kindly request GC gives us a data address & leaves this memory alone for a bit
        GCHandle oGCHData = GCHandle.Alloc(abyData, GCHandleType.Pinned);
        IntPtr pbyData = oGCHData.AddrOfPinnedObject();
    
        UInt32 cbCopied=ReadFromBuffer(pbyData, cbData);
    
        oGCHData.Free();
    
        if(cbCopied > 0)
        { 
            System.Text.Encoding enc = System.Text.Encoding.ASCII;
            news = enc.GetString(abyData,0,cbCopied);
        }
    }
    catch (Exception e)
    {
        System.Diagnostics.Debug.WriteLine(e);
    }
    


    请同时发布相关的C和C代码。是的,太笼统了,可能是任何东西really@vcsjones很抱歉,我刚刚编辑了它并发布了我的代码。您可能想看看更好的线程同步方法。循环直到互斥量大于0将使用100%的CPU,直到互斥量大于0。如果你只有一个CPU,你很可能会被挂起。@PeterRitchie谢谢Peter,这只是一个早期的演示,所以我非常关心得到正确的结果。我想在得到正确的缓冲区值后,我肯定会使用其他方法,而不是忙着等待。现在我发现编写代码很容易:)谢谢!那么我的InteropBF[]被释放了?或者正在释放ReadFromBuffer()中的临时[]?或者两者都有?temp被释放了(好吧,更像是被重复使用)好的,谢谢!我会用temp试试CoTaskMemAlloc,稍后再还给你!不,不,我简直做不到。我不能打电话给CoTaskMemAlloc。也许是因为我用的是Xcode?我可以在Xcode中使用.Net framework吗?还是有解决办法?XCode?C#部分是用单声道完成的吗?这将是非常有用的信息,包括在您的问题…谢谢!棘手的是我正在使用Xcode,我发现在其中使用.Net函数有点困难。不过,我可以在C#中使用.Net函数。我正在尝试从Cocoa框架中寻找类似的功能,如果您有建议,请告诉我。啊。您可能希望在问题中包含这一事实(可能添加一个xcode标记;)~我补充了另一种可能对你有用的方法——我不确定这是不是最好的方法。。
    // for the C# code to poll and read from C
    void ReadFromBuffer(BSTR* pstrRet)
    {
        while (mutex>0);
        mutex++;
    
        *pstrRet=SysAllocString(InteropBF)
    
        strcpy(InteropBF, "");
        mutex--;
    }
    
    //calling from c# 
    string news ="";
    ReadFromBuffer(out news);
    if(news != "")
    {
        print (news);
    }
    
    // for the C# code to poll and read from C
    unsigned int ReadFromBuffer(char* pstrRet, unsigned int cbSize)
    {
        unsigned int uiRet;
    
        while (mutex>0);
        mutex++;
    
        uiRet=strlen(InteropBF);
    
        if (uiRet > 0 && cbSize > uiRet) 
        {    
            strcpy(pstrRet, InteropBF);
        }
        else //error
        {        
            uiRet=0;
        }
    
        strcpy(InteropBF, "");
        mutex--;
    
        return uiRet;
    }
    
    
    //calling from c# 
    [DllImport ("CCNxPlugin")]
    public static extern UInt32 ReadFromBuffer(IntPtr pData, UInt32 cbData);
    .
    .
    . 
    
    String news;
    UInt32 cbData=INTEROP_BUFFER_SIZE; //you need to define this in c# ;)
    Byte[]  abyData=new byte[cbData];
    
    try
    {     
        //kindly request GC gives us a data address & leaves this memory alone for a bit
        GCHandle oGCHData = GCHandle.Alloc(abyData, GCHandleType.Pinned);
        IntPtr pbyData = oGCHData.AddrOfPinnedObject();
    
        UInt32 cbCopied=ReadFromBuffer(pbyData, cbData);
    
        oGCHData.Free();
    
        if(cbCopied > 0)
        { 
            System.Text.Encoding enc = System.Text.Encoding.ASCII;
            news = enc.GetString(abyData,0,cbCopied);
        }
    }
    catch (Exception e)
    {
        System.Diagnostics.Debug.WriteLine(e);
    }