Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
29261 Discussions

Tricky situation with DLLs, POINTER(), and LOC().

OP1
New Contributor III
657 Views

I need help with the following tricky situation.

I have a module called MODULE_ERROR which contains types and subroutines that other subroutines of my main program and DLLs can call (see below).
The idea is to have the user code provide a user-defined, error-handling subroutine, which is passed as an argument to the HANDLE_ERROR_INIT subroutine of MODULE_ERROR. HANDLE_ERROR_INIT then initializes a pointer to the user-provided subroutine.
Now, every subroutine in my code or DLLs just needs to call the module subroutine HANDLE_ERROR, which points to the user-defined subroutine (as long as the code/DLLshave the module MODULE_ERROR in their scopeof course).

This works well when the call to HANDLE_ERROR happens in my main executable; but I get an access violation when the call to HANDLE_ERROR is called from one of my DLLs. I suspect that the DLL doesn't know how to interpret the value of the pointer SUB_ERROR_PTR.

Is there a way to get around this? It seems a bit complicated - I hope I explained it well. I am using IVF 10.

Olivier



MODULE MODULE_ERROR
IMPLICIT NONE


!*******************************************************************************
! PUBLIC PARAMETERS AND VARIABLES
!*******************************************************************************


INTEGER,PARAMETER :: MAX_ERROR_MESSAGE_LENGTH = 800
INTEGER,PARAMETER :: MAX_SYMBOL_NAME_LENGTH = 63
INTEGER,PARAMETER :: NO_ERROR = 0
INTEGER,PARAMETER :: WARNING = 1
INTEGER,PARAMETER :: USER_ERROR = 2
INTEGER,PARAMETER :: INTERNAL_ERROR = 3


!*******************************************************************************
! PUBLIC TYPES
!*******************************************************************************


TYPE T_SUBROUTINE_PATH
CHARACTER(MAX_SYMBOL_NAME_LENGTH),POINTER :: SUBROUTINE_NAME
TYPE(T_SUBROUTINE_PATH),POINTER :: PREVIOUS_SUBROUTINE_PATH
END TYPE T_SUBROUTINE_PATH


!*******************************************************************************
! INTERFACES AND PRIVATE VARIABLES
!*******************************************************************************


INTERFACE
SUBROUTINE HANDLE_ERROR_LOC(ERROR_MESSAGE,SUBROUTINE_NAME, &
ERROR_TYPE,SUBROUTINE_PATH)
IMPORT :: MAX_ERROR_MESSAGE_LENGTH,MAX_SYMBOL_NAME_LENGTH, &
T_SUBROUTINE_PATH
IMPLICIT NONE
CHARACTER(MAX_ERROR_MESSAGE_LENGTH),INTENT(IN) :: ERROR_MESSAGE
CHARACTER(MAX_SYMBOL_NAME_LENGTH),INTENT(IN) :: SUBROUTINE_NAME
INTEGER,INTENT(IN) :: ERROR_TYPE
TYPE(T_SUBROUTINE_PATH),OPTIONAL,INTENT(IN) :: SUBROUTINE_PATH
END SUBROUTINE HANDLE_ERROR_LOC
END INTERFACE
INTEGER(KIND=INT_PTR_KIND()) :: SUB_ERROR_PTR
POINTER(SUB_ERROR_PTR,HANDLE_ERROR_LOC)
LOGICAL :: MODULE_IS_INITIALIZED = .FALSE.
PRIVATE :: SUB_ERROR_PTR,MODULE_IS_INITIALIZED,HANDLE_ERROR_LOC


!*******************************************************************************
! CONTAINED PROCEDURES
!*******************************************************************************


CONTAINS


!_______________________________________________________________________________
SUBROUTINE HANDLE_ERROR_INIT(HANDLE_ERROR_USER,INTERNAL_ERROR_FILE_USER)
USE IFPORT,ONLY: SETENVQQ
USE MODULE_OS_CONSTANTS
IMPLICIT NONE
CHARACTER(LEN=MAX_FILE_NAME_LENGTH),INTENT(IN) :: INTERNAL_ERROR_FILE_USER
INTERFACE
SUBROUTINE HANDLE_ERROR_USER(ERROR_MESSAGE,SUBROUTINE_NAME, &
ERROR_TYPE,SUBROUTINE_PATH)
IMPORT :: MAX_ERROR_MESSAGE_LENGTH,MAX_SYMBOL_NAME_LENGTH, &
T_SUBROUTINE_PATH
IMPLICIT NONE
CHARACTER(MAX_ERROR_MESSAGE_LENGTH),INTENT(IN) :: ERROR_MESSAGE
CHARACTER(MAX_SYMBOL_NAME_LENGTH),INTENT(IN) :: SUBROUTINE_NAME
INTEGER,INTENT(IN) :: ERROR_TYPE
TYPE(T_SUBROUTINE_PATH),OPTIONAL,INTENT(IN) :: SUBROUTINE_PATH
END SUBROUTINE HANDLE_ERROR_USER
END INTERFACE
LOGICAL :: STATUS
SUB_ERROR_PTR = LOC(HANDLE_ERROR_USER)
STATUS = SETENVQQ('FOR_DIAGNOSTIC_LOG_FILE=' // TRIM(INTERNAL_ERROR_FILE_USER))
END SUBROUTINE HANDLE_ERROR_INIT


!_______________________________________________________________________________
SUBROUTINE HANDLE_ERROR(ERROR_MESSAGE,SUBROUTINE_NAME,ERROR_TYPE, &
SUBROUTINE_PATH)
IMPLICIT NONE
CHARACTER(MAX_ERROR_MESSAGE_LENGTH),INTENT(IN) :: ERROR_MESSAGE
CHARACTER(MAX_SYMBOL_NAME_LENGTH),INTENT(IN) :: SUBROUTINE_NAME
INTEGER,INTENT(IN) :: ERROR_TYPE
TYPE(T_SUBROUTINE_PATH),OPTIONAL,INTENT(IN) :: SUBROUTINE_PATH
write(*,*) 'test'
CALL HANDLE_ERROR_LOC(ERROR_MESSAGE,SUBROUTINE_NAME,ERROR_TYPE,SUBROUTINE_PATH)
END SUBROUTINE HANDLE_ERROR


END MODULE MODULE_ERROR

0 Kudos
5 Replies
OP1
New Contributor III
657 Views
Here is a working example which illustrates the behavior of what I tried to explain. The solution is made of three projects:
1. The main program "MY_PROGRAM.f90" and "MY_SUB.f90"
2.A static library "MY_STATIC_LIB.f90"
3. A DLL "MY_DLL.f90"
The source of these procedures is shown below.

I don't know if this is a bug. It's a bit puzzling.

Thanks in advance for your help!

Olivier


PROGRAM MY_PROGRAM
USE MY_STATIC_LIB
USE MY_DLL
IMPLICIT NONE
INTEGER :: I
EXTERNAL :: MY_SUB
I = 0
CALL MY_STATIC_LIB_INIT(MY_SUB)
CALL MY_STATIC_LIB_SUB(I)
WRITE(*,*) I
! This will trigger an error.
CALL MY_DLL_SUB(I)
END PROGRAM MY_PROGRAM


SUBROUTINE MY_SUB(I)
IMPLICIT NONE
INTEGER,INTENT(INOUT) :: I
I = I+1
END SUBROUTINE MY_SUB


MODULE MY_STATIC_LIB
IMPLICIT NONE
INTERFACE
SUBROUTINE MY_STATIC_LIB_SUB_LOC(I)
IMPLICIT NONE
INTEGER,INTENT(INOUT) :: I
END SUBROUTINE MY_STATIC_LIB_SUB_LOC
END INTERFACE
INTEGER(KIND=INT_PTR_KIND()) :: MY_STATIC_LIB_SUB_PTR
POINTER(MY_STATIC_LIB_SUB_PTR,MY_STATIC_LIB_SUB_LOC)
PRIVATE :: MY_STATIC_LIB_SUB_PTR,MY_STATIC_LIB_SUB_LOC
CONTAINS
SUBROUTINE MY_STATIC_LIB_INIT(USER_PROVIDED_SUB)
IMPLICIT NONE
INTERFACE
SUBROUTINE USER_PROVIDED_SUB(I)
IMPLICIT NONE
INTEGER,INTENT(INOUT) :: I
END SUBROUTINE USER_PROVIDED_SUB
END INTERFACE
MY_STATIC_LIB_SUB_PTR = LOC(USER_PROVIDED_SUB)
END SUBROUTINE MY_STATIC_LIB_INIT
SUBROUTINE MY_STATIC_LIB_SUB(I)
IMPLICIT NONE
INTEGER,INTENT(INOUT) :: I
CALL MY_STATIC_LIB_SUB_LOC(I)
END SUBROUTINE MY_STATIC_LIB_SUB
END MODULE MY_STATIC_LIB


MODULE MY_DLL
IMPLICIT NONE
CONTAINS
SUBROUTINE MY_DLL_SUB(I)
!DEC$ ATTRIBUTES DLLEXPORT :: MY_DLL_SUB
USE MY_STATIC_LIB
IMPLICIT NONE
INTEGER,INTENT(INOUT) :: I
CALL MY_STATIC_LIB_SUB(I)
END SUBROUTINE MY_DLL_SUB
END MODULE MY_DLL
0 Kudos
IanH
Honored Contributor III
657 Views
I suspect that you have multiple "copies" (or instances, or whatever the term is) of the static library in your program, once it has been completely loaded at run time. The DLL has a copy and the exe has a copy (one for each Win32 module). These copies are independent. The DLL never initialises its copy, so when it makes the call to the working routine it references an uninitialised pointer.

One fix would be to put the code that is in your static library into in a DLL of its own. Then both the exe and other DLLs refer to that single instance. Another option (more complicated) would be to have the exe provide the DLL with an explicit pointer to a callback routine in the (only) instance of the static library that is in the EXE (the DLL would then not be linked against the static library).

This is similar in many respects to the debilitating programming disease known as multiple C library syndrome.

0 Kudos
OP1
New Contributor III
657 Views
Quoting - IanH
I suspect that you have multiple "copies" (or instances, or whatever the term is) of the static library in your program, once it has been completely loaded at run time. The DLL has a copy and the exe has a copy (one for each Win32 module). These copies are independent. The DLL never initialises its copy, so when it makes the call to the working routine it references an uninitialised pointer.

One fix would be to put the code that is in your static library into in a DLL of its own. Then both the exe and other DLLs refer to that single instance. Another option (more complicated) would be to have the exe provide the DLL with an explicit pointer to a callback routine in the (only) instance of the static library that is in the EXE (the DLL would then not be linked against the static library).

This is similar in many respects to the debilitating programming disease known as multiple C library syndrome.


Thanks a lot IanH. I think you nailed it. I naively assumed that both the DLL and the EXE would share the same module variables of the common static library they are linked with. Your explanation definitely make sense.

This IS debilitating :)

I'll find a workaround - your first suggestion seems the easiest to implement.

Olivier
0 Kudos
OP1
New Contributor III
657 Views
I thought a bit more about this. Is there any way tocreate a DLL so that this DLL has access to a specified set of variables which are in the scope of the program the DLL is linked to?

Olivier
0 Kudos
Steven_L_Intel1
Employee
657 Views

If you mean the executable that links to the DLL, then no. If it isn't visible when the DLL is linked, it isn't visible to the DLL. You can share variables from a DLL to an executable, but they are defined by the DLL and accessed by the executable.
0 Kudos
Reply