Have I missed something in FORTRAN?
Have I missed something in FORTRAN?
(OP)
I have been programming in FORTRAN for 47 years, from FORTRAN II up to Compaq Visual Fortran (CVF) and IVF, but recently came up against this simple thing I wanted to do, which I cannot solve either in FORTRAN or Assembler.
I have the address of a routine that I want to call (it's in a .DLL provided by somebody else).
I have the address as a 32 bit value in a Fortran Integer SUBNAM.
But you cannot write CALL SUBNAM( argument list )
This is because I actually want an indirect call, not to the address of SUBNAM, but to the address it contains.
So I went into assembler and passed SUBNAM in as an argument, and tried to JMP to that address after saving a return address on the stack. I also tried pushing it on the stack and just doing a RET.
No dice. ACCESS VIOLATION!!
One reason may be that the assembly code may be trying to jump to an absolute address, but this is not allowed. You are only allowed to touch virtual addresses. And user ring programs are not allowed to have access to the mapping from one to the other.
Does anyone know how to solve this?
Of course this is all Microsoft and Intel's fault for making such a dog's breakfast of the PC programming model.
I have the address of a routine that I want to call (it's in a .DLL provided by somebody else).
I have the address as a 32 bit value in a Fortran Integer SUBNAM.
But you cannot write CALL SUBNAM( argument list )
This is because I actually want an indirect call, not to the address of SUBNAM, but to the address it contains.
So I went into assembler and passed SUBNAM in as an argument, and tried to JMP to that address after saving a return address on the stack. I also tried pushing it on the stack and just doing a RET.
No dice. ACCESS VIOLATION!!
One reason may be that the assembly code may be trying to jump to an absolute address, but this is not allowed. You are only allowed to touch virtual addresses. And user ring programs are not allowed to have access to the mapping from one to the other.
Does anyone know how to solve this?
Of course this is all Microsoft and Intel's fault for making such a dog's breakfast of the PC programming model.
RE: Have I missed something in FORTRAN?
CODE
RE: Have I missed something in FORTRAN?
CODE
Both GNU and PS4/DVF/CVF/IVF have a function called LOC which gets the address of the function, similar to what comes back from GetProcAddress which is used with LoadLibrary which you can use for late loading DLLs.
RE: Have I missed something in FORTRAN?
(1) in the IDE, do "Add to Project". "files" amd locate the .dll you want to use. Alternatively copy the .dll to the program's workspace.
(2) put
USE kernel32
at the start of the main program to provide access to Windows APIs.
(3) For each subroutine or function that the .dll provides that you want to use, provide an INTERFACE block like
INTERFACE
SUBROUTINE NAME1(...argument list...)
...declare the types of the arguments....
If the .dll routines use C calling conventions, add
!DEC$ ATTRIBUTES CALLER
(this means that it is the calling program that will clean up the stack)
I have not found it necessary in the interface to specify whether arguments are
passed by value or address. This happens in the call, later.)
END SUBROUTINE NAME1
END INTERFACE
(replace SUBROUTINE by FUNCTION if it returns a value, as most C is written to do)
The names such as NAME1 above are your choice.
(3) In the program declarations,include statements like
POINTER(P1,NAME1)
for every subroutine or function that the .dll provides that you want to use. "NAME1" is the same name as you chose for the associated INTERFACE block
Also include a declaration
POINTER(PLIB,ILIB)
(4) in the program, load the .dll by including the windows API statement
PLIB-LOADLIBRARY("dllname.dll")
the logical variable STATUS will come back true of the .dll was loaded corrctly.
(5) For each subroutine or function you want to use, include a windws API statement like
P1=GETPROCADDRESS(PLIB,"subnam1"C)
"subnam1" is what the routine is called (case sensitive) in the .dll, not your name choice. They can be the same however, but FORTRAN will ignore the case. The suffix C passes the string as a C string, that is null-terminated, and no length passed. This sets up the subroutine or function you chose to call by the FORTRAN name NAME1.
(6) Now you can call the subroutine NAME1 in your program by writing
CALL NAME1(....argument list...). where for every scalar variable that is an input argument such as "N" you write %VAL(N) to make FORRTAN pass it by value if the .dll requires it. Output variables that are returned as well as strings and arrays are passed by address so do not need %VAL()
For a function you can write STATUS=NAME1(....argument list...) likewise
(7) Now, if you want to call such a subroutine or function inside another subroutine other than the program in which the above initialization was done, you have to repeat the declarations like:
POINTER(P1,NAME1)
in the subroutine in which you want to use NAME1 and you have to pass the pointers P1 in in the argumant list or better, by declaring a NAMED COMMON to hold them, thus avoiding encumbering the argument list
RE: Have I missed something in FORTRAN?
Step 4 should read
(4) in the program, load the .dll by including the windows API statement
PLIB=LOADLIBRARY("dllname.dll")
PLIB comes back as zero if the .dll failed to load.
RE: Have I missed something in FORTRAN?
RE: Have I missed something in FORTRAN?
(8) FreeLibrary(PLIB) to unload the DLL