Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

using gfortran to call windows api functions

288 views
Skip to first unread message

mark.h...@buseco.monash.edu.au

unread,
Nov 9, 2010, 6:14:37 AM11/9/10
to
I am attempting, using 32-bit gfortran in Windows, to call some
Windows API functions. I am having some success, and can for instance
report the current directory. I am using the current mingw binaries,
based on gcc version 4.5.0 (GCC). I compile the program below via:
gfortran -mrtd test4.f90 -otest4.exe
It compiles fine, without warnings.
When I run it, the output is:
Current Dir: C:\temp
0
Current ExeName:

Although GetCurrentDirectory works fine, GetModuleFileName does not;
and I suspect
the reason to lie in the first parameter, for which Windows expects a
4-byte unsigned integer
value of zero or null. How should I pass this unsigned zero ??
I have the same problem tying to use the Windows Sleep function, which
expects
Milliseconds, an unsigned doubleword.

I'd be very grateful to hear of any way to accomplish this from within
Gfortran (ie, not by writing my own C function).

Mark Horridge

!--------------------------------------------------------------
module Win32
use ISO_C_BINDING
implicit none
private

public GetCurrentDirectory
interface
function GetCurrentDirectory(cchBuffer, lpszCurDir)
bind(C,Name='GetCurrentDirectoryA')
use ISO_C_BINDING
implicit NONE
!GCC$ ATTRIBUTES STDCALL :: GetCurrentDirectory
integer(C_LONG) GetCurrentDirectory
integer(C_LONG) cchBuffer
character(kind=C_CHAR) lpszCurDir
end function GetCurrentDirectory
end interface

public GetModuleFileName
interface
function GetModuleFileName(hmod, lpszfnam, cchBuffer)
bind(C,Name='GetModuleFileNameA')
use ISO_C_BINDING
implicit NONE
!GCC$ ATTRIBUTES STDCALL :: GetModuleFileName
integer(C_LONG) GetModuleFileName
integer(C_LONG) hmod
character(kind=C_CHAR) lpszfnam
integer(C_LONG) cchBuffer
end function GetModuleFileName
end interface

end module Win32

program test
use Win32
use ISO_C_BINDING
implicit none
integer(C_LONG) b
character StringA*(255)
b = GetCurrentDirectory(255,StringA)
print *,' Current Dir: '//StringA(1:b)
b = GetModuleFileName(0,StringA,255)
print *,b
print *,' Current ExeName: '//StringA(1:b)
end program test


Tim Prince

unread,
Nov 9, 2010, 8:03:28 AM11/9/10
to
I suppose you required value attribute in many of those integer
definitions; without that, you are passing a pointer. This at least
would enable the compiler to complain about mistakes. I read your
character(kind=C_CHAR) definitions as covering only a single character.

--
Tim Prince

James Van Buskirk

unread,
Nov 9, 2010, 10:56:34 AM11/9/10
to
<mark.h...@buseco.monash.edu.au> wrote in message
news:6d61fd4a-23f6-4c6c...@p20g2000prf.googlegroups.com...

>I am attempting, using 32-bit gfortran in Windows, to call some
> Windows API functions. I am having some success, and can for instance
> report the current directory. I am using the current mingw binaries,
> based on gcc version 4.5.0 (GCC). I compile the program below via:
> gfortran -mrtd test4.f90 -otest4.exe
> It compiles fine, without warnings.
> When I run it, the output is:
> Current Dir: C:\temp
> 0
> Current ExeName:

> Although GetCurrentDirectory works fine, GetModuleFileName does not;
> and I suspect
> the reason to lie in the first parameter, for which Windows expects a
> 4-byte unsigned integer
> value of zero or null. How should I pass this unsigned zero ??
> I have the same problem tying to use the Windows Sleep function, which
> expects
> Milliseconds, an unsigned doubleword.

> I'd be very grateful to hear of any way to accomplish this from within
> Gfortran (ie, not by writing my own C function).

> !--------------------------------------------------------------

> end module Win32

First off, just forget about -mrtd with gfortran. It doesn't work
in any way you might consider sensible. Those !GCC$ ATTRIBUTES STDCALL
Directive Enhanced Compilation statements you put in there do the
right thing already.

Second, if you can, try to make interfacing to Win32 API functions
work in 64-bit Windows then go back and see if what you did works
in 32-bit Windows. 64-bit Windows is so much easier because there
is no issue with STDCALL: there is only one calling convention.

Third, look up the documention for the function you are trying to
interface to in MSDN. Use exactly the names given there; don't
try to be creative or abbreviate or anything. This doesn't have
any effect on whether or not your program works, it just makes
it more self-documenting. But you do have a real problem in
writing the interfaces. The integer arguments are passed by value
according to MSDN, so they should read, for example:

integer(C_LONG), value :: nBufferLength

Also HMODULE is a handle according to MSDN, and handles are
integer(C_INTPTR_T), not integer(C_LONG). Doesn't make a
difference in 32-bit Windows, I know, but if you make the change
as suggested your program will work perfectly in 32-bit gfortran,
64-bit gfortran, and 64-bit ifort. Sorry about 32-bit ifort, Intel
chose to make interfacing to STDCALL procedures not work with
ISO_C_BINDING.

And your string arguments should specify that you are passing
arrays, not single characters by reference. Maybe the way you
are doing works in the compilers you use now, the I dont think
that that is just an accident of implementation, not anything
the standard says.

So fixing just your interface bodies and getting rid of -mrtd, I
get:

C:\gfortran\clf\wintest>type wintest.f90


module Win32
use ISO_C_BINDING
implicit none
private

public GetCurrentDirectory
interface
function GetCurrentDirectory(nBufferLength, lpBuffer) &


bind(C,Name='GetCurrentDirectoryA')
use ISO_C_BINDING
implicit NONE
!GCC$ ATTRIBUTES STDCALL :: GetCurrentDirectory
integer(C_LONG) GetCurrentDirectory

integer(C_LONG),value :: nBufferLength
character(kind=C_CHAR) lpBuffer(*)


end function GetCurrentDirectory
end interface

public GetModuleFileName
interface
function GetModuleFileName(hModule, lpFileName, nSize) &


bind(C,Name='GetModuleFileNameA')
use ISO_C_BINDING
implicit NONE
!GCC$ ATTRIBUTES STDCALL :: GetModuleFileName
integer(C_LONG) GetModuleFileName

integer(C_INTPTR_T), value :: hModule
character(kind=C_CHAR) lpFileName(*)
integer(C_LONG), value :: nSize


end function GetModuleFileName
end interface

end module Win32

program test
use Win32
use ISO_C_BINDING
implicit none
integer(C_LONG) b
character StringA*(255)

b = GetCurrentDirectory(len(StringA),StringA)


print *,' Current Dir: '//StringA(1:b)

b = GetModuleFileName(0_C_INTPTR_T,StringA,len(StringA))


print *,b
print *,' Current ExeName: '//StringA(1:b)
end program test

C:\gfortran\clf\wintest>gfortran wintest.f90 -owintest

C:\gfortran\clf\wintest>wintest
Current Dir: C:\gfortran\clf\wintest
35
Current ExeName: C:\gfortran\clf\wintest\wintest.exe

C:\gfortran\clf\wintest>type wintest.f90


module Win32
use ISO_C_BINDING
implicit none
private

public GetCurrentDirectory
interface
function GetCurrentDirectory(nBufferLength, lpBuffer) &


bind(C,Name='GetCurrentDirectoryA')
use ISO_C_BINDING
implicit NONE
!GCC$ ATTRIBUTES STDCALL :: GetCurrentDirectory
integer(C_LONG) GetCurrentDirectory

integer(C_LONG),value :: nBufferLength
character(kind=C_CHAR) lpBuffer(*)


end function GetCurrentDirectory
end interface

public GetModuleFileName
interface
function GetModuleFileName(hModule, lpFileName, nSize) &


bind(C,Name='GetModuleFileNameA')
use ISO_C_BINDING
implicit NONE
!GCC$ ATTRIBUTES STDCALL :: GetModuleFileName
integer(C_LONG) GetModuleFileName

integer(C_INTPTR_T), value :: hModule
character(kind=C_CHAR) lpFileName(*)
integer(C_LONG), value :: nSize


end function GetModuleFileName
end interface

end module Win32

program test
use Win32
use ISO_C_BINDING
implicit none
integer(C_LONG) b
character StringA*(255)

b = GetCurrentDirectory(len(StringA),StringA)


print *,' Current Dir: '//StringA(1:b)

b = GetModuleFileName(0_C_INTPTR_T,StringA,len(StringA))


print *,b
print *,' Current ExeName: '//StringA(1:b)
end program test

C:\gfortran\clf\wintest>gfortran wintest.f90 -owintest

C:\gfortran\clf\wintest>wintest
Current Dir: C:\gfortran\clf\wintest
35
Current ExeName: C:\gfortran\clf\wintest\wintest.exe

I wish you similar success.

--
write(*,*) transfer((/17.392111325966148d0,6.5794487871554595D-85, &
6.0134700243160014d-154/),(/'x'/)); end


mark.h...@buseco.monash.edu.au

unread,
Nov 11, 2010, 8:21:17 AM11/11/10
to
Thanks very much James van Buskirk,
Of course we had already pored for hours over your previous Gfortran
examples
of how to write a windowed program, and how to create, load and run a
DLL --
but those are hardly beginners examples. Your wintest.f90 is, I think,
the first
example on the internet of how to call some simple windows API
functions from Gfortran.
We have called such windows routines in the past using lf90, lf95,
if32, and if64 -- but these
compilers have various different ways of accomplishing API calls (they
supply import libraries).

We have managed to extend your example to encompass
GlobalMemoryStatus, GetVersionEx, GetWindowsDirectory,
GetCurrentDirectory GetModuleFileName Sleep GetSystemInfo
GetFileAttributes GetCommandLine GetFullPathName and GetLastError

to create 32 and 64-bit exe files which run OK. That program is at:
http://www.monash.edu.au/policy/ftp/gpextra/test9.f90
It gives a compiler warning, and is doubtless sub-optimal -- but gives
a basis to progress further.
CreateProcess may be our next goal.

Thank you again,

Mark Horridge

James Van Buskirk

unread,
Nov 12, 2010, 4:02:38 PM11/12/10
to
<mark.h...@buseco.monash.edu.au> wrote in message
news:041a7967-7669-40f2...@u11g2000prn.googlegroups.com...

> We have managed to extend your example to encompass
> GlobalMemoryStatus, GetVersionEx, GetWindowsDirectory,
> GetCurrentDirectory GetModuleFileName Sleep GetSystemInfo
> GetFileAttributes GetCommandLine GetFullPathName and GetLastError

> to create 32 and 64-bit exe files which run OK. That program is at:
> http://www.monash.edu.au/policy/ftp/gpextra/test9.f90
> It gives a compiler warning, and is doubtless sub-optimal -- but gives
> a basis to progress further.
> CreateProcess may be our next goal.

There are a few things I would have changed in your example, but I
haven't gone over the whole thing. You can get rid of the warning
by changing:

character(128) szCSDVersion

to one of:

character(kind=C_CHAR) szCSDVersion*128
character(len=128,kind=C_CHAR) szCSDVersion
character(128,C_CHAR) szCSDVersion

0 new messages