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
초급자
5,526 조회수

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 포인트
14 응답
IanH
명예로운 기여자 III
5,527 조회수

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 포인트
jean
초급자
5,527 조회수

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 포인트
andrew_4619
명예로운 기여자 III
5,527 조회수

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 포인트
jean
초급자
5,527 조회수

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 포인트
FortranFan
명예로운 기여자 III
5,527 조회수

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 포인트
IanH
명예로운 기여자 III
5,527 조회수

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 포인트
JVanB
소중한 기여자 II
5,527 조회수

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 포인트
jean
초급자
5,527 조회수

Thanks, I will try your code.

 

0 포인트
jean
초급자
5,527 조회수

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 포인트
FortranFan
명예로운 기여자 III
5,527 조회수

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 포인트
jean
초급자
5,527 조회수

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

Thanks again.

0 포인트
FortranFan
명예로운 기여자 III
5,527 조회수

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 포인트
jean
초급자
5,527 조회수

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 포인트
IanH
명예로운 기여자 III
5,527 조회수

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 포인트
응답