- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I'm just trying to call an external function via dll.
There is an error on compiling the source code by calling the function. It seems that the function GetProcAddress ist not found.
error #6284: There is no matching specific function for this generic function reference. [GETPROCADDRESS]
lib_handle = LOADLIBRARY 1 ("c:\svn\NOx_DYN_dll\bin\NOx_DYN_dll.dll"C) if (lib_handle.eq.0) then WRITE(6,*) 'NOx_DYN_dll.dll not loaded' stop else ptr = GetProcAddress(lib_handle, "NOx_DYN_dll") if (ptr.eq.0) then write(6,*) 'Error: GetProcAddress' stop endif endif
Some ideas?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Don't worry about the newbie stuff. I've been banging my head against a brick wall in another thread for weeks now. I got your code to work. The dll:
subroutine test_dll !DEC$ ATTRIBUTES DLLEXPORT, alias: 'test_dll' ::test_dll write(6,*) 'Test_dll' end
The executable:
program test_call_dll use ifport, IGNORE => GetLastError use ifwin use ISO_C_BINDING integer(HANDLE) lib_handle integer(LPVOID) ptr abstract interface subroutine test_dll_TEMPLATE() end subroutine test_dll_TEMPLATE end interface procedure(test_dll_TEMPLATE), pointer :: test_dll integer(ULONG) last_error ! lib_handle = LOADLIBRARY & ! ("c:\svn\test_dll\x64\debug\test_dll.dll"//achar(0)) lib_handle = LOADLIBRARY("test_dll.dll"//achar(0)) if (lib_handle.eq.0) then last_error = GetLastError() WRITE(6,*) 'Last error = ',last_error WRITE(6,*) 'test_dll.dll not loaded' stop else ptr = GetProcAddress(lib_handle, "test_dll"//achar(0)) if (ptr.eq.0) then write(6,*) 'Could not find test_dll in test_dell.dll' stop else call C_F_PROCPOINTER(transfer(ptr,C_NULL_FUNPTR),test_dll) Call test_dll endif endif end
So what am I doing different? In the dll, I provide a name for the dll that I know I can use, as in Message #7, point 1. In the executable, I declared an interface (that's the abstract interface stuff), then I was careful to declare the variable ptr as integer(LPVOID). In going from 32 bits to 64 bits the biggest difference visible to software is the promotion of pointers and handles from 32 bits to 64 bits. Thus you have to be careful to look at not only the MSDN documentation for the function you want to use but also check out the interface as specified in, in my compiler, C:\Program Files (x86)\Intel\Composer XE\compiler\include\kernel32.f90. After a while you get so that you can anticipate what are the pointers and handles, but even so some of the ifort provided interfaces can be a little funky for historical reasons so it never hurts to look at the interface definition in ifort's compiler-provided *.f90 file.
If something may go wrong with invoking a Win32 API function it's a good thing to call GetLastError() to find out what happened. It has to follow right after the API call and before and READ or WRITE statements because ifort mungs the last error code with its I/O library. Turned out I really needed it because I forgot that you were putting your dll in another directory which I didn't want to make on my computer, so I had to change to look in the current directory for my example. In your original program the source code was freeform but you had a fixed-format style continuation in your call to LOADLIBRARY. I fixed that up for you before I changed it to suit my system. ifort seems to be making sort of a mistake in that they write out an interface both in ifport.f90 and kernel32.f90, and this causes problems if both IFPORT and IFWIN are USEd in the same program unit which also invokes GetLastError. The correct fix on the ifort side would be for module ifport to USE kernel32, only: GetLastError, but for the short term we have to wipe out what syntactically is a duplicate interface for GetLastError by renaming one of the interfaces or consistently applying ONLY clauses to all the USE statements of one of the modules. I chose renaming as shown.
As for invoking the procedure, you need either a cray pointer (and the ifort interfaces were originally written to work with cray pointers) or to use the newfangled f2003 syntax with procedure pointers and C_F_PROCPOINTER. I chose the latter route, using TRANSFER to convert the INTEGER(LPVOID) ptr to a C_FUNPTR for use with C_F_PROCPOINTER to point the procedure pointer test_dll at the procedure you wanted to invoke. I tested the code above and it worked. Hope this helps.
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
You should check the types and kinds of the arguments - notably that of lib_handle. Probably there is a mismatch with the exact signature that is required. Guessing from the source file kernel32.f90, I would say that lib_handle must be of type/kind integer(handle), not a mere integer.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
And when you fix that, the strings have to be fixed as well. The first one should be changed to
"c:\svn\NOx_DYN_dll\bin\NOx_DYN_dll.dll"//achar(0)
because the "string"C syntax also activates the C escape syntax within the string. That could be fixed by changing the backslashes to forward slashes because outside of CMD.EXE Windows accepts either kind of slash as a directory separator. But why clutter our minds up with such trivia when there is already a standard syntax for NUL-terminated strings?
The second should also be changed to
"NOx_DYN_dll"//achar(0)
for the same reasons.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
The interface to GetProcAddress is in KERNEL32.F90 which can be included by adding the necessary module
USE IFWIN
in your subprogram. That will ensure that the function is call is recognised and eventually found in a library. The recommended changes to the arguments should be made also for it to work properly. I note that the interface to LOADLIBRARY also resides in KERNEL32.F90, so if the USE IFWIN is not present, that function should also not be recognised, which is curious.
For what it's worth, I use CHAR(0) myself to terminate a text string with a null character so it becomes acceptable to the Windows API functions that require null-terminated character strings.esides
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
It's clear from the error message that the O.P. is already USEing IFWIN because the error refers to a generic function reference, which is impossible without an explicit interface being available. Arjen Markus'es diagnosis therefore seems correct in that INTEGER(HANDLE) is not the same as default INTEGER on 64-bit systems.
GetProcAddress is generic because it's allowed to pass the function's ordinal value in lieu of the address of its name. You can do that by stuffing the ordinal value into the address field of a Fortran pointer via TRANSFER and C_F_PROCPOINTER, but this wasn't possible pre-f2003, and although it still could have been achieved with cray pointers in those days, the generic interface makes a lot of sense.
BTW, what's with the ALLOW_NULL? Shouldn't that be an error according to MSDN docs?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thank you for all your help!
Now I succeeded to compile the source, but now I have the next problem. PTR has value 0.
I created now a test with a simple example, so you could get a better overview
Main Program:
program test_call_dll use ifport use ifwin integer(handle) lib_handle integer(4) ptr lib_handle = LOADLIBRARY 1 ("c:\svn\test_dll\x64\debug\test_dll.dll"//achar(0)) if (lib_handle.eq.0) then WRITE(6,*) 'test_dll.dll not loaded' stop else ptr = GetProcAddress(lib_handle, "test_dll"//achar(0)) if (ptr.eq.0) then write(6,*) 'Could not find test_dll in test_dell.dll' stop else Call test_dll endif endif end
and subroutine from which I created a dll
subroutine test_dll !DEC$ ATTRIBUTES DLLEXPORT::test_dll write(6,*) 'Test_dll' end
My questions:
1. Do you have an idea why ptr = 0?
2. And can you tell me how the command is to call the external subroutine test_dll? I know the command is'nt like shown in the source.
I know for most of you this is a trivial problem, but it's the first time I'm trying to load an external dll, so please be patient with me :-)
Thank you in advance for your help!
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi
1. the "actual" name of your subroutine in the dll is surely not "
test_dll" because it may have be changed by the compiler (upper case and decoration).
2. you must declare an interface associated to the pointer in order to call the external subroutine.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Don't worry about the newbie stuff. I've been banging my head against a brick wall in another thread for weeks now. I got your code to work. The dll:
subroutine test_dll !DEC$ ATTRIBUTES DLLEXPORT, alias: 'test_dll' ::test_dll write(6,*) 'Test_dll' end
The executable:
program test_call_dll use ifport, IGNORE => GetLastError use ifwin use ISO_C_BINDING integer(HANDLE) lib_handle integer(LPVOID) ptr abstract interface subroutine test_dll_TEMPLATE() end subroutine test_dll_TEMPLATE end interface procedure(test_dll_TEMPLATE), pointer :: test_dll integer(ULONG) last_error ! lib_handle = LOADLIBRARY & ! ("c:\svn\test_dll\x64\debug\test_dll.dll"//achar(0)) lib_handle = LOADLIBRARY("test_dll.dll"//achar(0)) if (lib_handle.eq.0) then last_error = GetLastError() WRITE(6,*) 'Last error = ',last_error WRITE(6,*) 'test_dll.dll not loaded' stop else ptr = GetProcAddress(lib_handle, "test_dll"//achar(0)) if (ptr.eq.0) then write(6,*) 'Could not find test_dll in test_dell.dll' stop else call C_F_PROCPOINTER(transfer(ptr,C_NULL_FUNPTR),test_dll) Call test_dll endif endif end
So what am I doing different? In the dll, I provide a name for the dll that I know I can use, as in Message #7, point 1. In the executable, I declared an interface (that's the abstract interface stuff), then I was careful to declare the variable ptr as integer(LPVOID). In going from 32 bits to 64 bits the biggest difference visible to software is the promotion of pointers and handles from 32 bits to 64 bits. Thus you have to be careful to look at not only the MSDN documentation for the function you want to use but also check out the interface as specified in, in my compiler, C:\Program Files (x86)\Intel\Composer XE\compiler\include\kernel32.f90. After a while you get so that you can anticipate what are the pointers and handles, but even so some of the ifort provided interfaces can be a little funky for historical reasons so it never hurts to look at the interface definition in ifort's compiler-provided *.f90 file.
If something may go wrong with invoking a Win32 API function it's a good thing to call GetLastError() to find out what happened. It has to follow right after the API call and before and READ or WRITE statements because ifort mungs the last error code with its I/O library. Turned out I really needed it because I forgot that you were putting your dll in another directory which I didn't want to make on my computer, so I had to change to look in the current directory for my example. In your original program the source code was freeform but you had a fixed-format style continuation in your call to LOADLIBRARY. I fixed that up for you before I changed it to suit my system. ifort seems to be making sort of a mistake in that they write out an interface both in ifport.f90 and kernel32.f90, and this causes problems if both IFPORT and IFWIN are USEd in the same program unit which also invokes GetLastError. The correct fix on the ifort side would be for module ifport to USE kernel32, only: GetLastError, but for the short term we have to wipe out what syntactically is a duplicate interface for GetLastError by renaming one of the interfaces or consistently applying ONLY clauses to all the USE statements of one of the modules. I chose renaming as shown.
As for invoking the procedure, you need either a cray pointer (and the ifort interfaces were originally written to work with cray pointers) or to use the newfangled f2003 syntax with procedure pointers and C_F_PROCPOINTER. I chose the latter route, using TRANSFER to convert the INTEGER(LPVOID) ptr to a C_FUNPTR for use with C_F_PROCPOINTER to point the procedure pointer test_dll at the procedure you wanted to invoke. I tested the code above and it worked. Hope this helps.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thank you! Yes that helps me highly! And also thank You for the comments.
The example is running now. Great!
But I have another request... How do I have to change the source which you have posted, if my main program has to pass parameters with the subroutine, like this
subroutine test_dll(A,B,C) !DEC$ ATTRIBUTES DLLEXPORT, alias: 'test_dll' ::test_dll integer A double precision B real C(3) A=1 write(6,*) 'Test_dll' write(6,*) A,B,C end
What do I have to change? What do I have to consider?
Do you have some hints for me? Thank you very much in advance!
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
That requires no change regarding the !$DEC statement. The code Repeat Offender posted should work for that scenario as well.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thank you all!
Only for the sake of completeness here the adapted code, maybe somebody need's this too.
1. Code of the called subroutine
subroutine test_dll(A,B,C) !DEC$ ATTRIBUTES DLLEXPORT, alias: 'test_dll' ::test_dll real a integer B double precision C(3) A=A+100. B=B+100 C(1)=C(1)+100. C(2)=C(2)+100. C(3)=C(3)+100. write(6,*) 'Test_dll' write(6,*) A,B,C end
2. Code of the main program
program test_call_dll use ifport, IGNORE => GetLastError use ifwin use ISO_C_BINDING integer(HANDLE) lib_handle integer(LPVOID) ptr abstract interface subroutine test_dll_TEMPLATE(a1,b1,c1) real a1 integer B1 double precision C1(3) end subroutine test_dll_TEMPLATE end interface procedure(test_dll_TEMPLATE), pointer :: test_dll integer(ULONG) last_error real a1 integer B1 double precision C1(3) ! lib_handle = LOADLIBRARY & ! ("c:\svn\test_dll\x64\debug\test_dll.dll"//achar(0)) lib_handle = LOADLIBRARY 1 ("c:\svn\test_dll\x64\debug\test_dll.dll"//achar(0)) if (lib_handle.eq.0) then last_error = GetLastError() WRITE(6,*) 'Last error = ',last_error WRITE(6,*) 'test_dll.dll not loaded' stop else ptr = GetProcAddress(lib_handle, "test_dll"//achar(0)) if (ptr.eq.0) then write(6,*) 'Could not find test_dll in test_dell.dll' stop else call C_F_PROCPOINTER(transfer(ptr,C_NULL_FUNPTR), 1 test_dll) A1=4.0 B1=5 C1(1)=3.0 C1(2)=2.0 C1(3)=1.0 Call test_dll(A1,B1,C1) WRITE(6,*) 'Test_Call_dll' write(6,*) A1,B1,C1 endif endif end
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Is it able/necessary to unload the external dll in Fortran like in c++ with
FreeLibrary(hGetProcID_dLL);
If yes, how can I do this?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Karin H. wrote:
Is it able/necessary to unload the external dll in Fortran like in c++ with
FreeLibrary(hGetProcID_dLL);If yes, how can I do this?
Presumably you're aware of further Windows API functions to get more information on any issues, should they occur: https://msdn.microsoft.com/en-us/library/ms680582
The wrapper module you use from Intel Fortran, IFWIN, provides interfaces for most of the Windows API functions and you can consume as follows:
.. use ifwin, only : .., DWORD, BOOL, FreeLibrary, GetLastError, FormatMessage, .. .. integer(BOOL) :: Lsuccess integer(DWORD) :: ErrorCode .. Lsuccess = FreeLibrary( lib_handle ) if ( Lsuccess == 0 ) then !.. FreeLibrary function failed; ! Use GetLastError to fetch information on the failure ErrorCode = GetLastError() !.. If needed, invoke FormatMessage for further details. ..

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