我有一个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#wrapper:
[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);
}
答案 0 :(得分:3)
你要传回一个堆栈变量。从方法返回时,该变量可以在C / C ++中“收集”或释放 - 这将在到达PtrToStringAuto时随机导致内存不良。这可以解释为什么有时 null。您需要分配传递回C#代码的内存,并且代码需要释放内存(或者C / C ++需要以某种方式执行)。
您可以使用CoTaskMemAlloc和C#在c / c ++中执行此操作,使用Marshal.FreeCoTaskMem将其释放。
答案 1 :(得分:0)
尝试传入c#字符串类型并使用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);
}