C#からFORTRAN DLLを呼び出す保護されたメモリ違反
質問
私は、FORTRANコードからコンパイルされたレガシーDLLを呼び出そうとしています。私はInteropを初めて使用しますが、それに関するいくつかの記事を読みましたが、私のケースはかなり簡単なはずです。
本当に呼び出したいメソッドには複雑なメソッドシグネチャがありますが、保護されたメモリ違反を取得せずにこの単純なGetVersionメソッドを呼び出すこともできません。
ここに私のDllImportコードがあります:
[DllImport("GeoConvert.dll",
EntryPoint="_get_version@4",
CallingConvention=CallingConvention.StdCall)]
public static extern void GetGeoConvertVersion([MarshalAs(UnmanagedType.LPStr, SizeConst=8)]
ref string version);
FORTRANコードは次のとおりです。
SUBROUTINE GetVer( VRSION )
C
!MS$DEFINE MSDLL
!MS$IF DEFINED (MSDLL)
ENTRY Get_Version (VRSION)
!MS$ATTRIBUTES DLLEXPORT,STDCALL :: Get_Version
!MS$ATTRIBUTES REFERENCE :: VRSION
!MS$ENDIF
!MS$UNDEFINE MSDLL
C
CHARACTER*8 VRSION
C
VRSION = '1.0a_FhC'
C
RETURN
END
失敗したユニットテストは次のとおりです。
[Test]
public void TestGetVersion()
{
string version = "";
LatLonUtils.GetGeoConvertVersion(ref version);
StringAssert.IsNonEmpty(version);
}
次のエラーメッセージが表示されます。
System.AccessViolationException
Message: Attempted to read or write protected memory.
This is often an indication that other memory is corrupt.
私が試した他のこと:
- デフォルトのマーシャリングの使用
- 文字列ではなくchar []を渡す(代わりにメソッドシグネチャエラーを取得する)
解決 2
OK、問題なく動作しました。理由はわかりませんが、これは機能します:
[DllImport("GeoConvert.dll",
EntryPoint="_get_version@4",
CallingConvention=CallingConvention.StdCall)]
public static extern void GetGeoConvertVersion([MarshalAs(UnmanagedType.LPArray)]
byte[] version);
このテストでは:
[Test]
public void TestGetVersion()
{
//string version = "";
byte[] version = new byte[8];
LatLonUtils.GetGeoConvertVersion(version);
char[] versionChars = System.Text.Encoding.ASCII.GetChars(version);
string versionString = new string(versionChars);
}
他のヒント
... snip ... OK、問題なく動作しました。理由はわかりませんが、これは機能します: ...スニップ...
FORTRANコードで使用されているセマンティックであるため、参照渡しする必要があります。クライアントコードは、戻り値を使用する代わりに、FORTRANコードが書き込むバッファーを渡します。
... snip ... !MS $ ATTRIBUTESリファレンス:: VRSION ...スニップ...
FORTRANコードのこの属性は、このパラメーターが参照によって渡されることを指定します。つまり、FORTRANコードはこのアドレスに書き込みます。 DllImportがref値としても宣言していない場合、アクセス違反が発生します。
StringBuilderを使用してみましたか?
StringBuilderとして文字列を作成し、それをdll関数に渡します。
どのMarashllingステートメントを使用するかわからない。おそらくデフォルトが機能する可能性がある。
ご覧ください:マーシャルC ++“ string” C#P / Invokeのクラス
こちらも役立つ記事をご覧ください:相互運用マーシャリング
FORTRANコンパイラーがないため、この解決策を試すことはできませんが、これはあなたに役立つと思います:
[DllImport("GeoConvert.dll",
EntryPoint="_get_version@4",
CallingConvention=CallingConvention.StdCall,
CharSet=CharSet.Ansi)]
public static extern void GetGeoConvertVersion(StringBuilder version);
皆さん、ありがとうございます。私はc#から文字列をfortran dllのサブルーチンに渡そうとしていましたが、このメソッドは他の多くの中で唯一機能するものでした