Calling CVF-Fortran-dll with ctypes and simple structure

  • Thread starter Michael Schäfer
  • Start date
M

Michael Schäfer

Hi all,

I deal with the old problem passing characters from python to a
fortran dll build with CFV6.6c.
I reduced our complex structure to a simple one. Here is the Fortran
code:

SUBROUTINE DEMO2L(s)

C sample for calling CVF6.6c-DLLs from
C vb/vba/python with simple structure

!DEC$ ATTRIBUTES DLLEXPORT::DEMO2L

TYPE rcDEMO
INTEGER a
REAL b
CHARACTER c*9
END TYPE

TYPE(rcDEMO) :: s
C
WRITE(*,*) s.a, s.b, s.c
C sample calculation:
s.a = s.a*10
s.b = s.b**2.3
s.c = 'Sample'

RETURN
END

From VB the calling is very simple:

Declare Sub DEMO2L Lib "release\demo1l.dll" (ByRef k As rcDEMO)

Type rcDEMO
a As Long
b As Single
c As String * 9
End Type

Dim k As rcDEMO

Sub run()

k.a = 2
k.b = 3#
k.c = "Test"

Call DEMO2L(k)

Debug.Print k.a, k.b, k.c

End Sub

and result to: " 20 12,5135 Sample"

When I try this from python:

from ctypes import *

class rcDemo(Structure):
_fields_ = [
('a', c_int),
('b', c_float),
('c', c_char_p),
]

mydll = windll.LoadLibrary("release\DEMO1L.DLL")

rc = rcDemo()
rc.a = 2
rc.b = 3.
rc.c = "Test56789"

mydll.DEMO2L(byref(rc))

print rc.a
print rc.b
print ">" + rc.c + "<"

I got " 2 3.000000 ♀Å╣" from the debug print in
Fortran and

20
12.513502121
Traceback (most recent call last):
File "run_demo.py", line 21, in ?
print ">" + rc.c + "<"
ValueError: invalid string pointer 0x706D6153

from Python. When passing only the string in a argument list instead
of the structure and considering to pass the length of the string as
2nd argument I am be able to send this characters to the dll and
receive a changed value back.

Have anyone an idea or perhaps a solution ;-) doing this in a
structure?

Thank you!
Michael
 
G

Gabriel Genellina

Hi all,

I deal with the old problem passing characters from python to a
fortran dll build with CFV6.6c.
I reduced our complex structure to a simple one. Here is the Fortran
code:

SUBROUTINE DEMO2L(s)

C sample for calling CVF6.6c-DLLs from
C vb/vba/python with simple structure

!DEC$ ATTRIBUTES DLLEXPORT::DEMO2L

TYPE rcDEMO
INTEGER a
REAL b
CHARACTER c*9
END TYPE

TYPE(rcDEMO) :: s
C
WRITE(*,*) s.a, s.b, s.c
C sample calculation:
s.a = s.a*10
s.b = s.b**2.3
s.c = 'Sample'

RETURN
END

From VB the calling is very simple:

Declare Sub DEMO2L Lib "release\demo1l.dll" (ByRef k As rcDEMO)

Type rcDEMO
a As Long
b As Single
c As String * 9
End Type

Dim k As rcDEMO

Sub run()

k.a = 2
k.b = 3#
k.c = "Test"

Call DEMO2L(k)

Debug.Print k.a, k.b, k.c

End Sub

and result to: " 20 12,5135 Sample"

When I try this from python:

from ctypes import *

class rcDemo(Structure):
_fields_ = [
('a', c_int),
('b', c_float),
('c', c_char_p),

Try with ('c', c_char*9). You may have alignment issues with such odd
size, but in this case it doesnt matter so much as it's the last field in
the struct.
(If both Fortran and VB say "char*9", why did you choose a pointer here?)
 
M

Michael Schäfer

Gabriel,

works perfect - even in complex nested structures!
Thank you very much!
(If both Fortran and VB say "char*9", why did you choose a pointer here?)
I do not know this possibility. Could you please drop me a few lines?
 
G

Gabriel Genellina

Gabriel,

works perfect - even in complex nested structures!
Thank you very much!

I do not know this possibility. Could you please drop me a few lines?

(It's just what I posted and you said it worked)
In C, strings are usually represented as an array of characters
implicitely ending at the first NULL character. By example: `char foo[20]`
declares a string variable named foo with enough space for up to 19
characters (plus the final NULL character).
A very common way of refer to such strings is to pass a pointer to its
first character: `char *pfoo` declares a variable pfoo which can hold a
reference to a string variable allocated somewhere (this is the usual way
to pass a string argument to a function).
In ctypes terminology, the first type is declared as c_char*20, and the
second one is c_char_p.
In FORTRAN you'd use CHARACTER*20 FOO (or CHARACTER(20)::FOO) to declare a
string variable with up to 20 characters, right padded with spaces. The
appropiate way to declare this with ctypes is then to use c_char*20 (and
probably initialize it with spaces, and trim spaces on the right coming
from Fortran). c_char_p declares a pointer, but Fortran filled it as it
were an array of characters: the first 4 letters of "Sample", when read as
a little-endian integer, give 0x706D6153, the "invalid pointer" you got.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,065
Latest member
OrderGreenAcreCBD

Latest Threads

Top