1

DllImport を使用して、自分の .net クラスから C ラッパー ライブラリのメソッドを呼び出しています。c dll のこのメソッドは、文字列変数を作成し、文字列のポインターを返します。

このようなもの;

_declspec(dllexport) int ReturnString()
{
 char* retval = (char *) malloc(125);
 strcat(retval, "SOMETEXT");
 strcat(retval, "SOMETEXT MORE");
 return (int)retval;
}

次に、Marshall.PtrToStringAnsi(ptr) を使用して文字列を読み取ります。文字列のコピーを取得したら、free(ptr) を呼び出す C ラッパー ライブラリにある別の C メソッド HeapDestroy を呼び出すだけです。

これが質問です。最近、魔法のように機能しているときに、「保護されたメモリ領域の読み取りまたは書き込みを試みました」という例外が発生し始めました。より深い分析の後、私は、このポインターに対して無料のメソッドを呼び出しても、ポインターの値がクリアされず、ヒープが無人でいっぱいになり、iis ワーカー プロセスがこの例外をスローするようになると信じています。ちなみに、このメソッドをcライブラリで呼び出しているのはWebサイトプロジェクトです。

この問題について助けていただけませんか?

もちろん、これは C# コードです。

    [DllImport("MyCWrapper.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
    private extern static int ReturnString();

    [DllImport("MyCWrapper.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
    private extern static void HeapDestroy(int ptr);

    public static string GetString()
    {
        try
        {

            int i = ReturnString();
            string result = String.Empty;
            if (i > 0)
            {
                IntPtr ptr = new IntPtr(i);
                result = Marshal.PtrToStringAnsi(ptr);
                HeapDestroy(i);
            }

            return result;
        }
        catch (Exception e)
        {
            return String.Empty;
        }
    }
4

3 に答える 3

7

問題の可能性があるのは、基になる C コードです。strcat が依存する文字列に NULL ターミネータを追加していない (または malloc からの NULL 戻り値をチェックしていない)。そのシナリオでは、メモリが破損しやすくなります。これは、次の手順で修正できます。

retval[0] = '\0';
strcat(retval, "SOMETEXT");

また、問題の一部は、システムでいたずらをしていることです。正しく記述して、システムが正しく機能するコードで動作するようにする方がはるかに優れています。最初のステップは、文字列を正しく返すようにネイティブ コードを修正することです。考慮する必要があることの 1 つは、特定の種類のメモリのみが CLR (HGlobal および CoTask 割り当て) によってネイティブに解放できるということです。したがって、関数シグネチャを変更して a を返し、char*別のアロケータを使用してみましょう。

_declspec(dllexport) char* ReturnString()
{
 char* retval = (char *) CoTaskMemAlloc(125);
 retval[0] = '\0';
 strcat(retval, "SOMETEXT");
 strcat(retval, "SOMETEXT MORE");
 return retval;
}

次に、次の C# シグネチャを使用して、Marshal.FreeCoTaskMem で IntPtr を解放できます。

[DllImport("SomeDll.dll")]
public static extern IntPtr ReturnString();

さらに良いですが。マーシャリング時に、CLR がメモリを解放する必要があると判断した場合は、FreeCoTaskMem を使用して解放します。これは通常、文字列の戻り値に関連しています。CoTaskMemAlloc でメモリを割り当てたので、マーシャリングと解放の手順を節約して、次のことを行うことができます。

[DllImport("SomeDll.dll", CharSet=Ansi)]
public static extern String ReturnString();
于 2009-09-30T15:54:10.987 に答える
1

メモリを解放してもクリアされません。再利用できるように解放されるだけです。一部のデバッグ ビルドはメモリを上書きして、0xBAADFOOD などの値の問題を見つけやすくします。

呼び出し元はメモリを割り当てる必要があり、割り当てられたメモリを決して返さないでください。

_declspec(dllexport) int ReturnString(char*buffer, int bufferSize)
{
    if (bufferSize < 125) {
        return 125;
    } else {
        strcat(buffer, "SOMETEXT");
        strcat(buffer, "SOMETEXT MORE");
        return 0;
    }
}
于 2009-09-30T15:54:39.160 に答える
0

メモリは DLL によってアプリケーションと同じヒープに割り当てられますが、リンクされたライブラリによっては、別のメモリ マネージャーを使用している可能性があります。まったく同じライブラリを使用していることを確認するか、DLL コード自体に DLL が割り当てたメモリを解放するコードを追加する必要があります。

于 2009-09-30T15:56:14.823 に答える