none
Problem calling Fortran code from C# RRS feed

  • Question

  • Hi all,

    I am having a problem that I can't seem to find an answer to. I have a .dll file which was built in MS Developer Studio, based on Fortran code. Unfortunately I don't have the source code which generated the .dll file. I know for a fact that this .dll is working, as the main method in it has been called successfully from C++ Borland Builder 6.0. The successful method call from C++ is something like:

    double a = 10;
    double b = 20;
    double array[1000];
    int i = RunDSPBatch(
    a, b, array);

    The method takes two doubles, does some calculations, and puts the results of the calculation in the array. I figured that doing the same thing in C# would be straight-forward, but this has not proved to be the case, so I need to find out what I am doing wrong. My C# looks like:

    public class CallDLL
    {
       [DllImport("Dllfile.dll", EntryPoint="RunDSPBatch", ExactSpelling=true, CallingConvention=CallingConvention.StdCall]
       public static extern int DSPBatch(ref double a, ref double b, ref double[] array);

       public int Test()
       {
           double a=10,b=20;
           double[] array = new double[1000];
           return DSPBatch(ref a, ref b, ref array);
       }
    }

    When I run Test(), the call to DSPBatch is executed, and the program runs for some 30 seconds, then closes without any Exceptions ever being thrown. So I tried to debug the process using MS Developer Studio, and the message I get then is "0xC0000005: Access Violation". I feel sure that the problem is related to the passing of the array parameter, but I'm not sure how this should be resolved. I have tried using [MarshalAs(UnmanagedType.LPArray)], [In, Out], and also tried with IntPtr instead of double[], but with the same unsuccessful result. Any help is very much appreciated!
    Tuesday, February 3, 2009 12:43 PM

Answers

  • As I mentioned, C++/CLI wrapper.
    Hans Passant.
    • Marked as answer by Zhi-Xin Ye Monday, February 9, 2009 2:42 AM
    Friday, February 6, 2009 10:46 AM
    Moderator
  • Hans is correct. The problem you are running into is that __fastcall allocates parameters (as much as it can) on the CPU registers rather than the stack (like the other calling conventions). P/Invoke doesn't support this calling convention at all (the option is available on the CallingConvention enumeration, but it's not supported). To be fair, _fastcall is not actually faster than stack allocation, as on the average (with exception of a some narrow edge cases), __fastcall underperforms __cdecl and __stdcall, so few people still use it.

    So, you basically have two options:
    1) Do like Hans suggests and create a CLR-compatible class in C++/CLI. This class exposes CLR-compatible methods that wrap the DLL functions.
    2) If you're really keen on using P/Invoke and/or you don't want to muck with C++ in managed mode, then you can create a DLL in C++ that wraps the Fortran DLL functions with __stdcall export functions. You can then call the __stdcall wrappers using P/Invoke.
    -Rob Teixeira
    • Marked as answer by Zhi-Xin Ye Monday, February 9, 2009 2:42 AM
    Friday, February 6, 2009 5:55 PM

All replies

  • Drop the "ref" keywords from all three arguments.
    Hans Passant.
    Tuesday, February 3, 2009 1:14 PM
    Moderator
  • If i do that, I get an Exception saying: "Atteptemted to read or write protected memory"
    Tuesday, February 3, 2009 1:33 PM
  • Your next target is the calling convention.  Show how DSPBatch() is declared in the C/C++ code.  The default calling convention is Cdecl.
    Hans Passant.
    Tuesday, February 3, 2009 9:17 PM
    Moderator
  • The declaration of RunDSPBatch in C++ (header file):

    int __fastcall RunDSPBatch(double a, double b, double *array);

    along with:

    extern "C" int DSPBatch(double *a, double *b, double *array);

    Changing the calling convention to Cdecl doesn't seem to have any effect
    Wednesday, February 4, 2009 8:22 AM
  • Bummer, you can't use P/Invoke.  You'll have to write a wrapper for it, using the C++/CLI syntax.
    Hans Passant.
    Wednesday, February 4, 2009 11:29 AM
    Moderator
  • Haven't tried using CLI, but I tried to make a simple project and build it using MSVSC++, but my problem there it seems is the required .lib file neccessary to link to the .dll. I have the .lib file which worked in my Borland C++ project, but this is not compliant with MSVSC++ as I have understood. So I tried creating a lib file myself, based on the following recipe:

    http://www.signalogic.com/index.pl?page=libcreate

    But is doesn't seem that the created .lib file is ok, because when compiling in MSVSC++, it does not accept the call to the method declared using 'extern "C" DSPBatch'.

    I have not used CLI before, but I assume I would still need a .lib file for linking the .dll? I found a tool called 'Dll to lib', but this did not work when loading the .dll. Are there any other free tools available for generating a .lib file from a .dll?
    Thursday, February 5, 2009 1:12 PM
  • Contact the vendor from which you purchased the license for this DLL and ask for the .h and .lib files you need to use their product.
    Hans Passant.
    Thursday, February 5, 2009 2:10 PM
    Moderator
  • Contacting the vendor is impossible as they do not exist any more, but I have managed to create a proper .lib file anyway, based on this recipe:

    http://www.coderetard.com/2009/01/21/generate-a-lib-from-a-dll-with-visual-studio/

    So now I have actually been able to run the function successfully from MSVSC++ (it does however not return correct results yet, but at least I am much closer to a solution). But assuming that I am able to produce working code from MSVSC++, is it then possible to generate a .dll and invoking this from C# using P/Invoke?
    Friday, February 6, 2009 8:10 AM
  • As I mentioned, C++/CLI wrapper.
    Hans Passant.
    • Marked as answer by Zhi-Xin Ye Monday, February 9, 2009 2:42 AM
    Friday, February 6, 2009 10:46 AM
    Moderator
  • Hans is correct. The problem you are running into is that __fastcall allocates parameters (as much as it can) on the CPU registers rather than the stack (like the other calling conventions). P/Invoke doesn't support this calling convention at all (the option is available on the CallingConvention enumeration, but it's not supported). To be fair, _fastcall is not actually faster than stack allocation, as on the average (with exception of a some narrow edge cases), __fastcall underperforms __cdecl and __stdcall, so few people still use it.

    So, you basically have two options:
    1) Do like Hans suggests and create a CLR-compatible class in C++/CLI. This class exposes CLR-compatible methods that wrap the DLL functions.
    2) If you're really keen on using P/Invoke and/or you don't want to muck with C++ in managed mode, then you can create a DLL in C++ that wraps the Fortran DLL functions with __stdcall export functions. You can then call the __stdcall wrappers using P/Invoke.
    -Rob Teixeira
    • Marked as answer by Zhi-Xin Ye Monday, February 9, 2009 2:42 AM
    Friday, February 6, 2009 5:55 PM
  • Thank you both for the answers, I will try to create a C++/CLI wrapper now that I am able to create a working .lib file for the .dll
    Monday, February 9, 2009 8:49 AM