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

Using BIND(C) for SetDllDirectory

jean
Beginner
1,566 Views

I am able to call SetDllDirectory using the following code (found on your web https://software.intel.com/en-us/forums/intel-visual-fortran-compiler-for-windows/topic/291698):

INTERFACE
    FUNCTION SetDllDirectory(lpPathName)
        IMPORT
        LOGICAL(KIND=1) :: SetDllDirectory
        !DEC$ ATTRIBUTES DEFAULT, STDCALL, DECORATE, ALIAS:'SetDllDirectoryA' :: SetDllDirectory
        !DEC$ ATTRIBUTES REFERENCE, ALLOW_NULL :: lpPathName
        !
        CHARACTER(LEN=*) lpPathName ! LPCSTR lpPathName
    END FUNCTION
END INTERFACE

I am curious, why the function is called SetDllDirectoryA in the Alias? What is the extra A?

Now I want to use BIND(C) to do the same, but it does not work. Here is my example, but it results into link error for SetDllDirectoryA

INTERFACE
    FUNCTION SetDllDirectory(lpPathName), BIN(C, name='SetDllDirectoryA')
        IMPORT
        LOGICAL(KIND=1) :: SetDllDirectory
       CHARACTER(KIND=C_CHAR), DIMENSION(*), INTENT(IN) :: lpPathName
        !        
    END FUNCTION
END INTERFACE

The USE, INTRINSIC :: ISO_C_BINDING 

statement is placed in my main module declaring this interface.

0 Kudos
14 Replies
IanH
Honored Contributor II
1,567 Views

The Win32 API provides two versions of any API taking a character string - a "wide" unicode (UTF-16) variant suffixed with W and an "ansi" variant, details of which are dependent on the code page in use, suffixed with 'A'. 

On 32 bit windows, the Windows API's use the STDCALL calling convention.  This calling convention is not typically used by 32 bit Windows C compilers of today.  BIND(C), without other Fortran compiler specific directives, specifies "compatible with [some specified] C compiler".  The linker names for a certain procedure name generated by the two different calling conventions are [thankfully] different, hence you see the error.

Later versions of ifort let you mix BIND(C) and the !DEC$ ATTRIBUTES STDCALL specification, earlier versions do not.

0 Kudos
jean
Beginner
1,567 Views

Thanks

So the conclusion is that in this case I should stay with the given code and not try to use BIND(C)... Which is disappointing since it means that we are still unable to eliminate the strange looking !DEC$ directives.

0 Kudos
andrew_4619
Honored Contributor II
1,567 Views

google msdn SetDllDirectory  and you will find the ref page for that sdk routine

https://msdn.microsoft.com/en-us/library/windows/desktop/ms686203(v=vs.85).aspx

I will also note that the function is type BOOL so integer(kind=4) is your nearest equivalent. or USE IFWINTY and then have INTEGER(BOOL)

 

0 Kudos
jean
Beginner
1,567 Views

Thanks

I know how to use it in C, and the code shown in my first question works in Fortran. I was just curious if I can make it work with BIND(C,...).

Apparently it is not possible. This is an essential function that does not exist in Fortran, from what I know.

0 Kudos
FortranFan
Honored Contributor II
1,567 Views

jean wrote:

Thanks

I know how to use it in C, and the code shown in my first question works in Fortran. I was just curious if I can make it work with BIND(C,...).

Apparently it is not possible. This is an essential function that does not exist in Fortran, from what I know.

As IanH mentioned in message #2, you can use BIND(C), however you still need the STDCALL attribute because of the way Microsoft Windows API are set up.  And if you're using a newer version, say current compiler 16, of Intel Fortran, BIND(C) and STDCALL can be used together. 

0 Kudos
IanH
Honored Contributor II
1,567 Views

jean wrote:

Thanks

So the conclusion is that in this case I should stay with the given code and not try to use BIND(C)... Which is disappointing since it means that we are still unable to eliminate the strange looking !DEC$ directives.

Not unless you want to restrict yourself to 64 bit windows.  On 64 bit windows the STDCALL API calling convention is different, and is commonly the same as the calling convention of a C compiler.

On 32 bit windows this issue is not Fortran language specific (though it may vary from compiler to compiler) - for example C code typically requires something equivalent to the strange looking __stdcall declaration specifier in the declaration of the function..

0 Kudos
JVanB
Valued Contributor II
1,567 Views

Unfortunately you can't use a compiler switch to make the default C compiler in interface blocks the one that you get if you use !DEC$ ATTRIBUTES STDCALL. Example of BIND(C) interface body that works:

module M
   use ifwin
   use ISO_C_BINDING
   implicit none
   private
   public SetDllDirectory
   interface
      function SetDllDirectory(lpPathName) bind(C,name='SetDllDirectoryA')
         import
         implicit none
!DEC$ ATTRIBUTES STDCALL :: SetDllDirectory
         integer(BOOL) SetDllDirectory
         character(kind=C_CHAR), intent(in), dimension(*) :: lpPathName
      end function SetDllDirectory
   end interface
end module M

program P
   use M
   use ISO_C_BINDING
   implicit none
   character(kind=C_CHAR), pointer :: lpPathName(:)
   integer R
   call C_F_POINTER(C_NULL_PTR,lpPathName,[0])
   R = SetDllDirectory(lpPathName)
   write(*,'(*(g0))') 'Result = ',R
end program P

Reading the documentation, any nonzero return value is indicative of successful execution of SetDllDirectory. In particular if you use LOGICAL(1) as your result type and the result is 256, your program will see an unsuccessful result erroneously. Also is /fpscomp:nologicals is in effect and the result is 2, your program will again see an unsuccessful result erroneously. Don't mix Fortran LOGICAL variables with any kind of C object and don't use the wrong size variables in interfacing.

 

0 Kudos
jean
Beginner
1,567 Views

Thanks, I will try your code.

 

0 Kudos
jean
Beginner
1,567 Views

I need help with another issue on DLLs.

The above call of  SetDllDirectory is apparently adding a new path, but removing the previously added search path. So I presume I need to use the call to AddDllDirectory and then use LoadLibraryEx. I am wondering if there is an easier way to add DLL search paths in Fortran. If not, do you have an example of usage for the two above functions?

0 Kudos
FortranFan
Honored Contributor II
1,567 Views

jean wrote:

I need help with another issue on DLLs.

The above call of  SetDllDirectory is apparently adding a new path, but removing the previously added search path. So I presume I need to use the call to AddDllDirectory and then use LoadLibraryEx. I am wondering if there is an easier way to add DLL search paths in Fortran. If not, do you have an example of usage for the two above functions?

How the Microsoft Windows API work should be the same whether the calls are made in Fortran or C, C++, etc..  So your statement, "I am wondering if there is an easier way to add DLL search paths in Fortran", doesn't make much sense.  Earlier you said "I know how to use it in C", well put together some C code that does what you want using Microsoft APIs and post it here - some reader here will surely be able to show you how to do the same in Fortran.

0 Kudos
jean
Beginner
1,567 Views

Thanks, you are absolutely correct. I did that, but I was curious about Fortran with the new features.

Thanks again.

0 Kudos
FortranFan
Honored Contributor II
1,567 Views

jean wrote:

.. I did that, but I was curious about Fortran with the new features. ..

By "I did that", I assume you mean you have called Windows APIs from C and you understand how those functions work.  Do you find "call of  SetDllDirectory" (you said it is "apparently adding a new path, but removing the previously added search path") any different in Fortran versus C?

0 Kudos
jean
Beginner
1,567 Views

The call above was made from Fortran to C so it works exactly like in C. I did not realize at the beginning how it was behaving exactly. 

The issue is that it is not possible to do these things directly from Fortran. For example loadlibrary works, getprocaddress works and also loadlibraryEx, but not AddDllDirectory . In fact the working functions are not documented in the Help of Intel, so I was wondering if there are some other functions to do the same things. Calling C is an option, but why we do not have these features in Fortran directly... 

Thanks for all the answers.

0 Kudos
IanH
Honored Contributor II
1,567 Views

AddDLLDirectory is a relatively recent addition to the Windows API.  It won't work on Windows 7, for example, unless you have particular updates installed.  The definitions for some of these recent API's (and for some that are not so recent) are simply yet to be added to the IFWIN style modules.  It is the same situation as if you are programming in C and using an older version of the Windows SDK - you won't have these API's in the Windows header files.

But LoadLibrary and the like have been around ever since Windows NT.

But it is easy enough to write your own version of the interface, as seen upthread.

0 Kudos
Reply