That's a tricky one. The working directory is not necessarily the directory where a DLL is located. It gets more complicated, though. The DLL's location is a bit difficult to determine from inside the DLL itself since these libraries can be loaded from anywhere, in theory. Getting the path of the executable is usually easier, but we can locate the DLL's path using a chain of Windows API calls.
We can use a combination of Windows calls to GetModuleHandleEx and GetModuleFileName to pull this off. Here's a module that wraps them nicely:
module dllpath
contains
function get_dll_handle(justname) result(hModule)
use iso_c_binding
implicit none
character(*), intent(in)::justname
interface
function GetModuleHandleEx(dwFlags, pModuleName, phModule) bind(c, name="GetModuleHandleExA")
use iso_c_binding
integer(kind=c_int32_t), value::dwFlags
type(c_ptr), value::pModuleName
type(c_ptr), value::phModule
logical(kind=c_bool)::GetModuleHandleEx
end function GetModuleHandleEx
end interface
character(kind=c_char, len=:), pointer::c_justname, c_fullpath
type(c_ptr)::c_fullpath_loc
type(c_ptr), target::hModule
hModule = c_null_ptr
allocate(character(len=len(justname)+1)::c_justname)
c_justname = trim(justname)//c_null_char
c_fullpath_loc = c_loc(c_fullpath)
if(.not. GetModuleHandleEx(int(0, kind=c_int32_t), c_loc(c_justname), c_loc(hModule))) then
Print *, "ERROR: Could not retrieve module handle"
hModule = c_null_ptr
end if
deallocate(c_justname)
end function get_dll_handle
function get_module_path(hModule, fullpath)
use iso_c_binding
implicit none
type(c_ptr), intent(in)::hModule
character(*), intent(out)::fullpath
logical::get_module_path
integer::i
interface
function GetModuleFileName(hMod, pFilename, nSize) bind(c, name="GetModuleFileNameA")
use iso_c_binding
integer(kind=c_int32_t), value::nSize
type(c_ptr), value::hMod, pFilename
integer(kind=c_int32_t)::GetModuleFileName
end function GetModuleFileName
end interface
character(kind=c_char, len=:), pointer::c_fullpath
allocate(character(len=len(fullpath)+1)::c_fullpath)
if(GetModuleFileName(hModule, c_loc(c_fullpath), len(fullpath)+1) == 0) then
get_module_path = .false.
else
i = index(c_fullpath, c_null_char)
fullpath = c_fullpath(1:i-1)
get_module_path = .true.
end if
deallocate(c_fullpath)
end function get_module_path
end module dllpath
Things tend to be messy because we need to pass valid C strings into these procedures. I've tried to isolate them, though. To use this in a routine, you would need first to use the proper modules:
use dllpath
use iso_c_binding, only: c_ptr ! Needed for holding a handle to our DLL
Next, you need to declare some variables to fill in with values:
character(2048)::path ! Probably long enough
type(c_ptr)::hMod ! A handle to our module
And finally, to get the info, we first request the DLL handle using just the base name of the DLL and then pass the handle to the routine to get the path:
hMod = get_dll_handle("my_library.dll")
if(.not. c_associated(hMod)) then
Print *, "no handle"
end if
if(get_module_path(hMod, path)) then
Print *, path
else
Print *, "no path"
end if
Again, it isn't a simple process because you need the operating system's help to determine where a DLL is located.
Jeff Armstrong
Approximatrix, LLC