I have a C# script that calls C functions via System.Runtime.Interop. I managed to invoke C functions, but I have problem managing a buffer between C and C#.
In my situation C is the (data) producer and C# is the consumer.
My problem is when I read data in C#, sometime I get the correct value but sometimes I get NULL.
This problem is already solved. I am pasting my wrong approach AND my right approach here to share with you.
The C# code is a unity script(part of Mono development), and the C code is in Xcode, which means I cannot use .Net framework functions in my C code.
Here is my C code (write to buffer and read from buffer):
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;
}
I exposed the function ReadFromBuffer() to C#:
[DllImport ("CCNxPlugin")]
public static extern IntPtr ReadFromBuffer(IntPtr temp);
Then, I call the function like this:
IntPtr temp = Marshal.AllocCoTaskMem(8912);
CCN.ReadFromBuffer(temp);
string news = Marshal.PtrToStringAuto(temp);
if(news != "")
{
print (news);
}
Marshal.FreeCoTaskMem(temp);
Using this code I sometimes get the correct buffer content but more frequently I get a NULL from the Marshal.PtrToStringAuto function.
I'd like to paste my working code and references I found here --
C function:
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();
Calling function in 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);
}
You're passing back a stack variable. That variable can be "collected" or freed in C/C++ upon return from the method--which will randomly result in bad memory by the time it gets to PtrToStringAuto. This would explain why it's sometimes null. You need to allocate memory that is passed back to the C# code and that code needs to free the memory (or the C/C++ needs to do it somehow).
You can do that in c/c++ with CoTaskMemAlloc and in C#, free it with Marshal.FreeCoTaskMem.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With