Calling a FORTRAN DLL using ctypes
Question: 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


Answer: 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.

