4

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[] を渡す (代わりにメソッド シグネチャ エラーを取得する)
4

5 に答える 5

3

...中略...OK、動作するようになりました。問題は ref を通過していました。理由はわかりませんが、これでうまくいきます: ...中略...

FORTRAN コードで使用されているセマンティックであるため、参照渡しする必要があります。クライアント コードは、戻り値を使用する代わりに、FORTRAN コードが書き込むバッファを渡しています。

...省略... !MS$ATTRIBUTES REFERENCE :: VRSION ...省略...

FORTRAN コードのこの属性は、このパラメーターが参照によって渡されることを指定します。これは、FORTRAN コードがこのアドレスに書き込むことを意味します。DllImport で ref 値としても宣言されていない場合は、アクセス違反が発生します。

于 2008-10-23T17:56:58.817 に答える
1

OK、動作するようになりました。問題は ref の通過でした。理由はわかりませんが、これは機能します:

[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);
    }
于 2008-10-23T12:22:49.833 に答える
0

StringBuilderを使用してみましたか?

StringBuilderとしてStringを作成し、それをdll関数に渡します。

使用するMarashllingステートメントがわからない場合は、デフォルトで機能する可能性があります。

ご覧ください:C#P/InvokeのマーシャルC++「文字列」クラス

ここに役立つかもしれない良い記事があります:相互運用マーシャリング

于 2008-10-23T12:13:56.920 に答える
0

みんなありがとう、私はC#からfortran dllのサブルーチンに文字列を渡そうとしていましたが、このメソッドは他の多くの中で唯一機能していました

于 2011-02-11T06:48:01.527 に答える
0

FORTRAN コンパイラを持っていないため、この解決策を試すことはできませんが、これでうまくいくと思います。

    [DllImport("GeoConvert.dll", 
            EntryPoint="_get_version@4", 
            CallingConvention=CallingConvention.StdCall,
            CharSet=CharSet.Ansi)]
    public static extern void GetGeoConvertVersion(StringBuilder version);
于 2008-10-23T14:14:32.917 に答える