3

VS2005 .NET Framework 2.0 で作成した WinForms プロジェクトに問題があり、VS2012 .NET Framework 4.5 にアップグレードしました。私のプロジェクトでは、サードパーティの DLLDllImportを使用し、それらのすべてのドキュメントを持っていたので、その関数を使用しました。

問題は、VS2005 .NET Framework 2.0 で正常に動作するインポートされた DLL の関数の 1 つが VS2012 .NET 4.5 で動作しないことです。

以下は、私のプロジェクトのコード スニペットです。

[DllImport("W5EditLD.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "K5GetClassName")]
public static extern string GetClassName();//Dll import definition

public string _GetClassName()
{
    return GetClassName();//wrapper function to DLL import function 
}

string sClassName = _GetClassName();//where i call API via wrapper method,**

上記のコード スニペットは VS2005 .NET Framework 2.0 で正常に動作しますが、プロジェクトを VS2012 .NET Framework 4.5 にアップグレードするときは、次のようにする必要があります。

[DllImport("W5EditLD.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "K5GetClassName")]

public static extern IntPtr GetClassName();//Dll import definition

public IntPtr _GetClassName()
{
    return GetClassName();//wrapper function to DLL import function 
}

IntPtr ptr = _GetClassName();//where i call API via wrapper method,    
string sClassName = System.Runtime.InteropServices.Marshal. PtrToStringAnsi(ptr);

これはなぜですか?自動文字列マーシャリングは VS2012 .NET Framework 4.5 でサポートされていませんか?

4

2 に答える 2

6

元の p/invoke を検討してください:

[DllImport(...)]
public static extern string GetClassName();

マーシャラーによる戻り値の扱いが重要です。これは、NULL で終わる文字配列へのポインターである C 文字列としてマーシャリングされます。データはネイティブからマネージに送られ、マネージ コードで割り当てられていないため、フレームワークはその割り当てを解除する責任がないと想定します。ネイティブ コードは実行されていないため、割り当てを解除できません。

したがって、p/invoke マーシャラーは、文字配列が共有 COM ヒープに割り当てられていると想定するというポリシーがあります。そして、それは を呼び出しますCoTaskMemFree。配列が共有 COM ヒープに割り当てられていないことは確かです。したがって、コードは常に壊れていました。古いバージョンの .net では、呼び出しがCoTaskMemFreeたまたまサイレントに失敗しました。最新バージョンでは、エラーで失敗しています。変更が .net フレームワークにあるのか、基盤となるプラットフォームにあるのかはわかりませんが、元のコードがいたるところで壊れているため、それはほとんど問題になりません。

自動文字列マーシャリングは、以前のバージョンとまったく同じ方法で .net 4.5 でサポートされています。しかし、あなたはそれを正しくしなければなりません。string既定のマーシャリングで戻り値を使用する場合は、 を呼び出して COM ヒープに文字配列を割り当てますCoTaskMemAlloc

返された文字列が実際に静的に割り当てられており、割り当てを解除する必要がない場合は、次の 2 つの明白な選択肢があります。

  1. IntPtrマネージ コードで、とを使用するように切り替えPtrToStringAnsiます。PtrToStringAnsi呼び出しをラッパー内に移動し、_GetClassName以前と同じパブリック インターフェイスを提示するため、これは簡単に実行できます。
  2. CoTaskMemAllocネイティブ コードでは、静的バッファーを呼び出してから、ヒープに割り当てられたバッファーにコピーします。
于 2013-08-29T09:58:03.753 に答える
2

これはフレームワークとは何の関係もありません。すべて Windows のバージョンと関係があります。古い 2.0 プロジェクトは、おそらく XP で実行されていました。XP では 4.5 が利用できないため、新しいオペレーティング システムを使用していることがわかります。

XP には、はるかに寛大なヒープ マネージャーがあります。pinvoke マーシャラーが CoTaskMemFree() を呼び出して文字列を解放し、無効なポインターを無視すると、肩をすくめるだけです。Vista 以降では、肩をすくめて AccessViolation をスローすることはなくなりました。このナンセンスな態度が、Vista の評判が悪い理由の 1 つです。プログラマーがプログラムのバグを修正するのに十分な時間があったため、現在では正常と見なされています。

あなたが見つけた回避策は正しいものです。マーシャラーは IntPtr のメモリを解放しようとしません。const char*これは、C コードが解放する必要のないa を返した場合にのみ、実際に良い結果になることに注意してください。そうでない場合は、永続的なメモリ リークが発生しています。

于 2013-08-29T13:08:10.623 に答える