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

function GETPROCADDRESS not found

Karin_H_
Beginner
1,979 Views

 

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?

 

0 Kudos
1 Solution
JVanB
Valued Contributor II
1,977 Views

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.

 

View solution in original post

0 Kudos
12 Replies
Arjen_Markus
Honored Contributor I
1,982 Views

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.

0 Kudos
JVanB
Valued Contributor II
1,982 Views

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.

 

0 Kudos
Anthony_Richards
New Contributor I
1,982 Views

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

0 Kudos
JVanB
Valued Contributor II
1,982 Views

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?

 

0 Kudos
Karin_H_
Beginner
1,982 Views

 

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!

0 Kudos
GVautier
New Contributor II
1,982 Views

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.

0 Kudos
JVanB
Valued Contributor II
1,978 Views

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.

 

0 Kudos
Karin_H_
Beginner
1,982 Views

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!

 

0 Kudos
Arjen_Markus
Honored Contributor I
1,982 Views

That requires no change regarding the !$DEC statement. The code Repeat Offender posted should work for that scenario as well.

0 Kudos
Karin_H_
Beginner
1,982 Views

 

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


 

0 Kudos
Karin_H_
Beginner
1,982 Views

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? 

 

 

0 Kudos
FortranFan
Honored Contributor II
1,982 Views

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

 

0 Kudos
Reply