I have an existing application that uses Delphi Pascal to create a windows GUI that gathers data and calls fortran routines from a fortran DLL. The sequence is complicated by the fact that there is an additional dll written in pascal that is used as the interface between the fortran and the pascal program.
The pascal program has function calls that get and put data to an SQL database. The fortran dll gets access to those functions through the routines defined in the pascal dll.
When the call is made to pass the collected data to the fortran dll to do the engineering calculations, the pascal dll is first loaded and initialized with some related data. Then the fortran dll function is called and the first thing it does is get the pointers to the functions in the pascal dll it needs to do its job (it will get and put data to the SQL database.)
What I want to do is make the pascal dll routines a part of the main program rather than a dll.
How do I change the interface definition in the fortran code to do this?
Here is more info
The fortran dll has the following definition of a function it expects from an external dll it will read.
integer*4 function GetPointer(i,irra)
!dec$ attributes stdcall, dllimport :: GetPointer
!dec$ attributes reference :: GetPointer, irra, i
and following that is the following line:
The fortran dll has the following exported function defined:
SUBROUTINE CORETG(nfunci, erri, pblui)
!DEC$ ATTRIBUTES STDCALL, DLLEXPORT, REFERENCE, ALIAS:'_CORETG' :: CORETG
One of its first tasks is to load the pascal dll and get the address of a function in it:
pdllDelphi = LoadLibrary(dllaux) (where dllaux is a string containing the name of the dll)
pGetPointer = GetProcAddress(pdllDelphi, 'GetPointer'C)
And this function then calls GetPointer:
So when the main program runs, it loads the two dlls discussed above, calls an initialization function in the pascal dll that populates an array of string for later use. Then it calls the exported fortran dll function CoretG which then calls the pascal function GetPointer, which in turn calls functions that are defined in the main program.
I believe this structure developed historically, probably with the fortran code used many years ago, and the windows application wrapped around it. What I want to do is eliminated the pascall dll, and let the fortran dll call the main program functions directly.
I may not be quite following what you are trying to do, but how do you anticipate that the Fortran DLL would get the addresses of the procedures in the main program that it needs to call?
There are a couple of options, but one that is more typical is that the main program would pass to the DLL the relevant addresses as arguments of a call to a procedure in the DLL. Is that what you envisage?
If memory serves me correctly, you can also use GetProcAddress to look up the addresses of explicitly exported procedures in executable modules as well, but this is rather atypical, and results in a tight binding between the DLL and executable that sort of defeats the point of having stuff in a DLL in the first place. The existence of the pascal DLL may have been a work around for this.
There are a number of possibilities, and you need to decide which fits your needs best. The simplest solution, if feasible, is to pass procedure pointers (of your callback procedures, for instance) as arguments from the EXE to the DLL. The DLL can call those callback procedures in the EXE and can also call procedures from other DLLs. If you can do this, the DLL can be built before you have written any code for the EXE, and when you build the EXE later you can use the import library that was created when you built the DLL.
If you do not wish to pass procedure pointers as arguments, the build process gets a bit more complicated. There is a recursive dependence between the EXE and DLL, and manual intervention is needed to resolve that recursion.
For simplicity, let us set aside Delphi, STDCALL and the database, and consider a situation where you build a Fortran EXE that calls a subroutine from a Fortran DLL, and the DLL calls a routine in the EXE. Here is the source code for the DLL:
subroutine fdsub(i) !dec$ attributes dllexport :: fdsub ! this procedure !dec$ attributes dllimport :: fmsub ! in EXE call fmsub(i,j) write(*,*)'In fdsub, i,j = ',i,j return end subroutine
We attempt to build the DLL from this:
ifort /LD fdsub.f90
The DLL build fails, but there is partial success, in that the DLL's import library fdsub.lib is produced.
We can now build the EXE and produce its exports library. The code for the EXE:
program tst integer i !dec$ attributes dllimport :: fdsub i=17 call fdsub(i) ! DLL procedure write(*,*)'Back in main' end program subroutine fmsub(i,j) ! will get called from DLL !dec$ attributes dllexport :: fmsub j=i*i write(*,*)'In fmsub, i,j = ',i,j return end subroutine
The command to build the EXE:
ifort fexe.f90 fdsub.lib
The EXE now gets built, and its export library is generated. We now repeat the DLL build, but with this export library added, and this time the DLL build succeeds.
ifort /LD fdsub.f90 fexe.lib
We now run the EXE, and see the output
In fmsub, i,j = 17 289
In fdsub, i,j = 17 289
Back in main
You can build on this, replacing the static procedure names and references with calls to GetProcAddress, LoadLibrary, etc.
Thanks, guys. I had limited experience with DLL's when I got handed this project. Yesterday when I packed up to go home, I still did not have any responses to my question but I was still thinking about it. Overnight I came to the conclusion that the solution was to pass in the pointers to the functions that I need to use in the fortran DLL. Which is exactly the solution you guys gave me. I had just finished outlining my solution to a fellow developer who has equally as little experience with DLL's as I when I read your responses. So thanks for the help. I now have a plan and will be able to make a lot of progress today. Plus I can become a DLL expert!