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

GlobalMemoryStatusEx problem

groupw_bench
Beginner
2,023 Views
I have a Fortran program which can be run on any Windows platform Windows 98 or later. It's started by the VB program via a CreateProcess API call. This method has been working flawlessly on all systems for a long time.

In the past, the Fortran program would check the memory with a GlobalMemoryStatus call. I recently modified it so that it first checks the operating system type then, if the OS is Windows 2000 or later, it calls GlobalMemoryStatusEx instead. The method works fine when running under XP. (The same method implemented in the VB program also works fine on all systems.) But when the Fortran program is launched under Windows 98, the CreateProcess fails, and the system returns ERROR_GEN_FAILURE. The puzzling thing is that this happens without GlobalMemoryStatusEx even being called. All that's necessary to cause the crash is for the call (and/or perhaps the setting of a variable as type T_MEMORYSTAUSEX) to be present in the code. When I comment out the GlobalMemoryStatusEx-related code, the program functions normally.

I'm using IVF v. 9.1. I haven't been able to figure out how to determine the build or minor version number.

Does anyone know what's going on here and how to solve it?

Thanks!

0 Kudos
9 Replies
groupw_bench
Beginner
2,023 Views
Additional information:

All I have to do to cause this crash is to add the following subroutine to the program and run it under Windows 98. It doesn't have to be called; it just has to be in the program. Tests show that the program isn't even starting when this subroutine is present; the first line of program code isn't being executed. Commenting out the line which calls GlobalMemoryStatusEx restores normal program operation.

SUBROUTINE CrashIt

USE IFWIN

IMPLICIT NONE

INTEGER (KIND = 4) :: RetVal
TYPE (T_MEMORYSTATUSEX) :: lpBufferEx

lpBufferEx.dwLength = SIZEOF(lpBufferEx)
RetVal = GlobalMemoryStatusEx(lpBufferEx)
END SUBROUTINE CrashIt

I realize that the GlobalMemoryStatusEx function doesn't exist on Windows 98 systems, but my program is written so it's not called on those systems. The program shouldn't crash just because the calling statement is in the code.

Is there some compiler directive or option I need to set to keep this from happening? I very much don't want to have to issue two versions of the program for different operating systems just because of this one compiler peculiarity.

0 Kudos
Jugoslav_Dujic
Valued Contributor II
2,023 Views
Win98, eh? Who [still] cares? Smiley with tongue out [:-P]

Now seriously, I think your only option is to use dynamic binding of DLL procedures. Otherwise, the linker will put a reference to Kernel32::GlobalMemoryStatusEx into the .exe's header, and the loader will likely refuse to run it when it encounters it. It's not a compiler's peculiarity, it's [unfortunately] a consequence of Windows design. Dynamic binding resolves the function reference at run-time, not at load-time:

INTERFACE
INTEGER FUNCTION _GlobalMemoryStatusEx(lpBuffer)
USE DFWINTY, ONLY: T_MEMORYSTATUSEX
!DEC$ATTRIBUTES STDCALL, REFERENCE:: _GlobalMemoryStatusEx
TYPE(T_MEMORYSTATUSEX):: lpBuffer
END FUNCTION
END INTERFACE
POINTER(pGMSE, GlobalMemoryStatusEx)

hKernel32 = GetModuleHandle("Kernel32.dll"C)
pGMSE = GetProcAddress(hKernel32, "GlobalMemoryStatusEx"C)
IF (pGMSE.NE.0) THEN
!We're on winNT+ here, call _GlobalMemoryStatusEx
ELSE
!We're on win9x here, call GlobalMemoryStatus
END IF
0 Kudos
groupw_bench
Beginner
2,023 Views
Thanks very much for the help. I had assumed that the problem was due to a compiler peculiarity because I have essentially the same code in a VB program running under Windows, and it doesn't have any problem with it at all.

Please pardon my ignorance, but I'm not a very skilled Fortran programmer. I believe you're suggesting putting the GlobalMemoryStatusEx call into a DLL function. I'd thought of that, but so far haven't been able to implement it. I don't use IVF for DLLs, because it requires bundling a large group of files with my program (including the whole .NET framework as I recall), with total size greater than the program itself, just to get a small DLL to run. So I've been using my old CVF for creating the few DLLs I have. And when I try to build the DLL with CVF, I get an "unresolved external" linker error for GlobalMemoryStatusEx. I don't have a clue as to what the problem might be, so I was hoping there was some simple way to solve the problem within the main Fortran program. Looks like I'm either going to have to make separate programs for the Windows 98 and other users, or see if I can figure out how to build a C dll.

Who cares about Windows 98? Probably only around a hundred or so of my customers. Not a really large fraction, but enough to take some trouble to accommodate for the time being.

Thanks again for the help!

0 Kudos
groupw_bench
Beginner
2,023 Views
I've solved the problems.

I got the CVF DLL to link by replacing the kernel32.lib file it was linking to, to the one used by IVF. Apparently the old one didn't contain GlobalMemoryStatusEx, even though it's defined in the kernel32.f90 include file.

When I built a DLL containing both GlobalMemoryStatus and GlobalMemoryStatusEx, the program behaves exactly the same as it does when GlobalMemoryStatusEx is integral to the program -- it crashes when starting. So what I ended up doing was making two separate DLLs with the same name and external interface, but internally one calls GlobalMemoryStatus and the other GlobalMemoryStatusEx. I'll issue both with the program and have the installer install the one appropriate to the operating system. This could, of course, have been done with the main program, but the application includes up to four variations of it, so it would have greatly increased the size and complexity of the application -- in addition to the necessity of recompiling twice as many exe files each time any change was made. The DLLs are just 24 kB, so no problem with having a couple of those.

0 Kudos
Jugoslav_Dujic
Valued Contributor II
2,023 Views
Well, I'm glad that you solved the problem, but you did misread my post -- I meant nothing of the sort of moving the call into a Dll. The reference to "dll" in my post was meant solely to the system's Kernel32.dll. Win9x versions of Kernel32.dll don't contain GlobalMemoryStatusEx; WinNT+ ones do.

One can call Dll functions, system dll's included in two ways: "statically" and "dynamically". The first method is probably common to you: you link in a small ("stub") .lib produced while building a dll. Kernel32.lib in your compiler's include folder is the same "stub" library for Kernel32.dll.

However, if you only reference a non-existing Dll export through a statically bound dll, the OS loader will refuse to start the application, as you discovered.

Instead, you can "dynamically" test whether the exported function exists, in run time, using GetProcAddress API, as I tried to demonstrate; since the call is resolved at run-time, it won't be referenced in your exe's header, and the loader will be happy. In that way, you wouldn't have to delegate the problem to the installer.
0 Kudos
groupw_bench
Beginner
2,023 Views
Once again, thank you very much for your help -- and your patience. Because of my general low level of Fortran knowledge, it took a while for what you did to sink in. But I've got it, and have successfully implemented in my program. It's a much better solution for me than the DLLs.

Thanks again!
0 Kudos
Zhanghong_T_
Novice
2,023 Views

Hi Jugoslav,

How can I get the available memory on Windows XP x64system? It seems that the function GlobalMemoryStatusdoesn't work. How about the function GlobalMemoryStatusEx?

I wrote the following code:

integer*8 dwAvailPhys
logical::res
type(T_MEMORYSTATUSEX) lpMstMemStatEx
lpMstMemStatEx.dwLength = 7
! Call windows API to get memory status
res = GlobalMemoryStatusEx (lpMstMemStatEx)
dwAvailPhys = lpMstMemStatEx.ullAvailPhys
out = dwAvailPhys / 1048576

But it can't pass the compiling. How should I modify the code to get the availabe memory?

Thanks,

Zhanghong Tang

0 Kudos
Steven_L_Intel1
Employee
2,023 Views
The problem is that on an x64 system, the fields such as ullAvailPhys are of a derived type T_LONG_INTEGERX which has two integer(4) fields.

I've attached a sample that shows one way to deal with this in a platform-independent manner.
0 Kudos
Zhanghong_T_
Novice
2,023 Views

Hi Steve,

Thanks for your kindly reply. I got it.

Zhanghong Tang

0 Kudos
Reply