I want to learn how to make a DLL share variables in a module USEd by both the DLL and its calling main program. From examples I can find I have put together a very small test program which unfortunately does not work. Here are the mainline, the DLL, and the module:
program DLLtester use SharedVariables use ifwin implicit none integer*4 hlb,iret pointer (cpaddr,calcpi) character*1 ch hlb = loadlibrary('..\..\CalcPi\Debug\calcpi.dll'//char(0)) if(hlb==0)then type *,"Can't find CalcPi.DLL" accept *, ch call exit end if cpaddr = getprocaddress(hlb,"CALCPI"C) if(cpaddr==0)then type *,"Can't locate CalcPi in CalcPi.DLL" accept *, ch iret = freelibrary(hlb) call exit end if numerator = 355.0 denominator = 113.0 call CalcPi iret = freelibrary(hlb) type *, pi accept *, ch end program DLLtester
subroutine CalcPi ! Expose subroutine CalcPi to users of this DLL ! !DEC$ ATTRIBUTES DLLEXPORT, ALIAS:"CALCPI" :: CALCPI use SharedVariables implicit none pi = numerator/denominator return end subroutine CalcPi
module SharedVariables implicit none real*8 :: numerator !DEC$ ATTRIBUTES DLLEXPORT :: NUMERATOR real*8 :: denominator !DEC$ ATTRIBUTES DLLEXPORT :: DENOMINATOR real*8 :: pi !DEC$ ATTRIBUTES DLLEXPORT :: PI end module
What do I need to do to make this work?
With many thanks in advance
You don't explain what does not work for you (or how you build your dll and exe). After making some changes to accommodate differences between my machine and yours (handle size and the DLL path), things work, to some extent, for me.
But there is a more fundamental problem with your current approach. The use of LoadLibrary and GetProcAddress implies that you are doing runtime dynamic linking (programmer controls when the DLL is loaded and from where it is loaded), while the use of a module from compilation of the DLL implies that you are doing load time dynamic linking (compiler and linker specify to the operating system that the DLL be loaded when the program starts). Which do you want?
If you want run time dynamic linking, then you need to look up the addresses of the variables in the module also using GetProcAddress.
Otherwise, consider that you could load lots of libraries named cacpi using LoadLibrary from different paths. How would the compiler then know (at compile time!) which specific library you were referring to with a certain reference to a module variable name?
Thanks for the reply, Ian.
I made up the CalcPi example in an attempt to capture, in simple terms, what I think my problem is - but perhaps it didn't really work. My real problem is this: I have a program that does many things that may be selected by the user, and some of those things are very large but are only rarely selected. So I believe, therefore, that the best way to manage those large sub-programs is to dynamically link them at run time when required.
The main program uses a module of global variables and the sub-programs also need to be able to see them in order to do their jobs. So, to summarize, I need a run-time dynamically linked subroutine to be able to see and use its main program's module of global variables. Can this be done? if so how? I have never been able to find a Fortran example demonstrating anything like this. The Intel supplied sample called DLL_Shared_Data doesn't actually contain a DLL at all so it hasn't been of much help.
Yes, it is quite possible.
But unless you are talking about these rarely user selected things being spectacularly large (say hundreds of megabytes or more), I wouldn't bother too much about trying to split them out. Linking from EXEs to DLLs is quite atypical.
Example below. Here the exe runtime links to the DLL, but the binding from the DLL back to the EXE happens at load time.
MODULE shared_from_exe IMPLICIT NONE REAL :: var !DEC$ ATTRIBUTES DLLEXPORT :: var END MODULE shared_from_exe PROGRAM exe USE shared_from_exe USE IFWIN USE, INTRINSIC :: ISO_C_BINDING IMPLICIT NONE INTEGER(HANDLE) :: dll_handle TYPE(C_FUNPTR) :: proc_address ABSTRACT INTERFACE SUBROUTINE DllProcIntf() BIND(C) IMPLICIT NONE END SUBROUTINE DllProcIntf END INTERFACE PROCEDURE(DllProcIntf), POINTER :: dll_proc var = 123.456 dll_handle = LoadLibrary('dll.dll'// ACHAR(0)) IF (dll_handle == 0) ERROR STOP 'dll_handle was zero!' proc_address = TRANSFER(GetProcAddress( & dll_handle, & 'DllProc' // ACHAR(0) ), proc_address) IF (.NOT. C_ASSOCIATED(proc_address)) & ERROR STOP 'proc_address was not c_associated!' CALL C_F_PROCPOINTER(proc_address, dll_proc) PRINT *, 'In exe' CALL dll_proc PRINT *, 'Back in exe' END PROGRAM exe
SUBROUTINE DllProc() BIND(C, NAME='DllProc') USE shared_from_exe IMPLICIT NONE !DEC$ ATTRIBUTES DLLEXPORT :: DllProc PRINT *, 'In DllProc', var END SUBROUTINE DllProc
Building and running:
>ifort /check:all /warn:all /standard-semantics exe.f90 Intel(R) Visual Fortran Intel(R) 64 Compiler for applications running on Intel(R) 64, Version 18.104.22.168 Build 20171018 Copyright (C) 1985-2017 Intel Corporation. All rights reserved. Microsoft (R) Incremental Linker Version 14.00.24215.1 Copyright (C) Microsoft Corporation. All rights reserved. -out:exe.exe -subsystem:console exe.obj Creating library exe.lib and object exe.exp >ifort /check:all /warn:all /standard-semantics /dll dll.f90 exe.lib Intel(R) Visual Fortran Intel(R) 64 Compiler for applications running on Intel(R) 64, Version 22.214.171.124 Build 20171018 Copyright (C) 1985-2017 Intel Corporation. All rights reserved. Microsoft (R) Incremental Linker Version 14.00.24215.1 Copyright (C) Microsoft Corporation. All rights reserved. -out:dll.dll -dll -implib:dll.lib dll.obj exe.lib Creating library dll.lib and object dll.exp >exe In exe In DllProc 123.4560 Back in exe
Thanks again, Ian, that's just what I need. I see there are some new concepts in there for me to learn so I will get to it.
On this same subject I would appreciate some general advice on what to do about what has become a big problem for me. This is all about a large GIS system that has been growing for about 15 years. The main program consists of a Windows GUI front end plus a large interactive graphics system. The size of the .exe is around 5.5 MBytes. In addition there are 44 DLLs, linked at run-time, some of which are used every day, others only once a year. They range in size from around 50 KBytes up to 900 KBytes. Following a recent upgrade of the main program I provoked, on loading the largest DLL, a message "Not enough storage is available to process this command". So I began to question my whole way of running the DLLs, which, by the way, did not share global variables in any modules, but merely received the necessary values in argument lists with the call to the routines. Is this the right way to do things? If not, what is?
If you have any comments to make I'd be very glad to read them.
I doubt the DLL and EXE sizes, as represented in the filesystem, are problematic. What is more likely an issue is the static storage that particular DLL's require - large variables or common blocks. Hard to say more without seeing some code.
(These days 64 bit platforms are near ubiquitous, but it appears that you are compiling for 32 bit, based on your original code.)
You are quite right - I have never been able to install the 64 bit compiler. It's not at all clear to me where to go for this - I have run something called w_fcompxe_redist_intel64_2013.5.198.msi but although it ran OK I still get the same message "Configuration 'Debug|64' requires compiler support for the 'x64' platform, but that support is not installed ...". What do I need to do?
That _redist_ exe is the redistributables installer, not a compiler. You want a w_fcompxe installer executable that does not have "ia32" or "intel64", or "redist" in its filename.
(sorry to dig-out such an old issue, but this one is exactly what I need...)
How would one do the very same thing on LINUX then?
I.e. an explanation as of post #5 (of IanH (Blackbelt) from Wed, 12/13/2017 - 00:54) would be greatly appreciated.
Thanks and best regards,