Calling a FORTRAN DLL using ctypes

2019-08-06 15:56发布

问题:

I am trying to learn how to complie FORTRAN code into a DLL that I can call from Python using ctypes. Even a simple example is not working, can anyone help?

I have a single procedure in FORTRAN:

  subroutine ex(i)
  integer i
  i=i+1
  return
  end 

Then I try to run this from Python

I compile it with the MinGW complier as follows

gfortran -c test.f
gfortran -shared -mrtd -o test.dll test.o

Looking at the DLL created I see

Microsoft (R) COFF/PE Dumper Version 12.00.30723.0
Copyright (C) Microsoft Corporation.  All rights reserved.

Dump of file test.dll

File Type: DLL

Section contains the following exports for test.dll

00000000 characteristics
       0 time date stamp Thu Jan 01 13:00:00 1970
    0.00 version
       1 ordinal base
       1 number of functions
       1 number of names

ordinal hint RVA      name

      1    0 00001280 ex_

Summary

    1000 .CRT
    1000 .bss
    1000 .data
    1000 .edata
    1000 .eh_fram
    1000 .idata
    1000 .rdata
    1000 .reloc
    1000 .text
    1000 .tls

Then I try to access this from Python

from ctypes import *

DLL = windll.test
print DLL

print getattr(DLL,'ex_')
print DLL[1]
print DLL.ex_

x = pointer( c_int(3) )
DLL.ex_( x )

The output is

<WinDLL 'test', handle 6bec0000 at 2143850>

<_FuncPtr object at 0x020E88A0>
<_FuncPtr object at 0x020E8918>
<_FuncPtr object at 0x020E88A0>
Traceback (most recent call last):
  File "C:\proj_py\test.py", line 20, in <module>
    DLL.ex_( x )
ValueError: Procedure probably called with too many arguments (4 bytes in excess) 

So although the function is there, I'm not calling it correctly. I'm stumped.

I am using python 2.7.10 (32 bit) on a 64-bit Windows-7 machine, I have a recent version of the MinGW compiler:

$ gfortran -v
Using built-in specs.
COLLECT_GCC=c:\mingw\bin\gfortran.exe
COLLECT_LTO_WRAPPER=c:/mingw/bin/../libexec/gcc/mingw32/4.8.1/lto-wrapper.exe
Target: mingw32
Configured with: ../gcc-4.8.1/configure --prefix=/mingw --host=mingw32 --build=mingw32 --without-pic --enable-shared --e
nable-static --with-gnu-ld --enable-lto --enable-libssp --disable-multilib --enable-languages=c,c++,fortran,objc,obj-c++
,ada --disable-sjlj-exceptions --with-dwarf2 --disable-win32-registry --enable-libstdcxx-debug --enable-version-specific
-runtime-libs --with-gmp=/usr/src/pkg/gmp-5.1.2-1-mingw32-src/bld --with-mpc=/usr/src/pkg/mpc-1.0.1-1-mingw32-src/bld --
with-mpfr= --with-system-zlib --with-gnu-as --enable-decimal-float=yes --enable-libgomp --enable-threads --with-libiconv
-prefix=/mingw32 --with-libintl-prefix=/mingw --disable-bootstrap LDFLAGS=-s CFLAGS=-D_USE_32BIT_TIME_T
Thread model: win32
gcc version 4.8.1 (GCC) 

Can anyone offer a solution?

Thanks

回答1:

Fortran is generally pass-by-reference. When calling from other languages like C, that means passing a memory address into the Fortran subroutine. Apparently the Python implementation is similar. Before your edit, you had an error that said something like "Illegal access at 0x000003" which was suspiciously the same value "3" as you were trying to pass as the value. You entered the Fortran subroutine just fine, but when it tried to do the add, it looked for the integer at memory location 3 instead of using the value 3 in the addition itself.

After your edit, you are passing a pointer (based on my comment, I think). That gives a different error. It suggests that you passed an extra argument because it got 4 bytes more data than it expected. I think that's likely an incompatibility between some 32 bit libraries and some 64 bit libraries, with 4 bytes being a likely difference in length between pointers in the two architectures.