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

Fortran and Win32 programming

p_p_1
Beginner
4,298 Views

I'm not really experienced in Fortran, I tried unsuccessfully to call a Windows API function as a test (previously I tried some other tests such as locking the screen, with success).
The function is QueryProcessCycleTime (just for curiosity, it retrieves the cpu cycles count of a process, in my test code with a fixed pid of an actually existent process). Its syntax:

BOOL WINAPI QueryProcessCycleTime(
  _In_  HANDLE   ProcessHandle,
  _Out_ PULONG64 CycleTime
);

I couldn't find the Fortran "translation" for these data types and I tried several combinations in my code (for each variables) with no luck. I compiled with
ifort /libs:static /exe: ...   or
ifort /libs:qwin,static /exe: ...
but the output is always:
 F                  4936                     0
 (return value,  pid,  cycles count).

The function doesn't appear in the kernel32.f90 supplied by Intel (see Calling Windows API Routines for syntax not involving ISO_C_BINDING).
 
Going beyond the specific test I wondered if:
really Fortran can't load dlls supplying full path (without adding it as an environment variable)?
Or more in general and in a few words:

Can Fortran be used as a Win32 programming language?
(I don't mean only for a limited set of functions)

Or, at the end, is it (very, we know) useful only for number crunching?

My test code:

module process_cpu_cycles
use ISO_C_BINDING
implicit none

public QueryProcessCycleTime
interface
    function QueryProcessCycleTime(proc_id, cpu_cycles) bind(C,Name='QueryProcessCycleTime')
        use ISO_C_BINDING
        implicit NONE
        !DEC$ ATTRIBUTES STDCALL :: QueryProcessCycleTime
        logical(C_BOOL) :: QueryProcessCycleTime        ! tried C_INT too
        integer(C_INTPTR_T), value :: proc_id           ! tried without value
        integer(C_INTPTR_T), value :: cpu_cycles        ! tried C_INT64_T too
    end function QueryProcessCycleTime
end interface

end module process_cpu_cycles


program cycles
use process_cpu_cycles
use ISO_C_BINDING
implicit none

logical(C_BOOL) :: ret_val
integer(C_INTPTR_T) :: pid
integer(C_INTPTR_T) :: ccycle        ! tried C_INT64_T too

ccycle=0
pid=4936
ret_val = QueryProcessCycleTime(pid,ccycle)
write (*,*) ret_val, pid, ccycle
end program cycles

 

0 Kudos
47 Replies
FortranFan
Honored Contributor II
2,636 Views

You say, "I'm not really experienced in Fortran" - but can you state what you are experienced in?  Is it C/C++?  Because C/C++ is what Microsoft usually uses to give examples for the Windows API functions.  But it is noticeable that Microsoft doesn't give anything for QueryProcessCycleTime and cyber world mostly shows links for C/C++ users having trouble using this function.  Hence questions arise about your experience, what you're trying to do, the applicability of this particular API function for your needs, and what else you can use.

Note if overall CPU usage is what you're interested in, CPU_TIME standard intrinsic function in Fortran may perhaps serve your needs?

https://software.intel.com/en-us/node/580475

0 Kudos
Steven_L_Intel1
Employee
2,636 Views

Yes, Fortran can be used for a wide variety of Win32 programming. We do provide tens of thousands of API declarations, but Microsoft adds so many in each new Windows version it's hard to keep up. We have to create these manually.

Some Windows APIs are not usable except from C++. If it's usable from C, it's usable from Fortran.

I fixed up your code. The major error was the way you declared and used the ccid argument. You declared it as an address passed by value, but failed to use LOC() in the call. A minor error is using LOGICAL for the Bool datatype. You really should use the kind constants in IFWINTY rather than trying to map them into ISO_C_BINDING.

Here's a corrected version. Use whatever you need for pid.

module process_cpu_cycles
implicit none

public QueryProcessCycleTime
interface
    function QueryProcessCycleTime(proc_id, cpu_cycles) bind(C,Name='QueryProcessCycleTime')
        use IFWINTY
        implicit NONE
        !DEC$ ATTRIBUTES STDCALL :: QueryProcessCycleTime
        integer(BOOL) :: QueryProcessCycleTime       
        integer(HANDLE), value :: proc_id         
        integer(ULONG64) :: cpu_cycles       
    end function QueryProcessCycleTime
end interface

end module process_cpu_cycles


program cycles
use process_cpu_cycles
use IFWINTY
use KERNEL32, only: GetCurrentProcess
implicit none

integer(BOOL) :: ret_val
integer(HANDLE) :: pid
integer(LONG64) :: ccycle        ! tried C_INT64_T too

ccycle=0
pid=GetCurrentProcess()
ret_val = QueryProcessCycleTime(pid,ccycle)
write (*,*) ret_val, pid, ccycle
end program cycles

 

0 Kudos
Steven_L_Intel1
Employee
2,636 Views

Oh, another error was that you thought the first argument was a process ID - it isn't. It's a process handle. If you have the process ID you get the handle with OpenProcess - see https://msdn.microsoft.com/en-us/library/windows/desktop/ms684868(v=vs.85).aspx

0 Kudos
JVanB
Valued Contributor II
2,636 Views

When translating those API interfaces to good Fortran, you will find

https://msdn.microsoft.com/en-us/library/windows/desktop/aa383751(v=vs.85).aspx

to be very helpful. A couple of other hints:

  • Always copy the dummy argument names verbatim from the MSDN documentation. This is especially helpful for functions with long argument lists, where it is desirable to make a keyword call. If your argument names match the MSDN docs, you won't have to look in more than one place to know what each argument is supposed to be.
  • C has no LOGICAL type. LOGICAL(C_BOOL) actually corresponds to C99 _Bool, which is by the C standard an integer type. The companion processor to ifort for Windows, MSVC++, doesn't support C99, so LOGICAL(C_BOOL) doesn't actually match any C data type.

 

0 Kudos
p_p_1
Beginner
2,636 Views

Thanks Steve, anyway it didn't work (same output). I didn't understand IFWNTY can be used just as ISO_C_BINDING. The example in the guide looked a bit different.

Yes, I noticed it was an handle but I was set on the wrong track by wmic path win32_process which is reporting pid and handle as the same. I had that in mind.

I attempted first with C_INT instead of C_BOOL: same result, no errors, no warnings.

Yes, Reapeat Offender, I already downloaded that list. But, at the end, I shouldn't say it is so helpful since it differs greatly with respect to the Fortran equivalent supplied for binding.

 

I'm trying to undertand if I can adopt Fortran (and consequently organize myself) even in the (frequent) cases I need to call Windows functions. Frankly, to me, it remains hard to understand if the issues in the test above are exceptional or standard. And I couldn't understand if one could code his own wrappers in case of need (for specific functions, for example). In other words, how flexible this interoperability is.

0 Kudos
Steven_L_Intel1
Employee
2,636 Views

The code I posted does work - I tried it. You had a couple of usage errors and a misunderstanding of the first argument. You can't pass in a process ID - you have to get a handle to the desired process (which your privileges may or may not allow.)

APIs added after Windows XP may or may not be there. We fill them in when we can. This one was new in Vista.

0 Kudos
TimP
Honored Contributor III
2,636 Views

Repeat Offender wrote:

  • C has no LOGICAL type. LOGICAL(C_BOOL) actually corresponds to C99 _Bool, which is by the C standard an integer type. The companion processor to ifort for Windows, MSVC++, doesn't support C99, so LOGICAL(C_BOOL) doesn't actually match any C data type.

 

According to MSDN, all versions of Visual Studio which are still under support have bool as a built-in type (maybe not _Bool).  It does seem to differ marginally from standards.

VS2015 supports a fair amount of c99, an obvious exception being VLA (counterpart of Fortran automatic array), which was made optional in c11.

I'm not sure, where there are conflicts in usage between MSVC++ and ICL, which one counts as the companion processor to ifort.

0 Kudos
p_p_1
Beginner
2,636 Views

The code I posted does work - I tried it. You had a couple of usage errors and a misunderstanding of the first argument. You can't pass in a process ID - you have to get a handle to the desired process (which your privileges may or may not allow.)

Ok at the first step I failed manipulating the code. If leaved unmodified "maybe" it works. It gives -1 (the pseudo handle) as return value. From MSDN for GetCurrentProcess: "A pseudo handle is a special constant, currently (HANDLE)-1, that is interpreted as the current process handle.". So I can't understand if everything was going as expected... Anyway the return value (1) for QueryProcessCycleTime shows that it succededed and the cycles count has a reliable aspect.

Maybe this was an example a bit hostile. I admit, I never heard about the process handle before, and it seems to be not so well known. I couldn't find a way to get the handles for non-current-processes in a reasonable amount of time (maybe going deeply in other APIs, but that was only a test, after all...). I suspect that this handle thing can be the cause for having troubles with the QueryProcessCycleTime function.

0 Kudos
Steven_L_Intel1
Employee
2,636 Views

I gave you a link to the MSDN page that explains how to get process handles.

I wish I had an automated way of creating new interface blocks from the MSDN description. I did try working on one a while ago but gave it up. Otherwise it's an intensely manual process.

0 Kudos
p_p_1
Beginner
2,636 Views

I beg your pardon, Steve, I didn't realize immediately that it was THE hint. I searched elsewhere.

The following code is perfectly running on my machine:

module process_cpu_cycles
implicit none

public QueryProcessCycleTime
interface
    function QueryProcessCycleTime(proc_id, cpu_cycles) bind(C,Name='QueryProcessCycleTime')
        use IFWINTY
        implicit NONE
        !DEC$ ATTRIBUTES STDCALL :: QueryProcessCycleTime
        integer(BOOL) :: QueryProcessCycleTime      
        integer(HANDLE), value :: proc_id        
        integer(ULONG64) :: cpu_cycles      
    end function QueryProcessCycleTime
end interface

end module process_cpu_cycles


program cycles
use process_cpu_cycles
use IFWINTY
use KERNEL32, only: GetCurrentProcess, OpenProcess
implicit none

integer(BOOL) :: ret_val
integer(LONG64) :: ccycle
integer(HANDLE) :: proc_handle
integer(DWORD) :: dwDesiredAccess
integer(BOOL) :: bInheritHandle
integer(DWORD) :: dwProcessId

dwDesiredAccess=10000
bInheritHandle=0
dwProcessId=4936						! PID of an existent process
proc_handle=OpenProcess(dwDesiredAccess,bInheritHandle,dwProcessId)
ccycle=0
ret_val = QueryProcessCycleTime(proc_handle,ccycle)
write (*,*) ret_val, proc_handle, ccycle
end program cycles

The value for dwDesiredAccess is sufficiently high for the scope. The PID (dwProcessId) is of an existent process. bInheritHandle can be 0 or 1 in this case.

I could confirm the correctness of the result (the cpu cycles count for the process) comparing it with other (few) softwares that retrieve this value.

I'm quite impressed. This function is not present in the include directory but it can work anyway? Have we suddenly jumped on the other side, Fortran can beat C/C++??

Thanks Steve, these kind of results can be a good news (for me, at least).

0 Kudos
Ambaprasad_P_
Beginner
2,636 Views

Dear Steve

Just joined the forum.I am basically a structural engineer using fortran for the past two decades.

My present issue is using Fortran dlls from vb.net.

A simple and concise example would be very helpful.

Thanking you in advance,

Regards

Ambaprasad P

 

0 Kudos
Steven_L_Intel1
Employee
2,636 Views

p p - Pretty much anything C can do, Fortran can do. If a particular API routine or definition is not in the compiler's modules, you can always define your own. We will be filling these in over time.

Ambaprasad, there are two examples provided of VB calling Fortran DLLs in the MixedLanguage ZIP under Samples, installed along with the compiler.

0 Kudos
Steven_L_Intel1
Employee
2,636 Views

A suggestion - if you find you have to add your own declaration of a Windows API item because it isn't in our provided modules, change the name so that it won't create a conflict when we do add the entry. For example, put an X at the end of the function name (not in the Name= value, of course!)

0 Kudos
p_p_1
Beginner
2,636 Views

I was trying to call the Windows API function NtQuerySystemTime, which is in ntdll.dll (it retrieves the current system time), but linking failed: LNK1120, unresolved symbol.

No ntdll-like thing was present in my Fortran installation.
So I tried to load ntdll.dll using the Windows API functions LoadLibrary and GetProcAddress and this way it worked. I was able to successfully access the NtQuerySystemTime function.
Note that with LoadLibrary you have the possibility (but it's not mandatory) to load a module providing the full path.


Here is the ISO_C_BINDING version of the code. IFWINTY has been called only to avoid rewriting the type for T_FILETIME.

program getSystemTime
use ISO_C_BINDING
use IFWINTY
implicit none

interface 
	function LoadLibrary(lpFileName) bind(C,name='LoadLibraryA')			! the ansi version
		use ISO_C_BINDING
		implicit none
		character(C_CHAR) :: lpFileName(*) 
		!DEC$ ATTRIBUTES STDCALL :: LoadLibrary 
		INTEGER(C_INTPTR_T) :: LoadLibrary 
	end function LoadLibrary 

	function GetProcAddress(hModule, lpProcName) bind(C, name='GetProcAddress')
		use ISO_C_BINDING
		implicit none
		!DEC$ ATTRIBUTES STDCALL :: GetProcAddress
		TYPE(C_FUNPTR) :: GetProcAddress
		INTEGER(C_INTPTR_T), value :: hModule
		character(C_CHAR) :: lpProcName(*)
	end function GetProcAddress
end interface

abstract interface
	subroutine gettime(SystemTime) bind(C)
		use IFWINTY
		TYPE (T_FILETIME) SystemTime
	end subroutine
end interface

character*(260) lpLibFileName
character*(260) lpProcName
TYPE (T_FILETIME) SystemTime
INTEGER(C_INTPTR_T) :: module_handle
TYPE(C_FUNPTR) :: module_address
PROCEDURE(gettime), POINTER :: fort_addr

lpLibFileName="C:\Windows\system32\ntdll.dll"		! "ntdll.dll" is good anyway
lpProcName="NtQuerySystemTime" // achar(0)

module_handle=LoadLibrary(lpLibFileName)
module_address=GetProcAddress(module_handle,lpProcName)

call C_F_PROCPOINTER(module_address, fort_addr)
call fort_addr(SystemTime)

write (*,*) trim(lpLibFileName)				! loaded module name
write (*,*) module_handle					! module handle
write (*,*) SystemTime						! the two parts of system time

end program getSystemTime

I'm not familiar with the abstract interface. I guess it allows building an interface without an explicit reference to the function, which would lead to a linking error.


Now the IFWINTY version. Use of IFWINTY and KERNEL32 provide already the necessary interfaces for LoadLibrary and GetProcAddress.
ISO_C_BINDING is loaded too, but this time for a less aesthetic reason. I didn't know how to translate the C_F_PROCPOINTER subroutine in a IFWINTY way. So it was necessary to make use of transfer to cast module_address to a variable with the suitable data type for C_F_PROCPOINTER.
This mixing between wrappers looks a bit messy, though.

program getSystemTime
use IFWINTY
use KERNEL32, only: LoadLibrary, GetProcAddress
use ISO_C_BINDING
implicit none

abstract interface
	subroutine gettime(SystemTime) bind(C)
		use IFWINTY
		TYPE (T_FILETIME) SystemTime
	end subroutine
end interface

character*(260) lpLibFileName
character*(260) lpProcName
TYPE (T_FILETIME) SystemTime
integer(HANDLE) :: module_handle
integer(LPVOID) :: module_address
TYPE(c_funptr) :: module_address2
PROCEDURE(gettime), POINTER :: fort_addr

lpLibFileName="C:\Windows\system32\ntdll.dll"		! "ntdll.dll" is good anyway
lpProcName="NtQuerySystemTime"// achar(0)

module_handle=LoadLibrary(lpLibFileName)
module_address=GetProcAddress(module_handle,lpProcName)
module_address2=transfer(module_address,module_address2)

call C_F_PROCPOINTER(module_address2, fort_addr)
call fort_addr(SystemTime)

write (*,*) trim(lpLibFileName)				! loaded module name
write (*,*) module_handle					! module handle
write (*,*) SystemTime						! the two parts of system time

end program getSystemTime

 

In both cases, command line:  ifort source.f90 /libs:static /exe:program.exe

0 Kudos
Steven_L_Intel1
Employee
2,636 Views

ntdll is a core Windows DLL, not a Windows API library. We don't provide interfaces to things that aren't in import libraries. Besides, MSDN says not to use NtQuerySystemTime and to use GetSystemTimeAsFileTime instead.

0 Kudos
JVanB
Valued Contributor II
2,636 Views

It's possible to get or make your own ntdll.lib: see http://stackoverflow.com/questions/6774967/lnk2019-unresolved-external-symbol-ntopenfile . It looks like you're getting the hang of interfacing with Windows API functions. I do see one mistake: NtQuerySystemTime is declared with the WINAPI macro in MSDN's documentation, which indicates the STDCALL calling convention. Thus you need

!DEC$ ATTRIBUTES STDCALL :: gettime

in the declaration. BTW, I think you should call the interface NtQuerySystemTime or at worst NtQuerySystemTimeX instead of gettime, it's more self-documenting that way. Why are you typing its argument as T_FILETIME when the MSDN docs document it as T_LARGE_INTEGER? INTEGER(INT64) (INT64 coming from ISO_FORTRAN_ENV) would be fine, too. Also lpLibFileName needs a terminating NUL which you forgot.

 

0 Kudos
FortranFan
Honored Contributor II
2,636 Views

p p. wrote:

.. Here is the ISO_C_BINDING version of the code. IFWINTY has been called ..

Not sure what you're getting at, what you suggest is neither here nor there.  Almost all the Intel Fortran users would simply prefer to make use of MSDN-recommended function of GetSystemTimeAsFileTime instead and they would do so very simply as:

program getSystemTime

   use IFWINTY, only  : T_FILETIME
   use KERNEL32, only : GetSystemTimeAsFileTime

   implicit none

   type(T_FILETIME) :: SystemTime

   call GetSystemTimeAsFileTime(SystemTime)

   write (*,*) SystemTime	

   stop

end program getSystemTime

and those need portable code would go the full distance with standard features for interoperability with C via ISO_C_BINDING and have no references to Intel Fortran provided modules of IFWINTY and KERNEL32 at all.

0 Kudos
p_p_1
Beginner
2,636 Views

Premising: the codes above were perfectly linking and running, on my machine at least.

ntdll is a core Windows DLL, not a Windows API library. We don't provide interfaces to things that aren't in import libraries

For what are my needs, I don't mind subdividing things in objects those times it's not significant. There was a function, there, in the Windows world and I could call it coding in Fortran. That's the point.

MSDN says not to use NtQuerySystemTime and to use GetSystemTimeAsFileTime instead

Yes, I saw it. It was good for testing purpose, in other cases may not (or yes anyway).

NtQuerySystemTime is declared with the WINAPI macro in MSDN's documentation, which indicates the STDCALL calling convention. Thus you need !DEC$ ATTRIBUTES STDCALL :: gettime
in the declaration. BTW, I think you should call the interface NtQuerySystemTime or at worst NtQuerySystemTimeX instead of gettime, it's more self-documenting that way

I don't know if I'm missing something. Anyway, this is what I did:
I called with compiler directives (!DEC$) ONLY the LoadLibrary and the GetProcAddress functions. In other words I used the Fortran built-in module loader to call a Windows module loader. Then again I used the Windows module loader (so, in some sense, outside the Fortran world) to load ntdll.dll and make its exported functions accessible. It seemed to be necessary to provide an abstract interface block, but I can't say more than this about this specific subject.
So, I had to do this way beacuse I wasn't able to achieve this result only using !DEC$ directives (in other words: only using the Fortran/ifort built-in module loader). In fact, the result was: unresolved symbol.

Also lpLibFileName needs a terminating NUL which you forgot.

It worked anyway, and me too I asked myself why. However I had to add //achar(0) in that specific point to make it work. Possibly I omitted the null termination in other working code (always testing).

It's possible to get or make your own ntdll.lib

I was thinking to test this way too.

Why are you typing its argument as T_FILETIME when the MSDN docs document it as T_LARGE_INTEGER? INTEGER(INT64)

For what I can understand, this is the (ready to use) IFWINTY translation for those Windows data types. I used it in the ISO_C_BINDING version of the code too, avoiding to translate it in a ISO_C_BINDING "language".

Almost all the Intel Fortran users would simply prefer...

In fact I'm trying to go beyond what generally Fortran users do.

and those need portable code would go the full distance with standard features for interoperability with C via ISO_C_BINDING and have no references to Intel Fortran provided modules of IFWINTY and KERNEL32 at all

I'm going step by step. I agree with you for not to have references to Intel-only modules, if this could be possible.

 

Lastly I want to point out, once again, that with the LoadLibrary you can load a module with his full (or relative) path. I don't know if I missed something, but I couldn't find I way to do the same with only !DEC$ directives.

0 Kudos
andrew_4619
Honored Contributor II
2,636 Views

This mixing between wrappers looks a bit messy, though.

 

It makes more sense and look cleaner to create your own interfaces module e.g. MyWinty to compliment IFWIN/IFWINTY and just add USE MyWinty where appropriate.  Or to go further add IFWIN to MyWinty and only use that!

 

0 Kudos
p_p_1
Beginner
2,436 Views

app4619 wrote:

This mixing between wrappers looks a bit messy, though.

 

It makes more sense and look cleaner to create your own interfaces module e.g. MyWinty to compliment IFWIN/IFWINTY and just add USE MyWinty where appropriate.  Or to go further add IFWIN to MyWinty and only use that!

 

In fact. Anyway, if we consider the tools made available as standard (I'm considering IFWINTY standard, which it isn't) it seems that this mixing could arise quite easily. The code above, involving a simple call to the NtQuerySystemTime function, is simple enough to be written solely for the ISO_C_BINDING wrapper (I guess so). But... is it always so? From a more general perspective, I mean, not only case by case.

0 Kudos
Reply