Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

How DLL Export from file Fortran 77

BABA767
Novice
4,949 Views

Hi everyone,
I have some Fortran 77 sources file that I'd like to compile as a DLL so I can call functions from an external GUI to interact with Fortran DLLs.
I compiled the DLL with the latest version of the Intel compiler, and apparently the DLL is generated correctly and called from the external GUI (in C#).
But I've noticed a behavioral issue with the DLL because it doesn't return the expected value of a variable. The problem isn't related to the source file, since compiling the .for file in Visual Studio returns the correct values.


I believe the problem is in the compiler settings or in the directives inserted in the .for file; can anyone help me resolve the issue?
Thanks,
Valerio

Labels (3)
12 Replies
andrew_4619
Honored Contributor III
4,942 Views
I suggest you attach the call and declarations from c# and the header of the Fortran routine in Fortran. Maybe you have a mismatch but we would be guessing.
0 Kudos
BABA767
Novice
4,920 Views

Ok I report the extract of my two files .for and .cs:

file .for 77 (two subroutine)

      SUBROUTINE B767AH
      IMPLICIT NONE
.....here there are global vars
      ENTRY  AHYD1()
!DEC$ ATTRIBUTES DLLEXPORT :: AHYD1
 
      DTPA = 13
...here rest of code
     
      SUBROUTINE GET_RDEMPRESS(val)
!DEC$ ATTRIBUTES DLLEXPORT :: GET_RDEMPRESS      
      IMPLICIT NONE
      REAL*4   AHP01
       LOGICAL*1  DUM0000001(1022488)
      COMMON   /XRFTEST   /
     & DUM0000001,AHP01
      REAL*4 val
      val = AHP01
      END
 
import of function from DLL (file c#)
 
        [DllImport("b767ag.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern void AHYD1();
 
        [DllImport("b767ag.dll", CallingConvention = CallingConvention.StdCall)]
        public static extern void GET_RDEMPRESS([Out] float[] x);
 
When i call cyclically the main routine (at ENTRY point) I read correctly value 13 come from AHP01 by GET_RDEMPRESS but in true that value should increase infact if I run the routine directly from the .for file into Visual code that value increase. So i presume that I'm missing or doing something wrong in declaration or in the compilation. Maybe I've to use ifort instead of ifx as my .for 77 is too older?
 
I compiled my .dll with these commands:
ifx /c myfile.for ->create the file .obj
ifx /dll myfile.obj /link /out:myfile.dll ->create the file .dll
 
 
BABA767
Novice
4,662 Views

I've solved using /Qsave parameter during the compilation, this permits to allocate the local variables into the memory without pass they into the stack.

cheers!

V.

0 Kudos
andrew_4619
Honored Contributor III
4,650 Views
Well that is both good and bad. You haven't fixed it really you have just hidden the problem which is sat there waiting to bite. I would to some builds and runs with the checks for uninitialised variables.
0 Kudos
jimdempseyatthecove
Honored Contributor III
4,504 Views

>>I've solved using /Qsave parameter during the compilation

Do you intend to (later) call your DLL from a multi-threaded process?

If so, then the /Qsave may be problematic.

 

Jim

0 Kudos
BABA767
Novice
4,116 Views

Hi,

yes I'm using multi-thread but each subroutine with /Qsave is called from only one dedicated thread so local variables should not be at risk of being overwritten.

Valerio

0 Kudos
BABA767
Novice
4,570 Views

Why might I have hidden problems? The Fortran source is secure and tested, including local variables. Even uninitialized variables will be assigned a value, the important is that the value is not lost.. or not?

 

 

0 Kudos
andrew_4619
Honored Contributor III
4,537 Views
Well if you have an issue with mismatched variables on calls or calling conventions or uninitialised variables changing the memory management or layout does not stop corruption it might just mean what is being corrupted is not important but after further code changes.......
0 Kudos
BABA767
Novice
4,095 Views

ok that's correct.

Just for info I'm using CallingConvention "Cdecl" C declaration so the caller (my  C# GUI) clear the stack; in the file .for77 use this directive -> !DEC$ ATTRIBUTES DLLEXPORT :: name_of _subroutine

It's correct use this or better STDCALL?  I honestly don't know which one is better to use. I only understood that they must be declared in the same way to correctly manage stack cleaning.

 

 

0 Kudos
GVautier
New Contributor III
3,982 Views

As far as I know,

with cdecl convention, arguments are pushed to the stack before calling the procedure who doesn't have to know the actual size of arguments to return correctly. That allows variable number of arguments.

with stdcall convention,, it's the opposite so the called procedure use the declared size of arguments to get the return address from the stack. It's why the arguments size is added to the procedure name to avoid mismatches. Optional arguments must be declared and pushed as null pointer if absent to preserve the argument size.

cdecl guaranties that the procedure will return correctly even if arguments number or size is incorrect may be silently.

stdcall will crash if arguments number or size is incorrect.

BABA767
Novice
3,889 Views

Okk thk for your advice..I'll use Cdecl convention..;-)

0 Kudos
MartinSpeare
Beginner
2,887 Views

>>I've solved using /Qsave parameter during the compilation

or seond sol is this 

SUBROUTINE B767AH
      IMPLICIT NONE
.....here there are global vars
      ENTRY  AHYD1()
!DEC$ ATTRIBUTES DLLEXPORT :: AHYD1
 
      DTPA = 13
...here rest of code
     
      SUBROUTINE GET_RDEMPRESS(val)
!DEC$ ATTRIBUTES DLLEXPORT :: GET_RDEMPRESS      
      IMPLICIT NONE
      REAL*4   AHP01
       LOGICAL*1  DUM0000001(1022488)
      COMMON   /XRFTEST   /
     & DUM0000001,AHP01
      REAL*4 val
      val = AHP01
      END
0 Kudos
Reply