Wrong values when calling function in Fortran

Wrong values when calling function in Fortran

I am new to Fortran and have to build an interface between C# and Fortran for that. The passing of the argument from C# to Fortran works without problems, but the easier task, passing values inside the Fortran-code does not want to work...

When I want to pass parameters from one Fortran function to another, in this case from VdiFunctionRunner to TGA_810, the called function does not have the correct paramter-values.

This is a stripped down version of my code:

module VdiFunctionRunnerMain
  implicit none

  contains       
    integer function VdiFunctionRunner (inXTGA, vdiCwertCllbak, vdiIwertCllbak, vdiRwertCllbak, vdiCwert2Cllbak, vdiIwert2Cllbak, vdiRwert2Cllbak, vdiErsterCllBak, vdiLetzterCllBak, vdiTestFuncCllBak)
      !DEC$ ATTRIBUTES DLLEXPORT, StdCall :: VdiFunctionRunner
      !DEC$ ATTRIBUTES REFERENCE :: inXTGA, vdiCwertCllbak, vdiIwertCllbak, vdiRwertCllbak, vdiCwert2Cllbak, vdiIwert2Cllbak, vdiRwert2Cllbak, vdiErsterCllBak, vdiLetzterCllBak, vdiTestFuncCllBak
      implicit none      
      external vdiCwertCllbak, vdiIwertCllbak, vdiRwertCllbak, vdiCwert2Cllbak, vdiIwert2Cllbak, vdiRwert2Cllbak, vdiErsterCllBak, vdiLetzterCllBak, vdiTestFuncCllBak

      CHARACTER (256) :: inXTGA, copyXTGA
      CHARACTER (256) :: ARRAY_810(10), retValue, satzArt, satzArt2
      CHARACTER (256) :: cWertCallBackRet

      copyXTGA = 'test'      
      nrReturnValues = tga_810(copyXTGA, 1)
      return
    end function VdiFunctionRunner

    integer function tga_810(xtga, testNr)
      character (256), intent(in)  :: xtga
      integer, intent(in) :: testNr
      return
    end function tga_810

end module VdiFunctionRunnerMain

As you see the passed values should be 'test' and 1, but in the debugger I get only the first character of the array ('t') and testNr = 0. I guess there is no error in writing the code, isn't it? When the code is correct, does somebody know if this can be a compiler problem or something like that?

I also wrote out the parameters to a file and I get the same values, so it can't be a problem of the debugger.

I am using the Intel 11 compiler.

Help is very much appreciated

5 posts / 0 new
Last post
For more complete information about compiler optimizations, see our Optimization Notice.

You may think that the C# calling Fortran works fine, but you may be unknowingly corrupting the stack if you do not have the calling convention exactly right. This will show up when other calls make use of the corrupted stack. Since your function parameters or their addresses are placed on the stack, and you are not getting the values you expect from the stack, corruption of the stack is a possible explanation.

Thanks for your answer.I stripped down the code more to find the problem.I removed all functionpointers and variables, that are not related to that problem, but I still get that.

    module VdiFunctionRunnerMain
      implicit none
    
      contains       
        integer function VdiFunctionRunner ()
          !DEC$ ATTRIBUTES DLLEXPORT, StdCall :: VdiFunctionRunner
          implicit none      
          
          CHARACTER (256) :: inXTGA, copyXTGA
          CHARACTER (256) :: ARRAY_810(10)
       
          copyXTGA = 'test'      
          nrReturnValues = tga_810(copyXTGA, 1)
          VdiFunctionRunner = nrReturnValues
          return
        end function VdiFunctionRunner
    
        integer function tga_810(xtga, testNr)
          character (256), intent(in)  :: xtga
          integer, intent(in) :: testNr
          tga_810=2
        end function tga_810
        
    end module VdiFunctionRunnerMain

But I still have the same problem, so I do not think, that this is a problem of the calling conventions.My interface-code:

[DllImport("VdiFunctionRunnerDll.dll", EntryPoint = "_VDIFUNCTIONRUNNERMAIN_mp_vdifunctionrunner@0", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
    public static extern int VdiFunctionRunner()

The Fortran-code also uses StdCall.Are there other things to look on, which can cause such behaviour?

I'll mention that I get a real bad nervous twitch when I have to supply mangled names to an interface block. The mangling is how caller and callee agree on how many bytes to push and pop during the call.

For grins, you could quickly change your C and Fortran switches to use /iface:cref, /names:lowercase and /assume:underscore, and give it a try. If it works, you have a problem with your stdcall.

Maybe you stripped/chopped a bit too far - the Fortran code you've presented doesn't compile as there's no declaration for nrReturnValues in either example despite the IMPLICIT NONE's (note that while it doesn't hurt the implicit none in the subroutine is redundant as the hosting module already has it).

If that variable had an inconsistent declaration then you could get surprising behaviour. For what its worth, after I fixed that error and provided a little C# main function, your code "worked for me" with 12.1.1.

Compiler options and the target platform (x86/Intel 64) will influence this sort of stuff - consider posting the build log to provide that sort of information. The stack corruption that anthony mentions can also be due to code that is relatively distant from the code that is executing when visible problems occur - any testing or posting of snippets needs to take that into account.

As jeremy_h implies, unless you have very strong reasons to the contrary, linking via the mangled name (and to a lesser extent, the default Fortran binding) is an invitation for future pain and suffering. As you are using C# you are are clearly not stuck with FORTRAN compilers, punched cards, etc, from the glorious days of yore, so consider using the F2003 C-interoperability features to make your life much safer. An example attached below, but note very well - I know absolutely nothing about C# so take it all with a grain of salt. For your original example you should also consider the calling convention that applies to the callbacks that you are passing (?) - that consideration may reveal some "strong reasons" as referred to at the start of this paragraph.

using System;
using System.Runtime.InteropServices;

namespace csharp
{
    class Program
    {
        [DllImport("fortran.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
        public static extern int VdiFunctionRunner();

        static void Main(string[] args)
        {
            System.Console.Write(VdiFunctionRunner());                
        }
    }
}

module VdiFunctionRunnerMain
  implicit none    
  contains       
  function VdiFunctionRunner () BIND(C, NAME='VdiFunctionRunner')  &
      RESULT(my_result)
  !DEC$ ATTRIBUTES DLLEXPORT :: VdiFunctionRunner
    USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_INT          
    CHARACTER (256) :: copyXTGA
    INTEGER(C_INT) :: my_result       
    copyXTGA = 'test'      
    my_result = tga_810(copyXTGA, 1)
  end function VdiFunctionRunner
    
  integer function tga_810(xtga, testNr)
    character(*), intent(in)  :: xtga   ! assumes length of actual argument
    integer, intent(in) :: testNr    
    tga_810=2
  end function tga_810        
end module VdiFunctionRunnerMain
  

Leave a Comment

Please sign in to add a comment. Not a member? Join today