簡體   English   中英

如何管理C和C之間的緩沖區#

[英]How to manage a buffer between C and C#

問題

我有一個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;
}

我將函數ReadFromBuffer()暴露給C#:

[DllImport ("CCNxPlugin")]
public static extern IntPtr ReadFromBuffer(IntPtr temp);

然后,我調用這樣的函數:

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

使用此代碼我有時會獲得正確的緩沖區內容,但更常見的是我從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;
    }
}

C#包裝器:

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


[DllImport ("CCNxPlugin")]
public static extern IntPtr ReadFromBuffer();

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

摘要

  1. char []似乎不是C和C#之間的良好緩沖區(至少在我使用Unity-Mono和Xcode的情況下)。 我的建議是在結構中組織數據並將結構作為參數或返回值傳遞給C#。 我已經找到了關於傳遞類和結構的很好的文檔,但我還沒有發現任何關於傳遞char數組的信息。 所以我想在結構或類中包裝char []總是更好。
  2. AC結構可以編組為C#類或C#結構。 將包裝作為參數傳遞給非托管函數將起作用。 將包裝結構作為參數傳遞給非托管函數也可以。 從非托管函數返回指向結構的指針是可以的。 返回一個指針,從非托管函數的 好。 (找到了一個很棒的教程: http//www.mono-project.com/Interop_with_Native_Libraries#Summary

你要傳回一個堆棧變量。 從方法返回時,該變量可以在C / C ++中“收集”或釋放 - 這將在到達PtrToStringAuto時隨機導致內存不良。 這可以解釋為什么它有時為空。 您需要分配傳遞回C#代碼的內存,並且該代碼需要釋放內存(或者C / C ++需要以某種方式執行此操作)。

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

嘗試傳入ac#string類型並在C ++中使用SysAllocString

// 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。 像這樣:

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


暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM