Community
cancel
Showing results for 
Search instead for 
Did you mean: 
Highlighted
Beginner
102 Views

DLL can't see main program's shared variables

Hi All,

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

Mike

0 Kudos
11 Replies
Highlighted
Black Belt
102 Views

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?

0 Kudos
Highlighted
Beginner
102 Views

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.

Many thanks

Mike

 

0 Kudos
Highlighted
Black Belt
102 Views

The example code in DLL_Shared_Data builds a small DLL and an EXE that shares and exchanges data with the DLL using shared memory.

0 Kudos
Highlighted
Black Belt
102 Views

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.

EXE source:

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

DLL source:

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.0.1.156 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 18.0.1.156 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

 

0 Kudos
Highlighted
Beginner
102 Views

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.

Many thanks

Mike

0 Kudos
Highlighted
Black Belt
102 Views

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.)


 

0 Kudos
Highlighted
Beginner
102 Views

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?

Many thanks

Mike

0 Kudos
Highlighted
Black Belt Retired Employee
102 Views

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.

0 Kudos
Highlighted
Black Belt
102 Views

Make sure you also have the corresponding 64 bit support installed for C++ development.

0 Kudos
Highlighted
102 Views

Hello,
(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,
Frank

0 Kudos
Highlighted
Black Belt Retired Employee
102 Views

I suggest asking in https://software.intel.com/en-us/forums/intel-fortran-compiler-for-linux-and-mac-os-x

0 Kudos