問題
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構造体は、C#クラスまたはC#構造体としてマーシャリングできます。ラッピングクラスをアンマネージ関数にパラメーターとして渡すと機能します。ラッピング構造体をアンマネージ関数にパラメーターとして渡すこともできます。アンマネージ関数から構造体へのポインターを返すことは問題ありません。アンマネージ関数からクラスへのポインターを返すことは問題ありません。(これに関する優れたチュートリアルが見つかりました:http ://www.mono-project.com/Interop_with_Native_Libraries#Summary )