- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hello everyone,
I am writing a Fortran program using Visual Studio and the ifort compiler. I would now like to use a DLL that has been made available to me. This was also written in Fortran. I already had a similar problem with a DLL written in C++, which was solved here (https://community.intel.com/t5/Intel-Fortran-Compiler/Integrate-C-DLL-in-Fortran-Code/m-p/1410919#M162620). Of course, this solution no longer works if it is not a C++ DLL.
Unfortunately, I only find suggestions on how Fortran DLLs can be used in other programming languages. What I have found are approaches like:
!DEC$ ATTRIBUTES DLLIMPORT :: libdiscon
!DEC$ ATTRIBUTES C, ALIAS:'DISCON' :: DISCON
call DISCON(SWAP, Fail, infile, outname, msg )
The DLL I want to use is called libdiscon.dll. It contains the subroutine DISCON(), which I would like to access. I have placed libdiscon.dll in the debug folder of Visual Studio.
I would be very grateful if someone could help me.
Best regards
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Your DLL "libdiscon.dll" should be accompanied by a so-called import library, "libdiscon.lib". Specify that import library with the link step. This should make the routine you want available via the DLL. To run the program that DLL will have to be found, one way is to copy it into the directory containing the executable.
There is no need for the !DEC$ compiler directives, unless perhaps you need global data from the DLL or vice versa, but that is an entirely different story.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thank you for the reply.
I don't compile the code myself and only get the DLL provided. In addition, I have access to a DYLIB file. However, this is probably the Linux equivalent of the DLL instead of the LIB file I am looking for.
Is it possible to generate the LIB file from the DLL?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
The import library is a byproduct of the link step, so that should be available. Alternatives are that you load the DLL explicitly and get a pointer to the routine. That would involve some coding. I know there is a utility that can produce the missing import library. I know there is one in Cygwin, but this webpage might help: https://www.asawicki.info/news_1420_generating_lib_file_for_dll_library, even if it is old.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I was able to create libdiscon.lib with the instructions in the link. In VS I added it under
Linker ->Input -> libdiscon.lib,
Linker -> Input -> Additional Library Dependencies -> (path to directory)
and under
Fortran -> Additional Include Directories -> (path to directory).
How can I now access the subroutine DISCON() in the code? I have found some results regarding C++. There, commands of the type #include are still needed.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
You use it in just the same way as any routine from an ordinary library. (I may try to put a small example together.)
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thank you, that is very helpful. Analogously I did the following in the module where I want to use the DLL:
module class_controller
implicit none
type :: model_controller
logical :: boolean_abort = .FALSE.
contains
procedure :: use_controller
procedure :: discon
end type model_controller
contains
subroutine use_controller(this, swap)
use, intrinsic :: iso_c_binding, only : c_float, c_int, c_char, c_null_char
implicit none
class(model_controller), intent(inout) :: this
real(kind = 8), dimension(78), intent(inout) :: swap
real(c_float), dimension(78) :: swap_c_float
real(c_float) :: ctrl_avrSWAP(78)
integer(c_int) :: ctrl_aviFAIL
character(kind=c_char,len=28) :: ctrl_accINFILE
character(kind=c_char,len=51) :: ctrl_avcOUTNAME
character(kind=c_char,len=49) :: ctrl_avcMSG
call discon(ctrl_avrSWAP, ctrl_aviFAIL, ctrl_accINFILE, ctrl_avcOUTNAME, ctrl_avcMSG)
end subroutine use_controller
subroutine discon(avrSWAP, aviFAIL, accINFILE, avcOUTNAME, avcMSG)
REAL(C_FLOAT),dimension(78), INTENT(INOUT) :: avrSWAP
INTEGER(C_INT), INTENT(INOUT) :: aviFAIL
CHARACTER(KIND=C_CHAR, len = 28), INTENT(IN) :: accINFILE
CHARACTER(KIND=C_CHAR, len = 20), INTENT(IN) :: avcOUTNAME
CHARACTER(KIND=C_CHAR, len = 49), INTENT(INOUT) :: avcMSG
!DEC$ ATTRIBUTES DLLEXPORT :: DISCON
end subroutine discon
end module class_controller
I used the same inputs and outputs as in the called subroutine itself. The subroutine I want to use starts as follows:
SUBROUTINE DISCON(avrSWAP, aviFAIL, accINFILE, avcOUTNAME, avcMSG) BIND (C, NAME='DISCON')
USE, INTRINSIC :: ISO_C_Binding
USE :: ROSCO_Types
USE :: ReadSetParameters
USE :: ControllerBlocks
USE :: Controllers
USE :: Constants
USE :: Filters
USE :: Functions
USE :: ExtControl
USE :: ROSCO_IO
USE :: ZeroMQInterface
IMPLICIT NONE
! Enable .dll export
#ifndef IMPLICIT_DLLEXPORT
!DEC$ ATTRIBUTES DLLEXPORT :: DISCON
!GCC$ ATTRIBUTES DLLEXPORT :: DISCON
#endif
REAL(C_FLOAT), INTENT(INOUT) :: avrSWAP(*)
INTEGER(C_INT), INTENT(INOUT) :: aviFAIL
CHARACTER(KIND=C_CHAR), INTENT(IN ) :: accINFILE(NINT(avrSWAP(50)))
CHARACTER(KIND=C_CHAR), INTENT(IN ) :: avcOUTNAME(NINT(avrSWAP(51)))
CHARACTER(KIND=C_CHAR), INTENT(INOUT) :: avcMSG(NINT(avrSWAP(49)))
I receive the following error messages:
error #8262: For a type-bound procedure that has the PASS binding attribute, the first dummy argument must have the same declared type as the type being defined. [AVRSWAP]
error #6683: A kind type parameter must be a compile-time constant. [C_FLOAT]
error #6683: A kind type parameter must be a compile-time constant. [C_INT]
error #6683: A kind type parameter must be a compile-time constant. [C_CHAR]
error #6683: A kind type parameter must be a compile-time constant. [C_CHAR]
error #6683: A kind type parameter must be a compile-time constant. [C_CHAR]
It seems that the subroutine is read in correctly.
Do you the problem that causes the error messages?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Your subroutine discon is missing USE, INTRINSIC :: ISO_C_Binding, only : c_float, c_int, c_char, c_null_char
Also, the model_controller contained procedure declaration of discon has no explicit attribute of PASS nor NOPASS. The default is PASS. See: Passed-Object Dummy Arguments (intel.com)
In order to access any member variables (boolian_abort), it would be required to PASS the object pointer (which you may name THIS or any other suitable name). NOPASS can be used on contained procedures that do not reference member objects of the object being used to access the procedure.
EDIT: FWIW Unlike C++ where the this pointer to the object is passed as a hidden argument named this, Fortran requires an explicit declaration of the object pointer as the first argument to the procedure.
Jim Dempsey
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
You are right. I have corrected the matter. Unfortunately, it still doesn't work to call the subroutine. It seems that the variables do not match. Could it be because the size of these is not yet allocated? Shouldn't they be assigned their size by the function call?
Below is the corrected code:
module class_controller
implicit none
type :: model_controller
logical :: boolean_abort = .FALSE.
contains
procedure :: use_controller
procedure :: discon
end type model_controller
contains
subroutine use_controller(this, swap)
use, intrinsic :: iso_c_binding, only : c_float, c_int, c_char, c_null_char
implicit none
class(model_controller), intent(inout) :: this
real(kind = 8), dimension(78), intent(inout) :: swap
real(c_float), dimension(78) :: swap_c_float
real(c_float), dimension(78) :: ctrl_avrSWAP
integer(c_int) :: ctrl_aviFAIL
character(kind=c_char,len=28) :: ctrl_accINFILE
character(kind=c_char,len=20) :: ctrl_avcOUTNAME
character(kind=c_char,len=49) :: ctrl_avcMSG
call discon(ctrl_avrSWAP, ctrl_aviFAIL, ctrl_accINFILE, ctrl_avcOUTNAME, ctrl_avcMSG)
end subroutine use_controller
subroutine discon(this, avrSWAP, aviFAIL, accINFILE, avcOUTNAME, avcMSG)
use, intrinsic :: iso_c_binding, only : c_float, c_int, c_char, c_null_char
implicit none
class(model_controller), intent(inout) :: this
REAL(C_FLOAT),dimension(78), INTENT(INOUT) :: avrSWAP
INTEGER(C_INT), INTENT(INOUT) :: aviFAIL
CHARACTER(KIND=C_CHAR, len = 28), INTENT(IN) :: accINFILE
CHARACTER(KIND=C_CHAR, len = 20), INTENT(IN) :: avcOUTNAME
CHARACTER(KIND=C_CHAR, len = 49), INTENT(INOUT) :: avcMSG
!DEC$ ATTRIBUTES DLLEXPORT :: DISCON
end subroutine discon
end module class_controller
leading to:
error #6631: A non-optional actual argument must be present when invoking a procedure with an explicit interface. [AVCMSG]
error #6633: The type of the actual argument differs from the type of the dummy argument. [CTRL_AVIFAIL]
error #6634: The shape matching rules of actual arguments and dummy arguments have been violated. [CTRL_AVRSWAP]
error #8284: If the actual argument is scalar, the dummy argument shall be scalar unless the actual argument is of type character or is an element of an array that is not assumed shape, pointer, or polymorphic. [AVRSWAP] 2
error #6633: The type of the actual argument differs from the type of the dummy argument. [CTRL_ACCINFILE]
error #7938: Character length argument mismatch. [CTRL_AVCOUTNAME]
error #6633: The type of the actual argument differs from the type of the dummy argument. [CTRL_AVRSWAP]
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
You forgot the argument "this" to the call to DISCON. Easy enough to overlook :).
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
That's true, thank you!
The programme now runs without error message. When creating the project in the output window of Visual Studio, nothing like :
"Programm.exe" (Win32): "C:\Program Files (x86)\...\libdiscon.dll" is loaded . Symbols have been loaded"
is displayed. Would this be expected if the DLL was actually read in correctly?
I have placed the DLL in the working directory as well as in the folder where the EXE is created.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
If the DLL was not loaded correctly, your program would not work. I am not sure when these messages are displayed, but I can imagine that the pdb file was not present, so that Visual Studio would not know how to debug the routines in the DLL and therefore is silent. But that is utter divination and I would not count on it being even remotely pointing in the right direction.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
The Visual Studio debugger will always tell you which DLLs have been loaded and whether or not symbols are available. This is merely informational. You won't see this running the program from outside the debugger.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
But as I am working in debug mode at the moment, so should the successful loading of the DLL already be indicated?
What Arjen_Markus says also makes sense to me, since an error would probably occur if the subroutine were not available.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
And an other question:
why do you propose that I use
!DEC$ ATTRIBUTES DLLEXPORT :: DISCON
instead of
!DEC$ ATTRIBUTES DLLIMPORT :: DISCON
Would't this be the commad to generate a DLL from a routine? I tried to use DLLIMPORT but it causes an error message. I would like to understand the difference in that case.
Thank you in advance
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
It did tell you the DLL was loaded.
DLLEXPORT is used when you are building a DLL and you want a particular procedure, variable or COMMON block to be visible to users of the DLL. It is this that triggers the creation of the import library (.lib) when you build the DLL.
DLLIMPORT is used to tell the compiler that the symbol you're referencing is coming from a DLL. This isn't required for procedures but is for data.
If you are building a DLL using modules, and have DLLEXPORT directives for things in that module, the compiler treats them as DLLIMPORT when the module is USEd. This can lead to informational messages when the DLL is linked about imported symbols already defined, but you can ignore these.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thank you for the explainations.
@Steve_Lionel, you said it told me that the DLL was loaded. Shouldn't that also be in the output window?
libdiscon.dll is not listed there. In addition, the DISCON function I want to use does not seem to work. Still I do not get any error messages.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
@Steve_Lionel, you said it told me that the DLL was loaded. Shouldn't that also be in the output window?
libdiscon.dll is not listed there. In addition, the DISCON function I want to use does not seem to work. Still I do not get any error messages.
Since I don't know what you did see, I can't comment. Please show, don't describe. I built a small application with a DLL and did get a message about my DLL in the output window. (Line 6)
'Console20.exe' (Win32): Loaded 'D:\Projects\Console20\x64\Debug\Console20.exe'. Symbols loaded.
'Console20.exe' (Win32): Loaded 'C:\Windows\System32\ntdll.dll'.
'Console20.exe' (Win32): Loaded 'C:\Windows\System32\kernel32.dll'.
'Console20.exe' (Win32): Loaded 'C:\Windows\System32\KernelBase.dll'.
'Console20.exe' (Win32): Loaded 'C:\Windows\System32\apphelp.dll'.
'Console20.exe' (Win32): Loaded 'D:\Projects\Console20\Dll1.dll'. Symbols loaded.
'Console20.exe' (Win32): Loaded 'C:\Windows\System32\vcruntime140d.dll'.
'Console20.exe' (Win32): Loaded 'C:\Windows\System32\ucrtbased.dll'.
'Console20.exe' (Win32): Loaded 'C:\Program Files (x86)\Intel\oneAPI\compiler\2022.2.0\windows\redist\intel64_win\compiler\libifcoremdd.dll'. Module was built without symbols.
'Console20.exe' (Win32): Loaded 'C:\Program Files (x86)\Intel\oneAPI\compiler\2022.2.0\windows\redist\intel64_win\compiler\libifcoremdd.dll'. Module was built without symbols.
'Console20.exe' (Win32): Unloaded 'C:\Program Files (x86)\Intel\oneAPI\compiler\2022.2.0\windows\redist\intel64_win\compiler\libifcoremdd.dll'
'Console20.exe' (Win32): Loaded 'C:\Windows\System32\imagehlp.dll'.
'Console20.exe' (Win32): Loaded 'C:\Windows\System32\ucrtbase.dll'.
'Console20.exe' (Win32): Loaded 'C:\Program Files (x86)\Intel\oneAPI\compiler\2022.2.0\windows\redist\intel64_win\compiler\libmmd.dll'. Symbols loaded.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
The output window shows the following:
"DeSiO.exe" (Win32): "C:\Users\Documents\DeSiO\DeSiO\DeSiO\x64\Debug\DeSiO.exe" loaded. Symbols loaded.
"DeSiO.exe" (Win32): "C:\Windows\System32\ntdll.dll" loaded.
"DeSiO.exe" (Win32): "C:\Windows\System32\kernel32.dll" loaded.
"DeSiO.exe" (Win32): "C:\Windows\System32\KernelBase.dll" loaded.
"DeSiO.exe" (Win32): "C:\Windows\System32\vcruntime140d.dll" loaded.
"DeSiO.exe" (Win32): "C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2020.4.311\windows\redist\intel64_win\compiler\libifcoremdd.dll" loaded. Module was built without symbols.
"DeSiO.exe" (Win32): "C:\Windows\System32\imagehlp.dll" loaded.
"DeSiO.exe" (Win32): "C:\Windows\System32\ucrtbase.dll" loaded.
"DeSiO.exe" (Win32): "C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2020.4.311\windows\redist\intel64_win\mkl\mkl_intel_thread.dll" loaded. Module was built without symbols.
"DeSiO.exe" (Win32): "C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2020.4.311\windows\redist\intel64_win\compiler\libmmdd.dll" loaded. Symbols loaded.
"DeSiO.exe" (Win32): "C:\Windows\System32\ucrtbased.dll" loaded.
"DeSiO.exe" (Win32): "C:\Windows\System32\ucrtbased.dll" loaded.
"DeSiO.exe" (Win32): "C:\Windows\System32\ucrtbased.dll" unloaded.
"DeSiO.exe" (Win32): "C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2020.4.311\windows\redist\intel64_win\compiler\libmmd.dll" loaded. Symbols loaded.
"DeSiO.exe" (Win32): "C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2020.4.311\windows\redist\intel64_win\mkl\mkl_core.dll" loaded. Module was built without symbols.
"DeSiO.exe" (Win32): "C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2020.4.311\windows\redist\intel64_win\mkl\mkl_avx512.dll" loaded. Module was built without symbols.
"DeSiO.exe" (Win32): "C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2020.4.311\windows\redist\intel64_win\mkl\mkl_vml_avx512.dll" loaded. Module was built without symbols.
"DeSiO.exe" (Win32): "C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2020.4.311\windows\redist\intel64_win\mkl\libimalloc.dll" loaded. Module was built without symbols.
"DeSiO.exe" (Win32): "C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2020.4.311\windows\redist\intel64_win\mkl\libimalloc.dll" unloaded.
"DeSiO.exe" (Win32): "C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2020.4.311\windows\redist\intel64_win\mkl\libimalloc.dll" loaded. Module was built without symbols.
"DeSiO.exe" (Win32): "C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2020.4.311\windows\redist\intel64_win\mkl\libimalloc.dll" unloaded.
"DeSiO.exe" (Win32): "C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2020.4.311\windows\redist\intel64_win\compiler\libiomp5md.dll" loaded. Symbols loaded.
So the libdiscon.dll does not appear here. However, when I remove the LIB from the folder, I get an error message. Could the error have occurred when creating the LIB from the DLL?
Another thing I wonder is why the commands associated with C programming such as "iso_c_binding" are used at all, when the called DLL was created in Fortran.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Furthermore, my observation is that the programme does the same when I comment out the line which calls the DLL
!DEC$ ATTRIBUTES DLLEXPORT :: DISCON
The only difference is that a programme.lib file is created.
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page