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

Error Calling DLL

DavidWhite
Valued Contributor II
880 Views

I am trying to load a Fortran DLL at runtime, using this code:

INTEGER :: p
pointer (q,AWAPROPS_SUB)
p = loadlibrary("AWAProps.dll"C)
NAME = "AWAPropsLicense_F"
q = getprocaddress(p, TRIM(NAME)//""C)
CALL AWAPROPS_SUB(IResult, CopyR)

(I am also checking that p and q are non-zero)

When I call this, I get the following error:

Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call.  This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention.

I suspect this is because the DLL has been set up for calling from VBA, and the DLLEXPORTS are like:

Subroutine AWAPropsLicense_F(IResult, CopyR)
!DEC$ ATTRIBUTES DLLEXPORT, STDCALL, ALIAS:'AWAPropsLicense_F' :: AWAPropsLicense_F
!DEC$ ATTRIBUTES REFERENCE :: CopyR
IMPLICIT NONE
INTEGER, INTENT(OUT) :: IResult(1)
CHARACTER(LEN=33), INTENT(OUT) :: CopyR

What do I need to add to my Fortran calling program to allow for this different calling convention?

Also, within the same run, can I reuse the pointer q to call different routines (with different numbers of arguments), or do need a separate pointer for each?

Thanks,

David

0 Kudos
3 Replies
Steven_L_Intel1
Employee
880 Views

You need to tell the compiler that the procedure pointer is to be called with STDCALL. The way I would recommend this is to adopt the method shown in the provided example DLL\Dynamic_Load.

First, it declares an abstract interface for the procedure:

abstract interface
  function USERFUNC_int (arg)
  integer USERFUNC_int
  integer, intent(IN) :: arg
  end function USERFUNC_int
end interface

In your case, you'd want to add:

!DEC$ ATTRIBUTES STDCALL, REFERENCE :: USERFUNC_int

Then it uses the Fortran 2003 procedure pointer feature (ISO_C_BINDING needed):

procedure(USERFUNC_int), pointer :: USERFUNC
integer(C_INTPTR_T) :: p_USERFUNC
...
p_USERFUNC = GetProcAddress (hModule=dll_handle, lpProcName="USERFUNC"//C_NULL_CHAR)
call C_F_PROCPOINTER (TRANSFER(p_USERFUNC, C_NULL_FUNPTR), USERFUNC)

Yes, with a STDCALL routine, you need a different interface for each set of arguments.

It might also work with your existing code if you added an EXTERNAL declaration for AWAPROPS_sub and then added an ATTRIBUTES STDCALL for the same name. I'm not 100% sure of this. You could use the same pointer for different numbers of arguments, but you had better be sure that you're passing the correct number to each routine.

0 Kudos
DavidWhite
Valued Contributor II
880 Views

Steve,

When I looked at the Dynamic_Load example before I wasn't sure how to use the abstract interface for my case where all my entry points are subroutines.  In addition, I have 130 or so different entry points, so setting up 130 interface blocks isn't trivial.

thanks,

David

0 Kudos
Steven_L_Intel1
Employee
880 Views

You don't need 130 different interface blocks, just one for each combination of argument number/types. If that is still daunting, you can probably make it work with EXTERNAL and ATTRIBUTES STDCALL.

0 Kudos
Reply