- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
All, I am attempting to create a DLL that calls a subroutine that is compiled in the main program. I have been able to compile the DLL by linking to the main codes .lib. I have also been successful at importing the DLL into the main code but when the DLL calls the subroutine in the main code, the subroutine does not have access to a module that is only contained in the main code. Is what I am trying to do possible? I have put a sample code below. When I debug the code and put a break in the Get_KWE subroutine, the debugger says that the KWE array is an Undefined pointer/array. If this is possible, why does the subroutine compiled into the main routine not have access to its own module and what would I need to do to fix it?
:: Main Code
MODULE PARAM
INTEGER(4) :: KWE(1000)
END MODULE
PROGRAM UDF
USE PARAM
EXTERNAL func
pointer ( q_udf_bc, func ) !Q points to the "Fortran_DLL" routine
integer ( handle ) p_udf_bc
integer(4) :: DLL_KWE
do i=1,1000
KWE(i) = i
enddo
p_udf_bc = LoadLibrary ("UDF.dll"//char(0))
q_udf_bc = GetProcAddress (p_udf_bc,MAKEINTRESOURCE(1))
DLL_KWE = 500
call func(DLL_KWE)
END PROGRAM
SUBROUTINE Get_KWE(index,KWE_RETURN)
USE PARAM
INTEGER(4) :: index,KWE_RETURN
KWE_RETURN = KWE(index)
RETURN
END SUBROUTINE
:: DLL Code
SUBROUTINE func(KWE_DLL)
INTEGER(4) :: KWE_DLL,KWE
CALL Get_KWE(KWE_DLL,KWE)
END SUBROUTINE
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Write the DLL function to accept a procedure pointer, and then pass the procedure pointer along with the args.
The two will have to agree on subroutine/function interface.
Jim Dempsey
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Jim,
Thanks for the reply. I'm not completely familiar with how to use procedure pointers. I will do my research and see what I can find. Thanks again for pointing me in the right direction.
Aaron Smith
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Jim and All,
Question on your advice. If I understand what I have found so far is the procedure pointer can only reference a single subroutine or function. While that is how my code is above, in reality, I would like to have 20-30 different subroutines that the DLL can call from the main code to retrieve data about specific things. In that case, would I have to have 20-30 different procedure pointers and need to pass all of them to the DLL when its called? Please excuse my ignorance in this problem. This is the first time I have ever tried to have a DLL callback to the main routine for additional information.
Aaron Smith
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
You could pass the DLL an array of derived type, that had a procedure pointer component. Each element in the derived type represents a "call back" in the executable.
You can arrange things such that an exe is dynamically linked to a dll which is dynamically linked back to the exe. I believe this is used in some of the base Windows operating system DLL's, that have interlinked dependencies. The two tricks are:
- you need to specify the procedures that the exe and dll are to export - either using !DEC$ ATTRIBUTES DLLEXPORT :: procname, or by using a DEF file or by librarian and linker command line options.
- you need to use the librarian to build an exports file (.exp) for the exe (or dll) ahead of linking the dll (or exe) if you are going to use load time linking, or failing that use runtime linking to do one of the directions of reference (DLL to EXE or vice versa)
You need to be mindful of the very tight binding that this introduces between the exe and dll.
Example code attached. The command line invocations required are:
[plain]
## Compile the fortran sources to object code (.obj).
>ifort /c Dll.f90 Exe.f90
Intel(R) Visual Fortran Compiler XE for applications running on IA-32, Version 14.0.1.139 Build 20131008
Copyright (C) 1985-2013 Intel Corporation. All rights reserved.
## Create the import library and exports file for the DLL object code.
>lib /def Dll.obj
Microsoft (R) Library Manager Version 10.00.40219.01
Copyright (C) Microsoft Corporation. All rights reserved.
Creating library Dll.lib and object Dll.exp
## Build the exe using the exe object code and the import library for the DLL created above.
>ifort exe.obj dll.lib
Intel(R) Visual Fortran Compiler XE for applications running on IA-32, Version 14.0.1.139 Build 20131008
Copyright (C) 1985-2013 Intel Corporation. All rights reserved.
Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation. All rights reserved.
-out:exe.exe
-subsystem:console
exe.obj
dll.lib
Creating library exe.lib and object exe.exp
## Build the DLL using the dll object code, the import library for the exe and the exports file
## for the dll.
>ifort /dll dll.obj exe.lib dll.exp
Intel(R) Visual Fortran Compiler XE for applications running on IA-32, Version 14.0.1.139 Build 20131008
Copyright (C) 1985-2013 Intel Corporation. All rights reserved.
Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation. All rights reserved.
-out:dll.dll
-dll
-implib:dll.lib
dll.obj
exe.lib
dll.exp
>exe
Start of the exe.
Hello from DllProc 1
Hello from ExeProc 1
Bye from DllProc 1
End of the exe.
[/plain]
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Here are the example source files.
exe.f90:
[fortran]PROGRAM Exe
IMPLICIT NONE
INTERFACE
SUBROUTINE DllProc(arg)
!DEC$ ATTRIBUTES DLLIMPORT :: DllProc
IMPLICIT NONE
INTEGER, INTENT(IN) :: arg
END SUBROUTINE DllProc
END INTERFACE
PRINT "('Start of the exe.')"
CALL DllProc(1)
PRINT "('End of the exe.')"
END PROGRAM Exe
SUBROUTINE ExeProc(arg)
!DEC$ ATTRIBUTES DLLEXPORT :: ExeProc
IMPLICIT NONE
INTEGER, INTENT(IN) :: arg
PRINT "('Hello from ExeProc ',I0)", arg
END SUBROUTINE ExeProc[/fortran]
dll.f90:
[fortran]SUBROUTINE DllProc(arg)
!DEC$ ATTRIBUTES DLLEXPORT :: DllProc
IMPLICIT NONE
INTEGER, INTENT(IN) :: arg
INTERFACE
SUBROUTINE ExeProc(arg)
!DEC$ ATTRIBUTES DLLIMPORT :: ExeProc
IMPLICIT NONE
INTEGER, INTENT(IN) :: arg
END SUBROUTINE ExeProc
END INTERFACE
PRINT "('Hello from DllProc ',I0)", arg
CALL ExeProc(arg)
PRINT "('Bye from DllProc ',I0)", arg
END SUBROUTINE DllProc[/fortran]
Note that the linking steps described in the previous post require a command sequence that might be difficult to implement within visual studio. Decoupling the link of the exe from the link of the DLL by loading and resolving the DLL procedures at runtime may help here.
From a Fortran language point of view, you could consider putting the call back routines in the exe in a module, that then gets used in the client DLL's that need to call back into the exe. This would require the EXE source files to be compiled before the DLL source files. This will save you the need to specify some of the interface bodies.
Aspects of this usage make me a bit twitchy, perhaps because it feels unnatural.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
IanH,
Thank you for your reply. I will try to use your example in my implementation and see what I can learn from it. There may be another way of making this whole thing work but I am not used to the exact situation that I have found myself in. Let me explain further in detail what I am trying to do. Maybe it will lead down a different path.
I have an analytical code that we have been using for many years. A few years ago, I added some code so that a user could compile a DLL that could be called by the main code for user intervention at defined points in the code. The information between the Main program and the DLL was passed as arguments. This was fairly straight forward. As more and more users began to write their own DLLs, more variables were asked to be passed into the DLL. The argument list became quite large with approximately 50 variables. Inside the main code, we have all the variables that could be requested in a very large module. The intent of my question above, is how can I code in some small subroutines that the DLL could call and access the information in real time that the user needs without having to pass every single variable that is in the module. I also wouldn't need to pass any variables since the user could "request" them as needed.
To give more insight based on your response, the EXE source files will always be compiled before the DLL source files. The problem that I ran into was when I compiled the DLL, I added the main EXE library to allow the DLL to compile but when the DLL called the "call back" subroutine while running, the variables in the main EXEs module were not accessable even though it had the use statement. It was almost like the module memory have been duplicated when the DLL was called. In my code snippet in my first post, I show an array that has been set by the main EXE before the DLL was opened and called. When I debug the code, and put a break in the "call back" subroutine, the array is no longer set and can not be accessed.
Thanks again to anyone that can help me with this problem.
Aaron Smith
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
IanH,
The technique you laid out would work well when only one app would link to the DLL.
I think a better approach might be table of procedure pointers, and where you pass the table address into the DLL.
The DLL can then copy the pointers in the table into its own named procedure pointer variables (that lie in caller context space). The only problem with this is enforcement of signatures (name, number of and types of arguments).
A different route would be to make a DLL out of the call-back routines, then pass the path/name of the callback DLL to the library DLL and have it dynamically load the callback DLL (this too would require the entry point table to be stored in the caller context within the DLL).
I do not think you can (effectively) static link the library DLL to the app entry points in the first case. In the second case, the DLL can load the callback DLL then issue calls to find callback function entry points by name. This does give some limited protection for matching call back routine to pointer. (no different than linking to C Runtime Library using standard headers).
Jim Dempsey
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Don't rely on the debugger for this as it occasionally gets confused and lies about the contents of variables - instead print the results out. Using print statements I do not see the behaviour you describe using ifort 14.0.1 (noting that your original source snippet was not compilable).
I would be inclined to make all the variables components in a derived type. Then you pass a single object of that derived type to the routine in the DLL. This helps to decouple the exe from the dll (you could have different derived types for different DLL/EXE versions) and allows multiple instances of the exe data to be extant at the same time.
(ifort 14.0.1 x64 doesn't appear to be robust (ICE) to seeing generated interface files from the x86 variant?)
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
jimdempseyatthecove wrote:
IanH,
The technique you laid out would work well when only one app would link to the DLL.
I think a better approach might be table of procedure pointers, and where you pass the table address into the DLL.
Specifics of the approach aside, I agree - there is a difference between being able to do something and that something being a good idea.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
IanH and Jim,
Thank you both for your responses to what i am trying to do. You have both given me much to consider on how to structure what I am doing. Hopefully in the end, I will have something that will work and will suit my users needs. I appreciate it immensely.
Aaron Smith

- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page