[英]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框架函數。
這是我的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);
}
你要傳回一個堆棧變量。 從方法返回時,該變量可以在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.