问题
我有一个通过 System.Runtime.Interop 调用 C 函数的 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);
}
概括
- char[] 似乎不是 C 和 C# 之间的良好缓冲区(至少在我使用 Unity-Mono 和 Xcode 的情况下)。我的建议是在结构中组织数据并将结构作为参数或作为返回值传递给 C#。我找到了关于传递类和结构的很好的文档,但我还没有找到任何关于单独传递 char 数组的信息。所以我想将 char[] 包装在结构或类中总是更好。
- AC struct 可以编组为 C# 类或 C# 结构。将包装类作为参数传递给非托管函数将起作用。将包装结构作为参数传递给非托管函数也可以。从非托管函数返回指向结构的指针是可以的。从非托管函数返回指向类的指针是不行的。(找到了一个很棒的教程:http ://www.mono-project.com/Interop_with_Native_Libraries#Summary )