9

次のように C# で呼び出されるいくつかの関数を公開する Win32 DLL を作成しようとしています。

__declspec(dllexport) int GetData(unsigned char* *data, int* size)
{
    try
    {
        int tlen = 3;
        unsigned char* tchr = new unsigned char[5];
        tchr[0] = 'a';
        tchr[1] = 'b';
        tchr[2] = 'c';

        *size = tlen;
        *data = tchr;

        return 1;
    }
    catch (char *p)
    {
        return 0;
    }
}

そしてC#側で

[DllImport("MyDll.dll")]
static extern int GetData(ref byte[] data, ref int size);

static void Main()
{
    try
    {
        int hr = 0;
        byte[] gData = null;
        int gSize = 0;
        hr = GetData(ref gData, ref gSize);
        Console.WriteLine(gSize);
        for (int i = 0; i < gSize; i++)
            Console.WriteLine((char)gData[i]);
    }
    catch (Exception p)
    {
        Console.WriteLine(p.ToString());
    }
}

C# コードを実行すると、C++ コードの例外の兆候である関数でAccessViolationException発生しGetDataますが、次の C++ コード スニペットはエラーなしで正常に動作します。

int _tmain(int argc, _TCHAR* argv[])
{
    unsigned char* data = NULL;
    int size = NULL;
    GetData(&data, &size);
    printf("%d", size);
    for (int i = 0; i < size; i++)
        printf("%c,", data[i]);
    return 0;
}

C#のmain関数と C++を比較すると_tmain、ほとんど類似しているため、どこで間違いを犯す可能性がありますか?

4

1 に答える 1

13

C++ new の呼び出しによって割り当てられた配列を返し、マーシャラーがそれを C# byte[] に変換することを期待しています。そんなことはありません。

参照によってポインターを渡し、それを手動でマーシャリングする必要があります。p/invoke は次のようになります。

[DllImport("MyDll.dll")]
static extern int GetData(out IntPtr data, out int size);

関数がデータを返すと、配列を指し、Marshal クラスを使用して内容を読み取ることができます。それを新しいバイト配列にコピーすると思います。

var arr = new byte[size];
Marshal.Copy(data, arr, 0, size);

その他のポイント:

  1. 呼び出し規約が一致しません。ネイティブ側は cdecl で、マネージド側は stdcall です。
  2. ネイティブ関数によって返されたメモリを削除するには、デアロケーターをエクスポートする必要があります。呼び出し元がバッファーを割り当てる再設計を検討してください。
于 2013-04-07T13:59:44.367 に答える