DLL Calling A Main Programs Subroutine

DLL Calling A Main Programs Subroutine

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

publicaciones de 11 / 0 nuevos
Último envío
Para obtener más información sobre las optimizaciones del compilador, consulte el aviso sobre la optimización.

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

www.quickthreadprogramming.com

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

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

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:

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

	

 

Here are the example source files.

exe.f90:

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

dll.f90:

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

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.

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

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

www.quickthreadprogramming.com

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

Adjuntos: 

Cita:

jimdempseyatthecove escribió:

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.

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

Inicie sesión para dejar un comentario.