C# 如何管理C和C之间的缓冲区# 问题
我有一个C#脚本,它通过System.Runtime.Interop调用C函数。我设法调用了C函数,但在管理C和C#之间的缓冲区时遇到了问题 在我的情况下,C是(数据)生产者,C#是消费者 我的问题是,当我在C#中读取数据时,有时会得到正确的值,但有时会得到NULL 这个问题已经解决了。我把我的错误方法和我的正确方法贴在这里与大家分享 背景 C#代码是unity脚本(Mono开发的一部分),C代码在Xcode中,这意味着我不能在我的C代码中使用.Net framework函数。 错误的方法(有时给我空值) 这是我的C代码(写入缓冲区和从缓冲区读取): 我将函数ReadFromBuffer()公开给C#: 然后,我调用如下函数:C# 如何管理C和C之间的缓冲区# 问题,c#,xcode,memory-management,interop,unity3d,C#,Xcode,Memory Management,Interop,Unity3d,我有一个C#脚本,它通过System.Runtime.Interop调用C函数。我设法调用了C函数,但在管理C和C#之间的缓冲区时遇到了问题 在我的情况下,C是(数据)生产者,C#是消费者 我的问题是,当我在C#中读取数据时,有时会得到正确的值,但有时会得到NULL 这个问题已经解决了。我把我的错误方法和我的正确方法贴在这里与大家分享 背景 C#代码是unity脚本(Mono开发的一部分),C代码在Xcode中,这意味着我不能在我的C代码中使用.Net framework函数。 错误的方法(有时
IntPtr temp = Marshal.AllocCoTaskMem(8912);
CCN.ReadFromBuffer(temp);
string news = Marshal.PtrToStringAuto(temp);
if(news != "")
{
print (news);
}
Marshal.FreeCoTaskMem(temp);
// 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);
}
使用这段代码,我有时会获得正确的缓冲区内容,但更频繁的是,我会从Marshal.PtrToStringAuto函数中获得空值
正确的方法(非常感谢大家!)
我想粘贴我在这里找到的工作代码和引用--
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#包装:
在C#中调用函数:
总结
您正在传回一个堆栈变量。该变量可以在从方法返回时在C/C++中“收集”或释放——这将在它到达PtrToStringAuto时随机导致内存损坏。这可以解释为什么它有时是空的。您需要分配传递回C#代码的内存,代码需要释放内存(或者C/C++需要以某种方式)
可以用C/C++在CoTaskMemAlloc中进行,将它与McHal.FeleCasaskMeM.< /P> < P>免费。在C++中:尝试传递C字符串类型,并使用<代码> SysLoStrys< /C> > C++中的< /P>
// 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。像这样:
IntPtr temp = Marshal.AllocCoTaskMem(8912);
CCN.ReadFromBuffer(temp);
string news = Marshal.PtrToStringAuto(temp);
if(news != "")
{
print (news);
}
Marshal.FreeCoTaskMem(temp);
// 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);
}
请同时发布相关的C和C代码。是的,太笼统了,可能是任何东西really@vcsjones很抱歉,我刚刚编辑了它并发布了我的代码。您可能想看看更好的线程同步方法。循环直到互斥量大于0将使用100%的CPU,直到互斥量大于0。如果你只有一个CPU,你很可能会被挂起。@PeterRitchie谢谢Peter,这只是一个早期的演示,所以我非常关心得到正确的结果。我想在得到正确的缓冲区值后,我肯定会使用其他方法,而不是忙着等待。现在我发现编写代码很容易:)谢谢!那么我的InteropBF[]被释放了?或者正在释放ReadFromBuffer()中的临时[]?或者两者都有?temp被释放了(好吧,更像是被重复使用)好的,谢谢!我会用temp试试CoTaskMemAlloc,稍后再还给你!不,不,我简直做不到。我不能打电话给CoTaskMemAlloc。也许是因为我用的是Xcode?我可以在Xcode中使用.Net framework吗?还是有解决办法?XCode?C#部分是用单声道完成的吗?这将是非常有用的信息,包括在您的问题…谢谢!棘手的是我正在使用Xcode,我发现在其中使用.Net函数有点困难。不过,我可以在C#中使用.Net函数。我正在尝试从Cocoa框架中寻找类似的功能,如果您有建议,请告诉我。啊。您可能希望在问题中包含这一事实(可能添加一个xcode标记;)~我补充了另一种可能对你有用的方法——我不确定这是不是最好的方法。。
// 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);
}
// 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);
}