- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page