- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi everybody,
I have a dll that is compiled withFORTRAN11, when it is loaded from C# program (not during debug, the problem is afterinstall the programs )C# program correctly load the DLL without any path and only by namebecauseboth of them are in the same directory.
but during the run ofFORTRANDLL, current path in the DLL is not same as position of that DLL is there and I can not load another file from the FORTRANDLL.
Is there any function to find the correct path (the directory of DLL), I cheked the "getcwd" and it gives the another path that is wrongcurrentpath.
Ihanks
Link Copied
13 Replies
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hello
A dll is searched first in the directory than contains the executable that wants to load it. So if your DLL is in the same directory it will be found.
But the getcwd function retrieves the current working directory that can be different from the DLL location directory.
If you want to retrieve the path of the DLL, you can use the GetModuleFileName API function that will return the full path of the DLL.
A dll is searched first in the directory than contains the executable that wants to load it. So if your DLL is in the same directory it will be found.
But the getcwd function retrieves the current working directory that can be different from the DLL location directory.
If you want to retrieve the path of the DLL, you can use the GetModuleFileName API function that will return the full path of the DLL.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thanks I checked theGetModuleFileName it is OK.
Usedfwin
CHARACTER*1024 ::
DLLPath
DLLPath = ''
N = GetModuleFileName(0, DLLPath, len(DLLPath))
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
GetModuleFileName with 0 handle returns the path to the executable that started the current process.
Current directory (getcwd) can be pretty much any directory, as set up by the program shortcut, or changed by the user in the UI.
OPEN statement, however, by default opens the file in the current directory.
In order to a) open the file in the same directory as the exe and b) not to change the current directory (which is generally a bad practice to do from code), find the first path and open file there explicitly:
Current directory (getcwd) can be pretty much any directory, as set up by the program shortcut, or changed by the user in the UI.
OPEN statement, however, by default opens the file in the current directory.
In order to a) open the file in the same directory as the exe and b) not to change the current directory (which is generally a bad practice to do from code), find the first path and open file there explicitly:
[fortran]CHARACTER(MAX_PATH) :: DLLPath integer:: dirLen DLLPath = '' N = GetModuleFileName(0, DLLPath, len(DLLPath)) !Strip the path up to the trailing backslash: dirLen = index(Dllpath, "", .true.) DLLPath = DLLPath(1:dirLen) OPEN(42, file=TRIM(DLLPath)//"MyFile.txt") ...[/fortran]
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Jugoslav,
once a DLL has been opened, is it possible to find out the directory it was loaded from?
As you say, GetModuleFileName returns the Executable (Excel) in my case. I would like to know which DLL(s) are actually being loaded at runtime, in part to check that the correct version of the code is in use.
Thanks,
David
once a DLL has been opened, is it possible to find out the directory it was loaded from?
As you say, GetModuleFileName returns the Executable (Excel) in my case. I would like to know which DLL(s) are actually being loaded at runtime, in part to check that the correct version of the code is in use.
Thanks,
David
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Quoting David White
Jugoslav,
once a DLL has been opened, is it possible to find out the directory it was loaded from?
once a DLL has been opened, is it possible to find out the directory it was loaded from?
Yes, if you give GetModuleFileName a correct handle (HMODULE/HINSTANCE). There are two ways to do it:
- By writing a DllMain and storing its hinstDll argument into a common/module variable, and
- The following should also work. The slight drawback is that you must know your .dll name in advance (thus, won't reliably work in .e.g. a .lib):
[fortran]integer(HANDLE):: hDll character(MAX_PATH):: path hDll = GetModuleHandle("MyDll.dll"//char(0)) len = GetModuleFileName(hDll, path, len(path)) !Returns the full path now[/fortran]
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
[fortran]MODULE DLLUtils
USE IFWINTY
IMPLICIT NONE
PRIVATE
PUBLIC :: GetThisDLLsFileName
INTERFACE
FUNCTION GetModuleHandleEx(dwFlags, lpModuleName, phModule)
!DEC$ ATTRIBUTES DEFAULT, STDCALL, DECORATE, ALIAS:'GetModuleHandleExA' :: GetModuleHandleEx
USE ISO_C_BINDING, ONLY: C_FUNPTR
USE IFWINTY
IMPLICIT NONE
INTEGER(DWORD), INTENT(IN), VALUE :: dwFlags
TYPE(C_FUNPTR), INTENT(IN), VALUE :: lpModuleName
INTEGER(HANDLE), INTENT(OUT) :: phModule
!DEC$ ATTRIBUTES REFERENCE :: phModule
INTEGER(BOOL) :: GetModuleHandleEx
END FUNCTION GetModuleHandleEx
END INTERFACE
INTEGER(DWORD), PARAMETER :: GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS &
= 4_DWORD
CONTAINS
SUBROUTINE GetThisDLLsFileName(filename)
!DEC$ ATTRIBUTES DLLEXPORT :: GetThisDLLsFileName
USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_FUNLOC
USE IFWIN
!----
CHARACTER(:), INTENT(OUT), ALLOCATABLE :: filename
!----
INTEGER(BOOL) :: rc
INTEGER(HANDLE) :: my_handle
INTEGER(DWORD) :: buf_len
INTEGER(DWORD) :: str_len
!****
rc = GetModuleHandleEx( &
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, &
C_FUNLOC(AnExportedProcedure), &
my_handle )
! should test rc, but I'm lazy...
buf_len = 256_DWORD
DO
ALLOCATE(CHARACTER(buf_len) :: filename)
str_len = GetModuleFilename(my_handle, filename, buf_len)
IF (str_len < buf_len) THEN
! choppity by reallocation
filename = filename(1:str_len)
RETURN
END IF
! buffer too small, double and try again...
buf_len = buf_len * 2
DEALLOCATE(filename)
END DO
END SUBROUTINE GetThisDLLsFileName
SUBROUTINE AnExportedProcedure() BIND(C)
!DEC$ ATTRIBUTES DLLEXPORT :: AnExportedProcedure
END SUBROUTINE
END MODULE DLLUtils
[/fortran] and to test...
[fortran]PROGRAM DLLUtilsTest USE DLLUtils IMPLICIT NONE !--- CHARACTER(:), ALLOCATABLE :: dll_name !**** ! ifort bug requires this to be allocated on entry?? ALLOCATE(CHARACTER(1) :: dll_name) CALL GetThisDLLsFileName(dll_name) PRINT "(A)", dll_name END PROGRAM DLLUtilsTest[/fortran]
might need /assume:realloc_lhs to work.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Aother solution would be to add a DllMain routine to your project. This routine is called each time the DLL is loaded and attached to a process or a thread. The routine has the handle of the dll as the first argument so you can retrieve the path of the dll from the GetModuleFileName API function.
This the way I proceed frequently:
Then in the main unit, you can retrieve the handle and call the GetModuleFileName API function, in the example below it is used to retrieve the full dll path in order to access the dll resources and particularly the dll version information:
Note that "GetVersionInfoString" is a function I have created to retrieve the version information from the DLL resources thus it is not part of the Windows API.
Regards,
Phil.
This the way I proceed frequently:
[fxfortran] integer(4) function DllMain(hInst, ul_reason_being_called, lpReserved)
!DEC$ IF DEFINED(_X86_)
!DEC$ ATTRIBUTES STDCALL, ALIAS : '_DllMain@12' :: DllMain
!DEC$ ELSE
!DEC$ ATTRIBUTES STDCALL, ALIAS : 'DllMain' :: DllMain
!DEC$ ENDIF
use DFWINA
integer(4) hInst
integer(4) ul_reason_being_called
integer(4) lpReserved
C
integer(4) hDLL
common /DLL_Handle/ hDLL
C
hDLL = hInst
C
ul_reason_being_called = ul_reason_being_called
lpReserved = lpReserved
C
DllMain = 1
return
C
end
[/fxfortran]Then in the main unit, you can retrieve the handle and call the GetModuleFileName API function, in the example below it is used to retrieve the full dll path in order to access the dll resources and particularly the dll version information:
[fxfortran]C
integer(4) hDLL
common /DLL_Handle/ hDLL
C
character(len=256) DLL_Name, VERS_INFO
C
integer(4) n
C
n = GetModuleFileName(hDLL, DLL_Name, sizeof(DLL_Name))
if (n /= 0) then
call GetVersionInfoString(DLL_Name, VERS_INFO)
else
VERS_INFO = 'N/A'
end if
[/fxfortran]Note that "GetVersionInfoString" is a function I have created to retrieve the version information from the DLL resources thus it is not part of the Windows API.
Regards,
Phil.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Phil,
Tried your method in my situation where I am loading a DLL from Excel via VBA. Like the other methods I have tried, this still returns the name of the exe (Excel) in DLL_Name, rather than the DLL.
David
Tried your method in my situation where I am loading a DLL from Excel via VBA. Like the other methods I have tried, this still returns the name of the exe (Excel) in DLL_Name, rather than the DLL.
David
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
David.
I am surprised that it doesn't work because I have tried the same thing, i.e. a Fortran dll declared in VBA to use some exported routines (in my example I was using IMSL routines to perform cubic spline interpolation in Excel). In my Fortran dll I have added the Dllmain function as above and when I call the GetModuleFileName using the "hInst" handle passed by argument in DllMain the results is the full path to the dll so I don't understand what is going wrong in your case.
Regards,
Phil.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Here's how I've been doing it:
ghinstance=GetModuleHandle("EnergyPlusDLL.dll"C)
ret = GetModuleFileName (INT(ghInstance), szFullPath, len(szFullPath))
I am currently challenged, however, to get the Version info from 64 bit compiles, so if the above works for you I would be grateful for any assistance in working out the version info.
Linda
ghinstance=GetModuleHandle("EnergyPlusDLL.dll"C)
ret = GetModuleFileName (INT(ghInstance), szFullPath, len(szFullPath))
I am currently challenged, however, to get the Version info from 64 bit compiles, so if the above works for you I would be grateful for any assistance in working out the version info.
Linda
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Linda,
Here is the GetFileVersionInfo source code. I hope it will work in 64 bit (I do compile only 32 bit executables):
[fortran] subroutine GetVersionInfoString(EXE_Name, VERSION_INFO)
use DFWIN
implicit none
character(len=*), intent(in) :: EXE_Name
character(len=*), intent(out) :: VERSION_INFO
! Retrieving the version information from an executable or dll
integer(4) Hndl
integer(4) nBytes
integer(4) rc
character(len=256) StringInfo
character(len=10) CharSet
integer(4) uVersionLen, uTranslationLen
integer(4) lpstrVffInfo
integer(4) hMem
integer(4) i
character(len=256) lpversion
integer(1) lpTranslation(256)
integer(4) lplpTranslation, lplpVersion
pointer(lplpTranslation, lpTranslation)
pointer(lplpVersion, lpversion)
nBytes = GetFileVersionInfoSize(EXE_Name, loc(Hndl))
if (nBytes /= 0) then
! Allocating the required memory bloc
hMem = GlobalAlloc(GMEM_MOVEABLE, int(nBytes))
lpstrVffInfo = GlobalLock(hMem)
! Retrieving the information bloc
rc = GetFileVersionInfo(EXE_Name, Hndl, nBytes, lpstrVffInfo)
! Retrieving the code page
StringInfo = "\VarFileInfo\Translation"C
rc = VerQueryValue(lpstrVffInfo, StringInfo, lplpTranslation, loc(uTranslationLen))
! Retrieving the version information string
write(CharSet,'(4z2.2)') lpTranslation(2),lpTranslation(1),lpTranslation(4),lpTranslation(3)
StringInfo = '\StringFileInfo'//trim(CharSet)//'\FileVersion'C
rc = VerQueryValue(lpstrVffInfo, StringInfo, lplpVersion, loc(uVersionLen))
! Decoding
if (rc /= 0) then
rc = lstrcpy(VERSION_INFO, lpVersion)
uVersionLen = min(uVersionLen,index(VERSION_INFO,char(0)))
do i=uVersionLen,len(VERSION_INFO)
VERSION_INFO(i:i) = ' '
end do
else
VERSION_INFO = 'N/A'
end if
! Freeing the allocated memory bloc
rc = GlobalUnlock(hMem)
rc = GlobalFree(hMem)
else
! Version information unavailable or not found
VERSION_INFO = 'N/A'
end if
return
end
[/fortran] Regards,
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thank you, Phill -- I will give this a try.
Linda
Linda
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
The handles (hMem, etc.) should be declared INTEGER(HANDLE). The "lp" variables should be INTEGER(LPVOID).
Reply
Topic Options
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page