3

C++/CLR ラッパーを介したネイティブ C++ から C# への相互運用を含むアプリケーションを開発しています。

次の操作で問題が発生し、メモリ リークが発生します。

MyObject data = (MyObject)Marshal.PtrToStructure(ptr, typeof(MyObject));
Marshal.StructureToPtr(data, ptr, false);

(注:現時点では「データ」に対して実際には何もしていないので、これは不要です。)

システムのメモリ不足によりアプリがクラッシュするまで、メモリ使用量は増加し続けます。このコードを削除すると、これは起こりません。a)システムのメモリが少なくなったときに収集する必要があり、b)GC.Collect()で強制しようとしたため、ガベージコレクターではありません。

実際、リークを StructureToPtr コマンドに絞り込みました。

メモリはネイティブ C++ によって割り当てられ、C# はこの「保護された」メモリを解放できないと見なすため、3 番目のパラメータを「true」に設定できません。

データが取り込まれた Data 構造体が無傷で、有効なデータを持ち、同等のネイティブ構造体と同じサイズであることを確認しました。

私の考えでは、これは何が起こっているべきかです:

  1. ptr によって参照されるネイティブ構造はマーシャリングされ、「データ」管理構造にコピーされます

  2. 管理構造体は、ptr によって参照される同じメモリにコピーされます。

まったく同じサイズの構造体であり、同じメモリ空間にコピーされているため、これがどのようにメモリ リークを引き起こすかわかりません。しかし、明らかにそうです。コードを削除すると、リークが塞がれます。

ここに私が正しく理解していないメカニックがありますか?

編集: 要求に応じて、「MyObject」の宣言を次に示します。

C#:

[StructLayout(LayoutKind.Sequential)]
public struct MyObject
{
    [MarshalAs(UnmanagedType.I1)]
    public bool ParamOne;
    [MarshalAs(UnmanagedType.I1)]
    public bool ParamTwo;
    [MarshalAs(UnmanagedType.I1)]
    public bool ParamThree;
    [MarshalAs(UnmanagedType.I1)]
    public bool ParamFour;
    [MarshalAs(UnmanagedType.I1)]
    public bool ParamFive;
    [MarshalAs(UnmanagedType.I1)]
    public bool ParamSix;
    [MarshalAs(UnmanagedType.R4)]
    public float ParamSeven;
    [MarshalAs(UnmanagedType.R4)]
    public float ParamEight;
    [MarshalAs(UnmanagedType.R4)]
    public float ParamNine;
    public Vector2f ParamTen;
    public Vector2f ParamEleven;
    [MarshalAs(UnmanagedType.LPWStr)]
    public string ParamTwelve;
    [MarshalAs(UnmanagedType.LPWStr)]
    public string ParamThirteen;
    [MarshalAs(UnmanagedType.LPWStr)]
    public string ParamFourteen;
    public IntPtr ParamFifteen;
    public IntPtr ParamSixteen;
}

C++:

struct MyObject
{
    public:
    bool ParamOne;
    bool ParamTwo;      
    bool ParamThree;
    bool ParamFour;
    bool ParamFive;
    bool ParamSix;
    float ParamSeven;
    float ParamEight;
    float ParamNine;
    Vector2f ParamTen;
    Vector2f ParamEleven;
    wchar_t * ParamTwelve;
    wchar_t * ParamThirteen;
    wchar_t * ParamFourteen;
    void * ParamFifteen; 
    void * ParamSixteen;
};

Vector2f の定義は次のとおりです。

[StructLayout(LayoutKind.Sequential)]
public struct Vector2f
{
    [MarshalAs(UnmanagedType.R4)]
    float x;
    [MarshalAs(UnmanagedType.R4)]
    float y;
}
4

1 に答える 1

4

構造体に文字列へのポインタがあります。これらのポインターは、アンマネージ コードで ( を使用してnew wchar_t[<a number>]) 割り当てられますよね? マネージ コードへのこれらのポインターをマーシャリングするとき、マーシャラーはマネージ文字列を作成し、それらにアンマネージ文字配列の内容を入力します。それらをアンマネージ コードにマーシャリングするとき、マーシャラーは、文字ポインターを含む構造体の内容全体をコピーし、それらに新しい値を割り当てます (を使用して各文字列にメモリを割り当てますCoTaskMemAlloc())。それが の 3 番目のパラメーターのMarshal.StructureToPtr目的です。true に設定されている場合、マーシャラーは (を使用してCoTaskMemFree()) 文字ポインターが指すメモリーの割り当てを解除しようとします。を使用して文字ポインタにメモリを割り当てた場合new演算子を使用すると、そのパラメーターを true に設定することはできず、アンマネージにマーシャリングすることによって、割り当てられたメモリへのポインターが失われます (マーシャラーはそれらを新しい値で上書きします)。また、マーシャラーによって割り当てられたメモリの割り当てを解除していないため、メモリ リークが発生します。

この状況に対処するための最良のオプションは次のとおりです。

文字列をポインタとしてマーシャリングし、使用Marshal.PtrToStringUni()して文字列に変換します。

[StructLayout(LayoutKind.Sequential)]
public struct MyObject
{
    [MarshalAs(UnmanagedType.I1)]
    public bool ParamOne;
    [MarshalAs(UnmanagedType.I1)]
    public bool ParamTwo;
    [MarshalAs(UnmanagedType.I1)]
    public bool ParamThree;
    [MarshalAs(UnmanagedType.I1)]
    public bool ParamFour;
    [MarshalAs(UnmanagedType.I1)]
    public bool ParamFive;
    [MarshalAs(UnmanagedType.I1)]
    public bool ParamSix;
    [MarshalAs(UnmanagedType.R4)]
    public float ParamSeven;
    [MarshalAs(UnmanagedType.R4)]
    public float ParamEight;
    [MarshalAs(UnmanagedType.R4)]
    public float ParamNine;
    public Vector2f ParamTen;
    public Vector2f ParamEleven;
    public IntPtr ParamTwelve;    // <-- These are your strings
    public IntPtr ParamThirteen;  // <--
    public IntPtr ParamFourteen;  // <--
    public IntPtr ParamFifteen;
    public IntPtr ParamSixteen;
}

そしてマーシャリング:

    MyObject data = (MyObject)Marshal.PtrToStructure(ptr, typeof(MyObject));
    var str1 = Marshal.PtrToStringUni(data.ParamTwelve);
    var str2 = Marshal.PtrToStringUni(data.ParamThirteen);
    var str3 = Marshal.PtrToStringUni(data.ParamFourteen);
    Marshal.StructureToPtr(data, ptr, false);
于 2012-12-03T18:34:46.797 に答える