2
votes

I am using the Intel Fortran Compiler 15.0 via the Microsoft Visual Studio 2015 IDE. I am learning OpenMP to (eventually) run sections of my code in parallel. My code will necessitate loading subroutines from .dlls. I have the following program that loads a .dll with a HelloWorld subroutine and calls that subroutine; the code below executes just fine in a simple console application.

program LoadThisLibrary
 
Use DFWIN
Use DFLIB

implicit none

!Variable declarations
Character (len=8) SubName

!Pointer declarations
 Integer i !not used but must be declared as part of a POINTER declaration.
 Pointer (p,i)
 Integer(INT_PTR_KIND()) paa(100)  !declares an integer array that will contain pointer addresses
 
!Declare an interface for MySub                                            
 Interface
   Subroutine MySub
   End Subroutine MySub
 End Interface
 
! Declare a pointer/pointee structure - this allows us to later "call" a pointer by its pointee (subroutine) name. 
! This is a CVF function, not "pure fortran"  
 Pointer(SubNum,MySub) 
 
!Load the dll that contains the subroutine we want to run
p = LoadLibrary("HelloDLL.dll"C) 
 
!Name the subroutine we wish to call from that dll
SubName = TRIM("HELLOSUB")//CHAR(0)

!Get a pointer to the subroutine we want and store it in the pointer array
paa(1) = GetProcAddress(p,SubName)

!set a POINTER to the subroutine's memory location
SubNum = paa(1) 

!Call the subroutine loaded from the dll
 Call MySub

end

Here is the code for "HelloDLL.dll"

subroutine HelloSub

  ! Expose subroutine HelloSub to users of this DLL
  !
  !DEC$ATTRIBUTES DLLEXPORT :: HelloSub

  ! Variables
  character variable
  
  ! Body of HelloSub 
  print *, "Congratulations! You have run a dll subroutine. You are the best. I am in awe of your brilliance. You have triumphed in the face of adversity. "
  READ(*,*) variable

end subroutine HelloSub

In order to enable OpenMP directives, I go to Project -> Console Properties (in my IDE), then Fortran -> Language, and set "Process OpenMP Directives" to "Generate Parallel Code (/Qopenmp)". When I do this, and nothing else, making no changes to my source code, my code no longer runs. Specifically, I find that LoadLibrary still finds an address for HelloDLL.dll, but the GetProcAddress call returns a null value (0). It doesn't seem to matter whether I include USE OMP_LIB or use any OpenMP directives or functions in the main program code or not. I've tried renaming the paa() array, even saving the address as just a plain integer - nothing seems to work.

I would be so grateful for any suggestions or insight. Obviously enabling OpenMP is doing more than just allowing access to OpenMP's abilities, I just can't figure out why it's having any effect on GetProcAddress. Please help if you can! Many thanks in advance.

1
I strongly suggest not to you Cray pointers, they were never standard and they are really obsolete in practice. Fortran has enough standard capabilities.Vladimir F
Thank you for the feedback. I'm working from inherited code and had to Google what 'Cray pointers' were. If you have suggestions for alternatives to Cray pointers in Fortran I'd be happy to hear them, otherwise I'll keep digging on my own.A_Weiss
The standard types (from the is_c_binding) module: c_ptr and c_funptr and the procedures available for working with them (c_loc, c_funloc, c_f_pointer, c_f_procpointer) gcc.gnu.org/onlinedocs/gfortran/Working-with-Pointers.html Conversion between a Cray pointer and a c_ptr or c_funptr should be possible using transfer().Vladimir F
Thank you for the recommendations! I have seen those in other code I've Googled but didn't really know how to implement them. Thanks for the suggestions.A_Weiss

1 Answers

1
votes

The null terminated subroutine name (8+1 = 9) is longer than the length of the SubName variable (8). While a null terminator is present in the expression assigning to SubName, it will be chopped off as part of the assignment (you cannot fit nine characters into eight).

Whether a null terminator happens to immediately follow the storage for SubName depends on the layout of variables in memory (and likely random chance). Without the null terminator, the name passed to GetProcAddress will be nonsensical. The layout of variables in memory is something that /Qopenmp will change.

Correct the length of SubName. Consider using an allocatable character scalar to make your code robust to later changes in the length of a procedure name.

(Other issues of style or portability exist, but they are more subjective.)