2017-06-09 10 views
2

次のコードスニペットはUnits Bonjourクライアントの例です。これはC#のネイティブコードと対話する方法を示しています。これは、バックC#(ユニティ)に文字列を返す簡単なC関数です:iOSからUnityへの文字列を正しくマーシャリングする方法は?

char* MakeStringCopy (const char* string) 
{ 
    if (string == NULL) 
     return NULL; 

    char* res = (char*)malloc(strlen(string) + 1); 
    strcpy(res, string); 
    return res; 
} 

const char* _GetLookupStatus() 
{ 
    // By default mono string marshaler creates .Net string for returned UTF-8 C string 
    // and calls free for returned value, thus returned strings should be allocated on heap 
    return MakeStringCopy([[delegateObject getStatus] UTF8String]); 
} 

機能のC#の宣言は次のようになります。

:ここで私をパズルいくつかのものがあります

[DllImport ("__Internal")] 
private static extern string _GetLookupStatus(); 

  1. これは、iOSネイティブコードからC#に文字列を返す正しい方法ですか?
  2. 返される文字列はどのように解放されますか?
  3. もっと良い方法がありますか?

この問題に関する洞察はあります。 ありがとうございます。

答えて

1

.No。

。自分で行う必要があります。

.Yes

あなたがCまたはC++側の関数の内部メモリを割り当てる場合は、それを解放しなければなりません。私はサイドにメモリを割り当てるコードは見当たりませんが、あなたはその部分を残したと思います。また、スタック上で宣言された変数をC#に返さないでください。クラッシュを含む未定義の動作に終わります。

Hereは、このためのC++ソリューションです。

C溶液について:

char* getByteArray() 
{ 
    //Create your array(Allocate memory) 
    char * arrayTest = malloc(2 * sizeof(char)); 

    //Do something to the Array 
    arrayTest[0]=3; 
    arrayTest[1]=5; 

    //Return it 
    return arrayTest; 
} 


int freeMem(char* arrayPtr){ 
    delete[] arrayPtr; 
    free(arrayPtr); 
    return 0; 
} 

唯一の違いは、Cバージョンはメモリを割り当て、デ割り当てるmallocfree機能を使用することです。

C#

[DllImport("__Internal", CallingConvention = CallingConvention.Cdecl)] 
public static extern IntPtr getByteArray(); 

[DllImport("__Internal", CallingConvention = CallingConvention.Cdecl)] 
public static extern int freeMem(IntPtr ptr); 

//Test 
void Start() 
{ 
//Call and return the pointer 
IntPtr returnedPtr = getIntArray(); 

//Create new Variable to Store the result 
byte[] returnedResult = new byte[2]; 

//Copy from result pointer to the C# variable 
Marshal.Copy(returnedPtr, returnedResult, 0, 2); 

//Free native memory 
freeMem(returnedPtr); 

//The returned value is saved in the returnedResult variable 
byte val1 = returnedResult[0]; 
byte val2 = returnedResult[1]; 
} 

注この2文字のみとチャーを使用する唯一の試験であること。文字列の動的サイズは、out int outValueパラメータをC#関数に追加してから、int* outValueパラメータをC関数に追加することで動的にすることができます。 C面のこのパラメータに文字のサイズを書き込んで、そのサイズをC#側からアクセスできます。

このサイズは、Marshal.Copy関数の最後の引数に渡すことができ、現在のハードコーディングされた2の値の制限値を削除します。私はこれをあなたのために残しておきますが、混乱した場合は、その例のthisを参照してください。


より良い解決策は、それに書き込むネイティブ側にStringBuilderを渡すことです。悪い点は、StringBuilderのサイズを時間通りに宣言しなければならないことです。

C++

void __cdecl _GetLookupStatus (char* data, size_t size) 
{ 
    strcpy_s(data, size, "Test"); 
} 

C#の:あなたは、あなたがC#の側に文字列を使用する必要があります最速の方法を探しているなら

[DllImport("__Internal", CallingConvention = CallingConvention.Cdecl)] 
public static extern int _GetLookupStatus(StringBuilder data, int size); 

//Test 
void Start() 
{ 
    StringBuilder buffer = new StringBuilder(500); 
    _GetLookupStatus (buffer, buffer.Capacity); 
    string result = buffer.ToString(); 
} 

、それをピンC#側では、IntPtrとしてCに送信します。 C側では、strcpy_sを使用してchar配列を変更できます。そうすれば、C側にメモリは割り当てられません。 C#のchar配列のメモリを再利用しています。回答hereの最後には、float[]の例があります。

+0

詳細な説明をありがとうございました!コードスニペットにMakeStringCopy()を追加しました。 –

+0

スタックに渡される変数を返さないことに関するもう1つの質問:プリミティブデータ型にも当てはまりますか?私はいくつかのテストとストリングを即座に失敗しましたが、intはうまくいくようです。 –

+0

int、float、boolの場合は問題ありません。あなたが '&'またはそれのポインタに '*'をつけてその参照を返すのであれば、それはうまくありません。 'int []'や 'int *'のような配列の場合は、そのメモリアドレスを返すのでうまくいきません。 C++の 'malloc'やC++の' new'キーワードで作成された変数であれば、関数の外にあってもそこに存在するので、問題ありません。私は関数内で参照やポインタを返すことについて学ぶべきだと思います。あなたはこれらを完全に理解するでしょう。 – Programmer

関連する問題