2

私は現在、DLLでエクスポートされた関数とC#でのP/invokeを調査しています。非常に単純な.dllを作成しました。

Test.h

#ifndef TEST_DLL_H
#define TEST_DLL_H
extern "C" __declspec(dllexport) const char * __cdecl hello ();
extern "C" __declspec(dllexport) const char * __cdecl test ();    
#endif  // TEST_DLL_H

Test.cpp

#include <stdlib.h>
#include "test.h"
#include <string.h>

const char* hello()
{
    char *novi = (char *)malloc(51);
    strcpy(novi, "Test.");

    return novi;
}

const char * test()
{
    return "Test.";
}

私はそれをコンパイルし、次のようにC#プロジェクトで使用しました:

    [DllImport("test.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern IntPtr hello();

    [DllImport("test.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern string test();

    private void button1_Click(object sender, EventArgs e)
    {
        MessageBox.Show(test());
        IntPtr a =  hello();
        MessageBox.Show(Marshal.PtrToStringAnsi(a));
    }

しかし、それは機能していません。test()正常に呼び出され、正しい文字列が返されます。しかしhello()、プログラムを切るだけです。定義からmalloc行を削除してhello()定数を返すと、すべてが機能するので、現在認識しているmallocに問題があると思います。

また、returntypeがcha​​r*の場合、文字列を使用すべきではないことをどこかで見ました。それが本当なら、なぜIntPtrを使用する必要があるのでしょうか。

4

1 に答える 1

3

DLLの境界を越えて文字列を返す関数は、CまたはC ++から確実に呼び出すことは非常に困難であり、C#から呼び出すと改善されません。問題は、呼び出し元が文字列バッファを解放する方法です。これはhello()に対して行う必要がありますが、test()に対しては行う必要がありません。推測するのが非常に難しい何か。hello()関数では、malloc()の呼び出しに使用されたものとまったく同じアロケータを使用して、free()関数を使用する必要があります。これは、DLLと呼び出し元が同じCRT実装を共有している場合にのみ機能します。スリムなオッズ。

pinvokeマーシャラーも文字列バッファを解放する必要があります。そして、唯一の合理的な選択であるCoTaskMemFree()を使用してこれを行います。これは、COMが使用するデフォルトのアロケータを使用します。これはうまくいきません。CコードはCoTaskMemAlloc()を使用していません。これにより発生する可能性のある結果は、オペレーティングシステムによって異なります。Vista以降では、プログラムはAccessViolationで停止します。これらのWindowsバージョンは、動作に問題のあるプログラムをクラッシュさせるように設計された厳密なヒープアロケータを使用します。XPでは、メモリリークとヒープの破損の間に何かが発生します。これは、2番目のオプションを押したように聞こえます。

IntPtrとして戻り値を宣言することは良い終わりになります。さて、あなたのプログラムはクラッシュしません、あなたはまだあなたが差し込むことができないメモリリークを持っています。free()を確実に呼び出す方法はありません。または、CコードでCoTaskMemAlloc()を使用して、pinvokeマーシャラーのリリース呼び出しが機能するようにします。

しかし現実的には、このようなCコードを記述しないでください。呼び出し元によって割り当てられたメモリを常に使用して、メモリの所有者が推測されないようにします。これには、次のような関数シグネチャが必要です。

extern "C" __declspec(dllexport) 
int hello(char* buffer, int bufferSize);
于 2012-07-08T19:47:02.567 に答える