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

Calling MFC Routines from Intel Fortran

SunMesa
초급자
5,384 조회수

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 포인트
32 응답
jimdempseyatthecove
명예로운 기여자 III
4,133 조회수

MFC use C++ calling API.

https://docs.microsoft.com/en-us/cpp/mfc/relationship-to-the-c-language-api?view=vs-2019

You could call the equivalent Windows API C functions or write C helper shell functions that call the decorated names.

Note, while you can construct the decorated name, it isn't pleasant to do.

>>AfxGetAppName

You may find what you want by examining command line argument [0], scan for last / then take what is remaining.

Jim Dempsey

0 포인트
Steve_Lionel
명예로운 기여자 III
4,133 조회수

Are you trying to get the name of the EXE currently running? You can do that with GetModuleHandle(NULL) and then GetModuleFileName.

0 포인트
SunMesa
초급자
4,133 조회수

Jim, Steve. thanks for your replies.

I'm actually not after the name of the application that's running, I'm after the name of the DLL that the current process has loaded, in a context where (potentially) that name is not known at runtime. I want to then feed that name into a call to GetModuleHandle, which will then allow extraction of a stringtable associated with the DLL that describes various attributes of the build (32 vs 64 bit, debug vs release, etc).

0 포인트
IanH
명예로운 기여자 III
4,133 조회수

You can always invoke the C++ call using a thin C wrapper.

But I don't understand how a call to AfxGetAppName (which will return a string, possibly read from resources or set programmatically, such as "My Awesome App 1.0") helps you find the name of a DLL.

Are you writing a Fortran DLL that is called by an MFC program?  Are you writing a Fortran main program that calls a DLL using MFC?

0 포인트
Steve_Lionel
명예로운 기여자 III
4,133 조회수

It's rather odd to not know the name of the DLL - this implies that the application loaded it dynamically. Is there a fixed set of possible names? If so, you can use GetModuleHandle with each of the names until it succeeds. Maybe if you explained the application a bit more we can make other suggestions. I agree with Ian that AfxGetAppName is not likely to be of additional help.

0 포인트
SunMesa
초급자
4,133 조회수

After closer examination of the documentation, I'm also now doubting whether AfxGetAppName will return the name of the loaded DLL (unless it happens to coincide with the name of the executable).

Basically, I have a Fortran driver application that loads a single Fortran DLL. For reasons too tedious to relate, the DLL has a variety of different incarnations, distinguished by 32-bit vs 64-bit, debug vs release, version number of particular constituent functions, build date and time, etc. These various attributes are put in a resource file that includes a stringtable describing the particulars of a given build of the DLL.

Earlier on, when the list of attributes/distinctions was smaller, I simply gave all the DLLs the same name (e.g., MyDLL.dll), and in the application that loads the DLL, I would extract the stringtable as follows:

      Integer Function DLL_Info(MyDLL_Info)
!     Extracts attrubutes of MyDLL.dll.
      USE IFWIN
      IMPLICIT NONE
      Character(*), INTENT(Out) :: MyDLL_Info
      DLL_Info = LoadString(GetModuleHandle('MyDLL.dll'),1,MyDLL_Info,LEN(DLL_Info)+1)
      End Function DLL_Info
 

The number of DLL incarnations is now such that, to avoid confusion on my end, I thought I would start adding some of the characteristics into the DLL name (e.g., MyDLL_x86_Debug.dll, MyDLL_x64_Release.dll, etc) , so that I could at least distinguish them by broad category 'on sight', but not in such detail that the entire attributes stringtable is incorporated in the DLL name.

Unfortunately, in that circumstance the code above won't work, unless, as Steve suggests, I loop through all the name permutations until I hit the right one. And then, if a new tag or property gets incorporated into the naming scheme, I would have to modify/rebuild the DLL_Info code to accommodate it.

It would be great if GetModuleHandle could handle wild cards, i.e., accept an argument like "MyDLL_*", but it definitely gags on that. That got me started looking for alternatives, and I stumbled upon AfxGetAppName as a possibility. Notwithstanding that it probably isn't what I'm looking for, I'm still curious to know if it's possible in general to access these MFC routines from Intel Fortran.

As an aside, I should note that, since migrating to Visual Studio 2017 and Composer 19.1, I must include the ".dll" extension on the argument passed to GetModuleHandle, even though the documentation indicates that .dll is assumed if the extension is absent. In fact, if the extension is absent, the function will return zero (i.e., it failed to find the file). With the VS 2013 Shell and Composer 17.0, the function behaved as documented (no .dll extension required).

0 포인트
Steve_Lionel
명예로운 기여자 III
4,133 조회수

You need to pass a NUL-terminated string to GetModuleHandle. Without that, it's unpredictable how it will be handled.

You cannot call MFC routines directly from Fortran as they require "magic" that Fortran doesn't know how to provide. As Ian notes, a simple C-compatible (can be in C++) shim routine can be used if needed.

Can you have the code that loads the DLL save the name somewhere (or even better, save the handle)?

0 포인트
IanH
명예로운 기여자 III
4,133 조회수

The MFC routines are most certainly not what you want, unless you are working within a larger MFC application or component.  If called from outside that context, there will be various global objects that will not have defined appropriately, and the calls will fail, probably in a rather brutal way.  For example - AfxGetAppName is a macro that compiles to a reference to a particular component of a global MFC CWinApp object - that object won't exist (let alone the component having been meaningfully defined) if the code behind the macro is invoked cold.

Not that knowing the application name would help you get the module handle of a specific DLL.

There are many examples of Fortran code calling C code using BIND(C), here and elsewhere.  C++ code generates functions with C compatible linkage if the C++ function is declared with extern "C".  So to call C++ code, all you need to do is write in C++ a small extern "C" function that the Fortran can call, and that small extern "C" function then invokes whatever C++ code you want to invoke.  Details depend on the information that needs to be passed from Fortran to C++, and back.

Is the example DLL_Info function above within the DLL or is it within the EXE?  If it is within the DLL, then DLL's are informed of their module handle when they are first loaded, via DllMain.  You can also query the module handle if you know the address of something within the module (a procedure within the DLL can invoke LOC or C_FUNLOC on itself to figure out its address), using GetModuleHandleEx and GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS (I have vague recollections that the module handle for a DLL is actually just the base address of the DLL in the current process, but that might have been for specific Windows versions and/or be faulty recollection). 

If it is within the EXE, how are you loading this specific DLL? 

(Ideally the function to query properties of the DLL would be within the DLL from an encapsulation point of view.)

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

SunMesa wrote:

After closer examination of the documentation, I'm also now doubting whether AfxGetAppName will return the name of the loaded DLL (unless ..

Basically, I have a Fortran driver application that loads a single Fortran DLL. For reasons too tedious to relate, the DLL has a variety of different incarnations, distinguished by 32-bit vs 64-bit, debug vs release, version number of particular constituent functions, build date and time, etc. These various attributes are put in a resource file that includes a stringtable describing the particulars of a given build of the DLL.

..

@SunMesa,

I suggest you use LoadLibrary and LoadStringA for this.  Intel Fortran offers interfaces for the same in its KERNEL32 and IFWIN modules.  The use of LoadLibrary will directly give you an instance handle to your Fortran DLL and should that fail, you will have control to issue diagnostics and do the error processing as desired.  The instance handle can then be in the LoadStringA Windows API function to get your STRINGTABLE.  It's fairly straightforward.

 

0 포인트
SunMesa
초급자
4,133 조회수

All,

Apologies for the slow response to your kind replies, I've been in meetings (remote ones, of course) most of the time.

To answer Ian's question above, the DLL_Info routine above is in the application (let's call it MyDLL_Driver) that loads one of the various forms of MyDLL.dll. There's no reason it couldn't be in the DLL itself and called from MyDLL_Driver, and indeed that does seem more logical, but it's not clear to me (very little of this is) how that would make this work.

I examined (as best I could) the LoadLibrary function, it still seems like it needs a specific name for the DLL in order to get the handle.

I gather that when the DLL is loaded (by executing MyDLL_Driver), a handle is generated for it, regardless of its name. Is it possible to simply store this value, so that MyDLL_Driver can retrieve it with a command equivalent of "Give me the handle of the DLL that loaded with this process" (knowing there was only one) and thereby obtain the same value that would otherwise be obtained with GetModuleHandle(MyDLL_tag1_tag2_etc.dll')?

Thanks again for all the feedback. I'm clearly out of my depth with these Windows APIs.

 

 

0 포인트
Steve_Lionel
명예로운 기여자 III
4,133 조회수

Yes - as I suggested above, the handle returned by LoadLibrary can be saved and then used as if it came from GetModuleHandle. No, LoadLibrary doesn't take wildcards. Do make sure you NUL-terminate any strings passed to Windows API routines.

0 포인트
SunMesa
초급자
4,133 조회수

Steve,

This is the part that seems to be going over my head. The syntax for LoadLibrary indicates that it needs the library module (DLL) filename as input, which is the step I'm trying to bypass. Moreover, in my generic example of the application MyDLL_Driver above, the DLL is loaded implicitly upon execution of the driver (by being built with a .lib file that references the DLL), whereas the LoadLibrary function seems to perform an explicit loading of the DLL after the fact. Can I call LoadLibrary if the DLL is already loaded?

0 포인트
Steve_Lionel
명예로운 기여자 III
4,133 조회수

Yes, you can call LoadLibrary if the DLL is already loaded, in which case you will get the handle of the already-loaded DLL. But I don't think you want to just start loading various DLLs - instead you want to use GetModuleHandle with the various names (if that's the approach you're taking.)

 

0 포인트
SunMesa
초급자
4,133 조회수

Yes, you can call LoadLibrary if the DLL is already loaded, in which case you will get the handle of the already-loaded DLL.

But LoadLibrary evidently needs the specific name of the DLL as an input argument... and if so, I might as well just call GetModuleHandle directly with that name, as I already do in the DLL_Info routine above. I'm trying to get to where I can call a function or invoke a command equivalent to: "I know there is a single, unique DLL that loaded with this process... I don't know or care what it's name is, just give me its handle so I can access its stringtable." Is this even possible? Thanks for your patience-

0 포인트
Steve_Lionel
명예로운 기여자 III
4,133 조회수

I think Ian in #9 above had a great idea, one I didn't even know about before, which is to use GetModuleHandleEx to ask "which module (DLL) provides the code at this address?" Since you say that the selection of DLL happens at link time, that implies there is a fixed set of routines in the DLL, in addition to the string table. You would declare an interface block to one of them, get LOC(routine_name), and call GetModuleHandleEx. This is defined in module KERNEL32. Now you have the handle and can query the string table.

This presumes that your code doing the asking is in the same EXE (or DLL) that linked to the library.

You don't want to use LoadLibrary as that will unconditionally load each DLL you ask about. GetModuleHandle is the one that tells you whether or not a particular DLL is loaded.

0 포인트
jimdempseyatthecove
명예로운 기여자 III
4,133 조회수

If (when) you explicitly use LoadLibrary then as you load a library by path\name, add that path\name and handle to your list of loaded libraries. And as (if) the library is unloaded, remove the path\name and handle from your list of loaded libraries.

When you query for library (by name only) first examine your list of loaded libraries. If the library is loaded by you then return that handle.

*** If the library is not explicitly loaded by you .AND. you desire to locate where a library will be loaded from should you provide the name only (but not the path), then this may be of use:

#include <iostream>       // std::cout
#define _CRT_SECURE_NO_WARNINGS

int main(int argc, char* argv[])
{
    if (argc != 2)
    {
        std::cout << "Usage: %s [file]\n" << std::endl;
        return 1;
    }

    char* lpFilePart;
    char filename[MAX_PATH];

    if (!SearchPathA(NULL, argv[1], NULL, MAX_PATH, filename, &lpFilePart))
    {
        std::cout << argv[1] << " not found\n" << std::endl;
        return 2;
    }
    std::cout << "The path is " << filename << std::endl;
    return 0;
}

The above is a C++ test program. In your Fortran program, you can USE KERNEL32 which declares the INTERFACE to SearchPath:

INTERFACE 
FUNCTION SearchPath( &
        lpPath, &
        lpFileName, &
        lpExtension, &
        nBufferLength, &
        lpBuffer, &
        lpFilePart)
import
  integer(DWORD) :: SearchPath ! DWORD
    !DEC$ ATTRIBUTES DEFAULT, STDCALL, DECORATE, ALIAS:'SearchPathA' :: SearchPath
!DEC$ ATTRIBUTES REFERENCE, IGNORE_LOC, ALLOW_NULL :: lpPath
  character*(*) lpPath ! LPCSTR lpPath
!DEC$ ATTRIBUTES REFERENCE, IGNORE_LOC, ALLOW_NULL :: lpFileName
  character*(*) lpFileName ! LPCSTR lpFileName
!DEC$ ATTRIBUTES REFERENCE, IGNORE_LOC, ALLOW_NULL :: lpExtension
  character*(*) lpExtension ! LPCSTR lpExtension
  integer(DWORD) nBufferLength ! DWORD nBufferLength
!DEC$ ATTRIBUTES REFERENCE, IGNORE_LOC, ALLOW_NULL :: lpBuffer
  character*(*) lpBuffer ! LPSTR lpBuffer
!DEC$ ATTRIBUTES REFERENCE, IGNORE_LOC :: lpFilePart
  integer(LPSTR) lpFilePart ! LPSTR* lpFilePart
 END FUNCTION
END INTERFACE

Jim Dempsey

0 포인트
SunMesa
초급자
4,133 조회수

I was somewhat baffled at first by Ian's LOC and GetModuleHandleEx scheme, but after further examination it actually looks quite promising. But I'm so helpless with calling Windows functions from Fortran, I can't seem to get the syntax and/or type declarations right.

I inserted an explicit interface for a function MyFunc in MyDLL.dll as Steve suggested. From Ian's recollection that the handle for the loaded DLL is just its address, I inserted USE IFWINTY into the DLL_Info routine and declared

      Integer(LPHANDLE) :: MyFunc_Addr       !Address for MyFunc
      Integer(LPHANDLE) :: MyDLL_Handle    !Handle for DLL containing MyFunc

then added

      MyFunc_Addr = LOC(MyFunc)

and

      MyDLL_Handle = GetModuleHandleEx(                                                 &
       GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS .OR.           &
       GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,   &
       MyFunc_Addr, MyDLL_Handle)
 

The compiler gags on this, with the following error:

"Error #6633: The type of the actual argument differs from the type of the dummy argument. [MyFunc_Addr]"

My method of passing the flags GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS and GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT is also apparently wrong, since I get a warning that a LOGICAL data type is required in this context.

Any suggestions greatly appreciated!

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

SunMesa wrote:

.. But I'm so helpless with calling Windows functions from Fortran, I can't seem to get the syntax and/or type declarations right. ..

Any suggestions greatly appreciated!

@SunMesa,

My first suggestion will be to go through a book such as this one, especially chapter 12 on facilities in Fortran toward interoperability with C.

Secondly, you can make use of a Fortran module such as this one that includes interfaces to the couple of Windows API functions I suggested earlier in Quote #10.

module IWinAPI_m

   use, intrinsic :: iso_c_binding, only : c_char, c_int, c_intptr_t, c_funptr

   implicit none

   !.. Public by default
   public

   !.. Mnemonics for types in Windows API functions
   integer(c_int), parameter :: HMODULE = c_intptr_t  !.. A handle to a module; base address in memory
   integer(HMODULE), parameter :: NULL_HANDLE = int( 0, kind=kind(NULL_HANDLE) )

   interface

      function LoadLibrary(lpLibName) bind(C, name='LoadLibraryA') result(RetVal)
      !DIR$ ATTRIBUTES STDCALL :: LoadLibrary
      ! https://msdn.microsoft.com/en-us/library/windows/desktop/ms684175(v=vs.85).aspx
      ! HMODULE WINAPI LoadLibrary(_In_ LPCTSTR lpFileName );

         import :: c_char, HMODULE

         !.. Argument list
         character(kind=c_char, len=1), intent(in) :: lpLibName(*)
         !.. Function result
         integer(HMODULE) :: RetVal

      end function LoadLibrary

      function LoadStringA(hInstance, uID, lpBuffer, cchBufferMax) bind(C, name='LoadStringA') result(RetVal)
      !DIR$ ATTRIBUTES STDCALL :: LoadStringA
      ! https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-loadstringa
      ! int LoadStringA(
      !   HINSTANCE hInstance,
      !   UINT      uID,
      !   LPSTR     lpBuffer,
      !   int       cchBufferMax
      ! );

        import :: HMODULE, c_int, c_char

         !.. Argument list
         integer(HMODULE), intent(in), value :: hInstance
         integer(c_int), intent(in), value   :: uID
         character(kind=c_char,len=1)        :: lpBuffer(*)
         integer(c_int), intent(in), value   :: cchBufferMax
         !.. Function result
         integer(c_int) :: RetVal

      end function LoadStringA

   end interface

end module IWinAPI_m

Then say you have a DLL with a string table as follows but where the Caption might vary depending on the target platform (Win32/x64) and configuration (Debug/Release) of the build:

strtbl.PNG 

Then in your calling application, you can have code as follows to extract the string table:

   use iso_c_binding, only : c_int, c_char    
   use IWinAPI_m, only : LoadLibrary, LoadStringA, HMODULE, NULL_HANDLE
   
   implicit none

   ! Local variables
   integer(HMODULE) :: DllHandle
   character(kind=c_char, len=256) :: DllString !<-- String of suitable length
   integer(c_int) :: irc

   ! Load the DLL
   DllHandle = LoadLibrary( c_char_"foo.dll" )
   if ( DllHandle == NULL_HANDLE ) then
      print *, "Failed to load the DLL"
      stop
   end if
   ! Load DLL string
   irc = LoadStringA( DllHandle, uID=0, lpBuffer=DllString, cchBufferMax=len(DllString)-1 )
   if ( irc > 0 ) then
      print *, "DLL String: ", DllString(1:irc)
   else
      print *, "Failed to load DLL String."
   end if
    
end

Upon execution with a sample DLL with different target platforms, the output for a debug version of Win32 target

 DLL String:  DEBUG_WIN32

and for a Release build of x64 target:

 DLL String:  RELEASE_X64

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.).  Your calling application can then parse things out to inform the user re: the attributes of the DLL.  You can have as many IDs and their Values and Captions as needed in your string table and you can use the values in LoadStringA function invocations to get the string information. 

Here's some info on LoadLibrary you might find useful: https://stackoverflow.com/questions/3497516/does-loadlibrary-create-distinct-instances

0 포인트
jimdempseyatthecove
명예로운 기여자 III
4,140 조회수

FortranFan's suggestion is good for getting an identifier from a string table that you add to your DLL. You could also obtain a similar identifier of your design by yourGetVersionInfo subroutine to your DLL. Both require some maintenance on your part. (updating version)

My suggestion is different as it will return the full path and name of the DLL that was loaded either automatically or via LoadLibrary in your library loader routine.

As to which method (or possibly both) suits your needs is for you to decide.

Jim Dempsey

0 포인트
jimdempseyatthecove
명예로운 기여자 III
4,117 조회수

BTW You can use FindResourcA to locate a resource handle by name.
https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-findresourcea

This can be used if you cannot peg your version information at a specific resource number.

And look at: https://docs.microsoft.com/en-us/windows/win32/menurc/adding-deleting-and-replacing-resources

Jim Dempsey

0 포인트
응답