- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
We have a lot of legacy Fortran code that we would like to be able to continue using but we'd like to update it somewhat. For example, we have a number of Fortran executables that send their output to the console, either with "print*" or with "write(*,10)". We would like to turn the executable into a DLL and route the console output to a callback. I've had no trouble, in my proof-of-concept, turning an executable into a DLL, and no trouble using a callback, but I have no idea where to begin to reroute console output to the callback.
The point of this is that I need to turn
PRINT*,OutString
into
CALL WriteToCaller( OutString )
That, of course, is the simple case. I could do that with global search/replace. It gets much more difficult with a "write" statement with a format statement and a list of variables:
WRITE(*,10) (var1(var2(j)),j=1,num)
So, it's impractical to replace all of our console writes. It would be much simpler if we could redirect them somehow. Is this possible? If so, can someone please describe to me how to do it or point me to documentation that I could follow?
One important caveat: the application can take a long time to run, so I'd like the output to be somewhat real-time. So, for example, capturing all console output and presenting it at the end is not sufficient.
Thanks!
Brad.
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Look at the MSDN article regarding DllMain. Also this.
When you detect the process attaching, you will have to use different Windows API to obtain the handle to the "Console", which may be abstract in a GUI application. Using this handle you can redirect output to this handle to code you provide.
Here is something I used when trying to debug asserts from a Fortran .dll called from a C# main.
! enable this section to get console ouput for debugging of MIC offloads function DllMain (hInstDLL, fdwReason, lpReserved) !DEC$ ATTRIBUTES STDCALL, DECORATE, DLLEXPORT, ALIAS:"DllMain" :: DllMain USE IFWINTY USE Kernel32 IMPLICIT NONE integer(BOOL) :: DllMain integer(HANDLE), intent(IN) :: hinstDLL integer(DWORD), intent(IN) :: fdwReason integer(LPVOID), intent(IN) :: lpReserved character(500) :: eVar character(500) :: fName logical :: exists logical, save :: FirstTime = .true. integer(BOOL) :: b integer(HANDLE) :: hConFile interface INTEGER FUNCTION internal_crames_try_offload_mic(iMIC) integer :: iMIC end FUNCTION internal_crames_try_offload_mic end interface select case(fdwReason) case (DLL_PROCESS_DETACH) ! ... DllMain = TRUE case (DLL_PROCESS_ATTACH) !DIR$ IF(.FALSE.) b = AllocConsole() !DIR$ ELSE hConFile = CreateFile("C:\\DebugOutput\\ConsoleOutput.txt"C, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0) b=SetStdHandle(STD_OUTPUT_HANDLE, hConFile) !DIR$ ENDIF write(*,*) "***************************" CALL GET_ENVIRONMENT_VARIABLE("MIC_LIBRARY_PATH",eVar) write(*,*) "MIC_LIBRARY_PATH=",TRIM(eVar) CALL GET_ENVIRONMENT_VARIABLE("MIC_LD_LIBRARY_PATH",eVar) write(*,*) "MIC_LD_LIBRARY_PATH=",TRIM(eVar) write(*,*) "***************************" case (DLL_THREAD_ATTACH) ! ... case (DLL_THREAD_DETACH) ! ... case default ! ... end select DllMain = TRUE write(*,*) "DllMain(",hInstDLL, ",", fdwReason, ",", lpReserved,")" end function DllMain
Maybe you can mung this up for your needs.
Jim Dempsey
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
That might work for the console, but not for file units, and it doesn't really help with getting a procedure to be called for I/O.
What I was thinking was using OPEN or defining an environment variable to redirect the Fortran I/O unit to a named pipe. Then in a separate thread, have a routine that reads from the pipe and calls the desired routine. You'd probably need one for each unit.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
jimdempseyatthecove wrote:
I didn't get much from those, unfortunately.
jimdempseyatthecove wrote:
Here is something I used when trying to debug asserts from a Fortran .dll called from a C# main.
That code looks very promising. The part I'm struggling with now is creating a named pipe rather than a file. Any ideas or pointers?
Thanks!
Brad.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Steve Lionel (Intel) wrote:
What I was thinking was using OPEN or defining an environment variable to redirect the Fortran I/O unit to a named pipe. Then in a separate thread, have a routine that reads from the pipe and calls the desired routine. You'd probably need one for each unit.
Apparently our comments crossed in the ether. :-)
As you can see from my most recent post, I was thinking, too, that I need to "redirect the Fortran I/O unit to a named pipe". The problem is that I have no idea how to do that, especially not on the Fortran side.
Brad.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I've never done this, but I think the sequence would be:
Call the Windows API routine CreateNamedPipe, giving a name that you hope will be unique on the system. You can construct it using the parent process ID as part of the name, for example. Let's say you call it 'CONSOLE0043'. Save the handle it returns.
In an OPEN statement, specify FILE='\\.\pipe\CONSOLE0043'. (See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365783(v=vs.85).aspx ) For the console, open unit 6 and make sure you build with /standard-semantics or /assume:noold_unit_star.
After you close the unit, call CloseHandle on the saved handle.
This takes care of the writing. You'd then need to use CreateThread to create a thread per pipe that opens the pipe (no need to create it again), reads from it and does whatever processing you want.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Steve Lionel (Intel) wrote:
I've never done this, but I think the sequence would be:
Call the Windows API routine CreateNamedPipe, giving a name that you hope will be unique on the system. You can construct it using the parent process ID as part of the name, for example. Let's say you call it 'CONSOLE0043'. Save the handle it returns.
In an OPEN statement, specify FILE='\\.\pipe\CONSOLE0043'. (See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365783(v=vs.85).aspx ) For the console, open unit 6 and make sure you build with /standard-semantics or /assume:noold_unit_star.
After you close the unit, call CloseHandle on the saved handle.
This takes care of the writing. You'd then need to use CreateThread to create a thread per pipe that opens the pipe (no need to create it again), reads from it and does whatever processing you want.
OK, I think we're getting closer, but unfortunately I have no idea how to do these things in Fortran. Anyone out there that has done this and can provide some sample code?
Thanks!
Brad.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Be very careful what you do in DLLMain. The practical limitations are severe - e.g. it is possible for a mere variable declaration to result in the the Fortran compiler generating code that requires an allocation from the heap, which may require the C runtime library to be loaded and initialized, and you may not be able to count on that in DLLMain. There be dragons, with sharp teeth and bad breath.
This post has some example code of handle redirection in Fotran, using the IFWIN stuff, when calling a child executable. In the file attached to that post see the check_program_output internal procedure. With the large caveat that it has been a while since I played with this stuff, so I might be spruiking nonsense (well, there's always the risk of that regardless of the passage of time), but you may be able to adapt something like this. However I have recollection of trying that from in-process, using SetStdHandle or similar, and the results being variable and not being satisfactory.
Philosophically, there are benefits and costs to DLL'ising something. Depending on your situation (how tightly integrated you need things), you may find that it is better to keep the Fortran code as a separate process anyway, in which case the example is directly relevant. You can still have quite efficient interchange of things like inputs and outputs through things like memory mapped files and the like. One large benefit is if the child process carks it for whatever arbitrary reason, then your parent process can relatively easily recover.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
IanH,
Thanks for the warning about what can/cannot be performed in DllMain. I should have included that in the comments of the code sample.
>> You can still have quite efficient interchange of things like inputs and outputs through things like memory mapped files and the like. One large benefit is if the child process carks it for whatever arbitrary reason, then your parent process can relatively easily recover.
That was one of the major benefits I "loved" having with the Intel Array Visualizer (long defunct). This is the API you use from the program and not the "toy" feature of using it from the debugger. Once configured, your compute process can churn away, with occasional updating of the memory mapped file (database), with exceptionally low overhead. The display process, or multiple processes, can be started at will. For example, it was handy for me to launch three viewers from my simulation program (no extra code in the simulation program), with one view of a 3D graph from the X viewpoint, and one from the Y viewpoint, and then an animated 2D chart and/or spreadsheet-like tables (all browseable). Occasionally when a viewer crashes (bug in viewer), it would not take down the simulation, which may take days to complete.
That product put the "Visual" into Intel Visual Fortran.
Jim Dempsey
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I think at this point that I'll put this on hold. My Fortran is so rusty that I'd need a more complete sample to get me where I need to go.
Thanks anyway!
Brad.
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page