Topic: Incorrect working directory for DLL

Hi everybody,

I have a DLL which is compiled with SF. But during the run of DLL, working directory from "getcwd" is not the same as the path where the DLL is. Are there any ways to return where the DLL file actually located?

Thanks.

Re: Incorrect working directory for DLL

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

3 (edited by Gale 2024-02-27 01:59:46)

Re: Incorrect working directory for DLL

jeff wrote:

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.

Thanks a lot, Jeff god. It really works well.