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

Calling MFC Routines from Intel Fortran

SunMesa
Beginner
3,836 Views

Does Composer v19.1 contain interfaces to any MFC (Microsoft Foundation Class) API routines? If not, is it possible to add one manually, or does it have to be part of the Intel Fortran installation itself?

I was hoping to use the AfxGetAppName function in a Fortran routine, but it doesn't appear anywhere in the kernel32.f90 include file.

0 Kudos
32 Replies
SunMesa
Beginner
998 Views

FortranFan, thanks for your detailed reply, I'm still digesting it.

Bottom-line: my suggestion remains for you to use the same name DLL for your different combinations (similar to how Microsoft does with kernel32.dll, etc.) but instead fill the string table suitably based on Visual Studio macros (_DEBUG, etc.).

If I revert to uniformly-named DLLs then the problem is effectively already solved, i.e., the DLL_Info routine in #7 above will handle that circumstance already.

While that is clearly common practice, I personally think it can easily lead to confusion and problems. As we speak, I'm unable to launch a formerly healthy Fortran application following migration to Composer 19.1 and VS 2017, because (you guessed it) there is confusion/ambiguity about which version of the math library libmmdd.dll to use: "The procedure entry point pow2o3f could not be located in the dynamic link library libmmdd.dll." I then discovered that this files exists in fully six different locations on my hard drive, all versions being unique, and all installed by current or previous incarnations of Intel Fortran. 

In any case, since I've taken it this far, as well as out of academic curiosity, I am continuing to pursue the LOC and GetModuleHandleEx approach, as it certainly looks (on paper) like it will do exactly what I need, in very compact form. It seems like I'm so-o-o close to making it work, but for some (by all appearance) fairly trivial issues with type declarations and syntax in comunicating with the Windows API routines.

Since I've long since abandoned the idea of using an MFC routine for this (per the wise advice of Ian in #9 above), the title for this thread is clearly inappropriate. I will try to reformulate this in a new thread that addresses my current endeavor in a more focused way.

Thanks again for your input. I admit I'm looking for a quick and dirty solution, as I'm not in a position now to invest the time to become genuinely expert in Fortran interoperability. However, I do appreciate the Metcalf et al reference, and I hope to procure a copy soon.

0 Kudos
SunMesa
Beginner
998 Views

Jim,

Thanks also for your reply. I'm stubbornly clinging to the possibility that this can be done with no filename input at all, but should that prove incorrect I'll probably just stick with my original rudimentary method using a single dLL name, and just be very careful about how I organize/store/distribute them.

0 Kudos
IanH
Honored Contributor II
998 Views

DLLs are told of their handle when they are first loaded into a process (plus some other events), via arguments passed to the "DllMain" entry point.  You can save this handle and then reference it later on.  Note there are many restrictions around what can be done in a DllMain procedure (in part to avoid circular DllMain references between DLLs); and language runtimes may also provide and require their own DllMain procedure (you may need to chain a call to the language runtime, not sure what ifort's requirements are here) - given this I'd prefer the GetModuleHandleEx approach below.

MODULE dll1
  USE KERNEL32
  IMPLICIT NONE
  PRIVATE
  
  PUBLIC :: Sub1
  
  INTEGER(HANDLE) :: my_hinst = 0_HANDLE
CONTAINS
  FUNCTION DllMain(hinstDLL, fdwReason, lpvReserved) BIND(C, NAME='DllMain')
    !DEC$ ATTRIBUTES STDCALL :: DLLMain
    !DEC$ ATTRIBUTES DLLEXPORT :: DllMain
    INTEGER(HANDLE), INTENT(IN), VALUE :: hinstDLL
    INTEGER(DWORD), INTENT(IN), VALUE :: fdwReason
    INTEGER(LPVOID), INTENT(IN), VALUE :: lpvReserved
    INTEGER(BOOL) :: DllMain
    
    INTEGER(DWORD), PARAMETER :: DLL_PROCESS_ATTACH = 1_DWORD
    
    IF (fdwReason == DLL_PROCESS_ATTACH) THEN
      my_hinst = hinstDLL
    END IF
    DllMain = TRUE
  END FUNCTION DllMain
  
  SUBROUTINE Sub1
    !DEC$ ATTRIBUTES DLLEXPORT :: Sub1
    PRINT "(Z16.16)", my_hinst
  END SUBROUTINE Sub1
END MODULE dll1

As described above, you can use GetModuleHandleEx to determine the handle of a DLL or EXE that is responsible for a particular procedure.  Below the call is used from within a DLL, if you are calling from outside of the DLL you may need to be mindful that references to procedures that are imported from a DLL can be through a jump table that I guess would belong to the address space of the EXE (this is what can happen if the compiler (rather than the linker) is not told that the procedure is a DLLIMPORTed symbol).

MODULE dll2
  IMPLICIT NONE
  PRIVATE
  
  PUBLIC :: Sub2
CONTAINS
  SUBROUTINE Sub2
    USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_FUNLOC
    USE KERNEL32
    !DEC$ ATTRIBUTES DLLEXPORT :: Sub2
    
    INTERFACE
      FUNCTION GetModuleHandleEx_proc(dwFlags,lpModuleName,phModule)
        USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_FUNPTR
        USE IFWINTY
        IMPLICIT NONE
        !DEC$ ATTRIBUTES STDCALL, REFERENCE, DECORATE, ALIAS:"GetModuleHandleExA" :: GetModuleHandleEx_proc
        INTEGER(BOOL) :: GetModuleHandleEx_proc
        INTEGER(DWORD), intent(IN) :: dwFlags
        !DEC$ ATTRIBUTES VALUE :: dwFlags
        TYPE(C_FUNPTR), intent(IN) :: lpModuleName
        !DEC$ ATTRIBUTES VALUE :: lpModuleName
        INTEGER(HANDLE), intent(OUT) :: phModule
      END FUNCTION GetModuleHandleEx_proc
    END INTERFACE
    
    INTEGER(HANDLE) :: my_handle
    INTEGER(BOOL) :: brc
    
    brc = GetModuleHandleEx_proc(  &
        IOR(  &
          GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,  &
          GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT ),  &
        C_FUNLOC(Sub2),  &
        my_handle )
    IF (brc == 0) THEN
      PRINT *, "GetModuleHandleEx_proc failed"
      RETURN
    END IF
    
    PRINT "(Z16.16)", my_handle
  END SUBROUTINE Sub2
END MODULE dll2

Rather than the EXE getting the handle to the DLL and then loading string resources from the DLL's image, I would instead put a procedure inside the DLL for the EXE to call.  That way the EXE can be agnostic to how the string information is stored - it just needs to call a procedure.  The implementation within the DLL can store the string data in a resource, or large character constant, or separate file - and that implementation can be changed without the EXE needing to care.  This also makes it easy to move the string getting code from the DLL to the EXE (I've seen quite a few cases where code is stuck into DLLs without there being any real reason or benefit from doing so).

Driver for the above.

PROGRAM main
  USE dll1
  USE dll2
  IMPLICIT NONE
  CALL sub1
  CALL sub2
END PROGRAM main

LoadStringA is an API that has been with Windows since the very early days, and has some flaws associated with that history.  Frameworks that need to be robust avoid it - instead doing things like accessing the wide string form of the resource (perhaps using its less flawed cousin LoadStringW or [Find|Load|Lock]Resource, and then manually convert the string from wide to ansi form).  Less of an issue when you have complete control over the string and the loading code (you can make sure the buffer for the string is large enough).

0 Kudos
Steve_Lionel
Honored Contributor III
998 Views

Intel Fortran places no restrictions on users providing their own DllMain. Note that DllMain gets called both on load and unload, so make sure your code accounts for that (as Ian's example does.)

0 Kudos
SunMesa
Beginner
998 Views

Ian,

Thanks so much for this detailed example code. I'm unable to attend to it today, but I'm going to import it as literally as possible into my code at my nearest opportunity. I agree completely that the most logical place to put the handle-grabbing code is in the DLL rather than the driver application.

One of my main difficulties here has been trying to decipher the various data types in the Windows APIs and their Fortran 'equivalents'. Despite having waded through (as best I could) the Calling Windows API Routines page, it's really still quite opaque to me (although this exercise has definitely moved my education forward significantly). Mind you, I've only barely ever dabbled in C/C++ programming!

I'll report on my results from your suggestion as soon as possible, best regards-

0 Kudos
SunMesa
Beginner
998 Views

Ian, all,

My apologies for the delay in trying to implement this.

I attempted to use Ian's GetModuleHandleEx technique as follows:

I created a Fortran DLL project called "DLLInfoTest" in VS 2017 which consisted of a single integer function DLL_Func that is virtually a cut-and-paste of the handle-grabbing code that Ian provided above:

!=============================================================================================

      Integer Function DLL_Func(my_handle)
      USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_FUNLOC
      USE KERNEL32
      IMPLICIT NONE
      !DEC$ ATTRIBUTES DLLEXPORT :: DLL_Func
!     =============================================================================================
      INTERFACE
        FUNCTION GetModuleHandleEx_proc(dwFlags,lpModuleName,phModule)
        USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_FUNPTR
        USE IFWINTY
        IMPLICIT NONE
        !DEC$ ATTRIBUTES STDCALL, REFERENCE, DECORATE, ALIAS:"GetModuleHandleExA" :: GetModuleHandleEx_proc
        INTEGER(BOOL) :: GetModuleHandleEx_proc
        INTEGER(DWORD), intent(IN) :: dwFlags
        !DEC$ ATTRIBUTES VALUE :: dwFlags
        TYPE(C_FUNPTR), intent(IN) :: lpModuleName
        !DEC$ ATTRIBUTES VALUE :: lpModuleName
        INTEGER(HANDLE), intent(OUT) :: phModule
        END FUNCTION GetModuleHandleEx_proc
      END INTERFACE
!     =============================================================================================
      INTEGER(HANDLE), Intent(OUT) :: my_handle
      INTEGER(BOOL) :: brc

      brc = GetModuleHandleEx_proc(  &
      IOR(  &
      GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,  &
      GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT ),  &
      C_FUNLOC(DLL_Func),  &
      my_handle )
      IF (brc == 0) THEN
        PRINT *, "GetModuleHandleEx_proc failed"
        RETURN
      END IF
      PRINT "(Z16.16)", my_handle
      DLL_Func = brc

      END FUNCTION DLL_Func

!=============================================================================================

 I added a resource file to the project called DLLInfoTest.rc which contained the following stringtable:

STRINGTABLE {1 "This is the stringtable from DLLInfoTest.dll"}

The DLL built successfully, resulting in DLLInfoTest.dll and DLLInfoTest.lib. I then created a new Fortran console application project called "Driver", which consisted of the following:

!====================================================================================================

      Program Driver
!     Tests the DLL info extraction routine
      USE IFWIN, ONLY: LoadString,GetModuleHandle
      USE IFWINTY
      IMPLICIT NONE
      INTERFACE
        Integer Function DLL_Func(my_handle)
        USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_FUNLOC
        USE KERNEL32
        IMPLICIT NONE
        INTEGER(HANDLE), Intent(OUT) :: my_handle
        INTEGER(BOOL) :: brc
        END FUNCTION DLL_Func
      END INTERFACE
      Integer :: i,j,k
      Integer(HANDLE) :: my_handle
      Character(44) :: DLL_Info

!     EXTRACT THE STRINGTABLE FROM DLLInfoTest.dll
!     METHOD 1:
      i = DLL_Func(my_handle)
      j = LoadString(my_handle,1,DLL_Info,LEN(DLL_Info)+1)
      Write(*,'(4a,/)')'Stringtable content is: ','"',TRIM(DLL_Info),'"'
!     METHOD 2:
      k = LoadString(GetModuleHandle('DLLInfoTest.dll'),1,DLL_Info,LEN(DLL_Info)+1)
      Write(*,'(4a,/)')'Stringtable content is: ','"',TRIM(DLL_Info),'"'

      END PROGRAM Driver

!====================================================================================================

I added the file DLLInfoTest.lib as a resource to the project, and successfully built the application. As you can see, Driver.exe is using two different methods to try to extract the stringtable from the DLL. Method 1 is my translation of Ian's method, and Method 2 is my original method that requires the specific name of the DLL as input.

When I run the Driver application, Method 1 returns the failure alert that "GetModuleHandleEx_proc failed" and ultimately produces an empty character string. Method 2 works as it should, returning: 'Stringtable content is: "This is the stringtable from DLLInfoTest.dll"'.

I tried adding a new variable in DLL_Func:

      TYPE(C_FUNPTR) :: my_loc

and used it in an intermediate step to get the location of DLL_Func prior to the call to GetModuleHandleEx:

      my_loc = C_FUNLOC(DLL_Func)

and then modified the call to GetModuleHandleEx to be: 

      brc = GetModuleHandleEx_proc(  &
      IOR(  &
      GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,  &
      GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT ),  &
      my_loc,  &
      my_handle )

This didn't make any difference in the result, but by stepping through the code in the debugger, I could see that my_loc (shown as MY_LOC%PTR in the debugger) went from 0 to 2357624 after the call to C_FUNLOC, so something was returned as a location, but apparently not what I needed.

Can anyone see where I went astray? I know there's a way to extract the details of the failed call to GetModuleHandleEx using GetLastError and FormatMessage, but frankly it looks way over my head. Thanks for any info/guidance!

0 Kudos
FortranFan
Honored Contributor II
998 Views

@SunMesa,

Given your difficulties with consuming Windows APIs in your Fortran code, why not simply have the "string" data in your current string table resource with your DLL as named constants (CHARACTER intrinsic type with the PARAMETER attribute) instead and then have an exported Fortran subroutine in your DLL that your caller Fortran executable can call to retrieve the string values?

Note you're going by suggestions by IanH in Quote #9 and #24 where the premise appears to be as mentioned in Quote #9 "the function to query properties of the DLL would be within the DLL from an encapsulation point of view".  So if you're going to do that, you can ask yourself why you need the stringtable resource at all, why not go with string values as named constants that you can retrieve using Fortran subroutines (or functions)?

   

0 Kudos
IanH
Honored Contributor II
998 Views

The subroutine has been changed to a function.

Within the scoping unit of the function DLL_Func, the name DLL_Func is a reference to the function result, not the function itself.  That function result will more than likely just be a few bytes of memory on the program's stack, which has nothing to do with where the code for the function has been laid out in memory by the operating system loader when the DLL was loaded.   The memory address of the few bytes on the stack is not associated with the code of any DLL or EXE, hence the call fails.

You should be able to use a RESULT clause in the function statement to specify a different name for the function result - in which case inside the name of the function continues to refer to the function inside the scoping unit (as would be required for recursive calls, for example).  But this appears to confuse the compiler.  (The compiler should probably also complain about C_FUNLOC being given an argument that is not a function, but passing a variable is still principally a programming error.)

Returning information via function arguments is typically considered poor style for functions written in Fortran (versus functions written in C or similar called from Fortran).  I'd very much suggest just reverting the procedure to be a subroutine.

You have a buffer overrun in your main program when you call LoadString - you are adding one to the actual length of the available buffer. 

Is there a particular reason you are using an external procedure?

0 Kudos
SunMesa
Beginner
998 Views

@FortranFan

...why not go with string values as named constants that you can retrieve using Fortran subroutines (or functions)?

The resource file containing the stringtable associated with the DLL is modified 'on the fly' as a pre-build event in VS 2017, so that the the precise build date/time becomes part of the stringtable. The pre-build event also includes a real-time query to the builder regarding the level of the DLL modification, so as to correctly increment the version number. I can't see how to incorporate those as named constants in the source code.

0 Kudos
SunMesa
Beginner
998 Views

@IanH

WHOA! IT WORKS!

The subroutine has been changed to a function.

This was clearly the problem, as you explained. For completeness sake, here is the integer function DLL_Func above converted to the subroutine DLL_Sub:

!=============================================================================================

      Subroutine DLL_Sub(my_handle)
      USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_FUNLOC,C_FUNPTR
      USE KERNEL32
      IMPLICIT NONE
      !DEC$ ATTRIBUTES DLLEXPORT :: DLL_Sub
!     =============================================================================================
      INTERFACE
        FUNCTION GetModuleHandleEx_proc(dwFlags,lpModuleName,phModule)
        USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_FUNPTR
        USE IFWINTY
        IMPLICIT NONE
        !DEC$ ATTRIBUTES STDCALL, REFERENCE, DECORATE, ALIAS:"GetModuleHandleExA" :: GetModuleHandleEx_proc
        INTEGER(BOOL) :: GetModuleHandleEx_proc
        INTEGER(DWORD), intent(IN) :: dwFlags
        !DEC$ ATTRIBUTES VALUE :: dwFlags
        TYPE(C_FUNPTR), intent(IN) :: lpModuleName
        !DEC$ ATTRIBUTES VALUE :: lpModuleName
        INTEGER(HANDLE), intent(OUT) :: phModule
        END FUNCTION GetModuleHandleEx_proc
      END INTERFACE
!     =============================================================================================
      INTEGER(HANDLE), Intent(OUT) :: my_handle
      INTEGER(BOOL) :: brc
      TYPE(C_FUNPTR) :: my_loc

      my_loc = C_FUNLOC(DLL_Sub)
      brc = GetModuleHandleEx_proc(  &
      IOR(  &
      GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,  &
      GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT ),  &
      my_loc,  &
      my_handle )
      IF (brc == 0) THEN
        PRINT *, "GetModuleHandleEx_proc failed"
        RETURN
      END IF
      PRINT "(Z16.16)", my_handle

      END Subroutine DLL_Sub

!=============================================================================================

Here is the converted Driver:

!====================================================================================================

      Program Driver
!     Tests the DLL info extraction routine
      USE IFWIN, ONLY: LoadString,GetModuleHandle
      USE IFWINTY
      IMPLICIT NONE
      INTERFACE
        Subroutine DLL_Sub(my_handle)
        USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_FUNLOC
        USE KERNEL32
        IMPLICIT NONE
        INTEGER(HANDLE), Intent(OUT) :: my_handle
        INTEGER(BOOL) :: brc
        END Subroutine DLL_Sub
      END INTERFACE
      Integer :: j,k
      Integer(HANDLE) :: my_handle
      Character(44) :: DLL_Info

!     EXTRACT THE STRINGTABLE FROM DLLInfoTest.dll
!     METHOD 1:
      Call DLL_Sub(my_handle)
      j = LoadString(my_handle,1,DLL_Info,LEN(DLL_Info)+1)
      Write(*,'(4a,/)')'Stringtable content is: ','"',TRIM(DLL_Info),'"'
!     METHOD 2:
      k = LoadString(GetModuleHandle('DLLInfoTest.dll'),1,DLL_Info,LEN(DLL_Info)+1)
      Write(*,'(4a,/)')'Stringtable content is: ','"',TRIM(DLL_Info),'"'

      END PROGRAM Driver

!====================================================================================================

The GetModuleHandleEx method now works perfectly! Both Method 1 and Method 2 both produce the same correct result, with Method 1 requiring no actual DLL name.

Returning information via function arguments is typically considered poor style for functions written in Fortran (versus functions written in C or similar called from Fortran).

For some reason, I got into the habit of formatting my Fortran DLLs as integer functions, with the return result (say, ierr) interpreted as an error code that can be deciphered via the BTEST function, such that if BTEST(ierr,j) is .TRUE., then error #j (and its associated text) is reported. There are no doubt cleaner ways to do this, I'm certainly open to suggestions.

Bottom line is, you solved it Ian. Thanks so much!

0 Kudos
IanH
Honored Contributor II
998 Views

Fix the buffer overflow issue!  In the two LoadString invocations...

j = LoadString(my_handle,1,DLL_Info,LEN(DLL_Info)+1)

the length of the buffer is just LEN(DLL_Info).  Don't arbitrarily add one to it.

If you tell the API that the buffer is larger than it is, then the API may merrily trample the following memory, and disaster awaits.

If you want the buffer to be longer, then make it longer in the declaration for DLL_Info.

If you want to obliterate the trailing null out of the string that the API appends because of its C heritage, then do that with an assignment statement after the API call (check its return value...)

DLL_Info = DLL_Info(:j)

but the value of j must be less than or equal to the LEN of DLL_Info.

My suggestion around encapsulation was to also stick the string retrieval code into the DLL.  You can still use a resource to store the string, but then the EXE doesn't need to care about the internals of the DLL, it just asks for a version string.

(Build systems for programs that need to operate where Windows resources aren't a thing can write out a little "Version" module, that contains a string constant with the relevant information, but if you are always going to be on Windows, then it doesn't matter.)

(The documentation for LoadStringA on the Microsoft website is incorrect - while the documentation is a near verbatim copy of LoadStringW, the two API's have a material difference - LoadStringA does not have the ability (on versions of Windows all the way up to today) to simply return the length of the complete string resource.  This is the flaw I refer to in #24.  If you want the ability for the buffer to be dynamically allocated to match the length of meaningful text in the resource file, then you cannot (efficiently) use this API (you can stick the API in a loop with an ever expanding buffer, but that's not very neat).  Given you control both the string resource and the code to load it, this isn't such a problem - perhaps make the buffer way bigger than you need - and then to be defensive consider the unlikely event you go a bit overboard with your string resource (if is just for user info it probably doesn't matter if it gets trunca

0 Kudos
SunMesa
Beginner
998 Views

j = LoadString(my_handle,1,DLL_Info,LEN(DLL_Info)+1)

...the length of the buffer is just LEN(DLL_Info).  Don't arbitrarily add one to it.

I recall just copying and pasting this form of the call way back when and never gave it a second thought (lol!). I'm sure it had something to do with the appended null character. Anyway that's fixed, and I do now strip off the null character with the assignment DLL_Info = DLL_Info(:j), where j is the return value from LoadString.

My suggestion around encapsulation was to also stick the string retrieval code into the DLL.

I've added another routine to the DLL that basically does what the Driver program above does, so that now any application that loads the DLL can just ask that routine for the DLL_Info string. Everything still works great, very tidy and compact!

As you say, the advertised capability of LoadString to return the number of characters in the string (if you set the buffer size to zero) is evidently a hoax... I tried it and it returns a value of -1! It's irritating to have to hardwire the DLL_Info string length to some large value, but I'm more after function here than elegance.

(if is just for user info it probably doesn't matter if it gets trunca

Ha!  :)

 

0 Kudos
Reply