2

私のコードは機能しますが、P/Invoke内のメモリが怖いです。

これが、ネイティブC++コードから呼び出すデリゲートです。

[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)]
public delegate string CommandCallbackDelegate([MarshalAs(UnmanagedType.LPWStr)] string command
    , [MarshalAs(UnmanagedType.LPWStr)] string arg1
    , [MarshalAs(UnmanagedType.LPWStr)] string arg2
    );

そしてC#では、デリゲートをネイティブDLLに渡しました。

private static GCHandle _gcHandle;
public static void RegisterCommandCallback(CommandCallbackDelegate fun) { _gcHandle = GCHandle.Alloc(fun);

    if (IntPtr.Size == 8)
        RegisterCommandCallback_x64(fun);
    else
        RegisterCommandCallback_x86(fun);
}

また、ネイティブDLLでは、関数ポインターを取得した後、メソッドを正常に呼び出して、StringC#から返されたものを取得できます。

typedef LPCWSTR (WINAPI* PFN_CommandCallback)( LPCWSTR wszCommand, LPCWSTR wszArg1, LPCWSTR wszArg2);
PFN_CommandCallback g_pfnCommandCallback = ....;

LPCWSTR wszRet = g_pfnCommandCallback( L"CMD", L"ARG1", L"ARG2");

ご覧のとおり、C#メソッドはStringネイティブDLLに戻りますが、返されたアンマネージメモリがGCによって再処理されるかどうかはわかりません。

上記のコードは機能しますが、解放されたばかりのメモリにアクセスしているのではないかと思います。

4

1 に答える 1

1

この場合、pinvoke マーシャラーは、共有 COM アロケーターを使用して文字列の戻り値を割り当てます。したがって、ネイティブ コードはCoTaskMemFree、ネイティブ DLL が返すポインターを呼び出す必要があります。そのため、メモリを解放する前にコンテンツのコピーを取得する必要がある場合があります。

余談ですが、パラメーターを装飾する必要はありません[MarshalAs(UnmanagedType.LPWStr)]。これは、デフォルトを再設定するだけだからです。それを削除すると、コードが読みやすくなります。

于 2013-01-10T12:05:18.383 に答える