using gfortran to call windows api functions

  • Follow


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


0
Reply mark 11/9/2010 11:14:37 AM

On 11/9/2010 3:14 AM, mark.horridge@buseco.monash.edu.au wrote:
> 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
>
>
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
0
Reply Tim 11/9/2010 1:03:28 PM


<mark.horridge@buseco.monash.edu.au> wrote in message 
news:6d61fd4a-23f6-4c6c-93af-f77da6d48e8d@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).

> !--------------------------------------------------------------
>        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

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


0
Reply James 11/9/2010 3:56:34 PM

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
0
Reply mark 11/11/2010 1:21:17 PM

<mark.horridge@buseco.monash.edu.au> wrote in message 
news:041a7967-7669-40f2-b6b7-b71fc774bb5a@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

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


0
Reply James 11/12/2010 9:02:38 PM

4 Replies
1469 Views

(page loaded in 0.003 seconds)

Similiar Articles:













7/20/2012 3:06:10 PM


Reply: