Jump to content

Calling Fortran dll's (g95 compiler) from AutoIt


khimik
 Share

Recommended Posts

I have worked out how to create Fortran dll's using g95 compiler and call them from AutoIt. AutoIt is a fantastic language, but when it comes to handling large amount of information or complex maths, it is quite slow. Fortran, on the other hand, is very fast, and there are many available maths libraries. The combination of AutoIt and Fortran dll's provides the power over Windows of AutoIt and the speed of Fortran.

Here are the rules I found:

1) Fortran functions should be compiled by g95 as g95 -shared -o <function name>.dll <function name>.f95

2) G95 mangles function names by adding an underscore after the name, or two if the name already contains an underscore. Hence the function name called in DllCall should be appended with undescore(s). There are ways of avoiding this mangling (e.g., by using BIND( C) in Fortran) but it is not necessary.

3) Arguments passed TO the dll:

- int/float data can be passed as variables using ByRef method (e.g., "int*"). Alternatively they can be passed as pointers ("ptr") to DllStruct. Variables can probably be also passed as values if one uses VALUE attribute in Fortran but I have not tested this.

- strings can be passed as "str" or as pointers ("ptr") to DllStruct. In the latter case, the size of the DllStruct should be greater than the actual string by one character, as 0x00 is automatically added to the end of the string. In any case, for ALL strings passed to the dll, integer arguments with the length of the strings must be added to the end of the argument list.

4) All arguments received FROM the dll MUST be pointers to DllStruct.

5) Fortran function returns an array. First element in this array is the function return value, other elements are the arguments passed to the dll.

6) Use cdecl calling method in AutoIt, e.g., "int:cdecl". It is also possible to compile the Fortran dll with the -mrtd switch and use stdcall in AutoIt (e.g., "int"), but in one case I had an unexplained memory leak with this method. Also, if stdcall is used, then lengths of strings passed back from the dll should also be added to the end of the argument list.

I attach a simple self-explanatory working example including source and compiled dll. The example illustrates diffrent ways of passing arguments to/from the dll. The dll and AutoIt script must be in the same directory. Once the dll is loaded/cached (e.g., if you run AutoIt script more than once, or change the script to load the dll before initialising the timer), it works ca. 100 times faster than the equivalent AutoIt code on my computer. Hope somebody will find this useful.

AutoIt program:

$timer = TimerInit()

$r = 3.3

$i = DllStructCreate("int")
DllStructSetData($i, 1, 8); Set value to 8.

$str2 = DllStructCreate("char[16]"); 1 char longer as it will automatically be null-terminated
DllStructSetData($str2, 1, "string variable")

$str3 = DllStructCreate("char[65]")
$j = DllStructCreate("int")

; An underscore was added by g95 compiler to the function name (e.g., to make it "test_").
; Dll is called using cdecl method; 1st argument is passed ByRef, 2nd as a pointer, 3rd (str1) as a string,
; 4th (str2) as a pointer to a null-terminated string. Return parameters are pointers.
; Length of str1 and str2 is given at the end of the argument list
; Return value is $res_dll[0]
$res_dll = DllCall("test.dll", "int:cdecl", "test_", "float*", $r, "ptr", DllStructGetPtr($i), _
    "str", "string variable", "ptr", DllStructGetPtr($str2), "ptr", DllStructGetPtr($str3), _
    "ptr", DllStructGetPtr($j), "int", StringLen("string variable"), "int", DllStructGetSize($str2))
    
MsgBox(0,"","Dll return value: " & $res_dll[0] & @CRLF & "String returned: " & DllStructGetData($str3,1) & @CRLF & _
    "Integer returned: " & DllStructGetData($j, 1) & @CRLF & "Time taken: " & TimerDiff($timer))

$timer = TimerInit()
For $k = 1 To 100000; the same maths as in Fortran dll
    $p = 3.14*$k + $r
Next
MsgBox(0,"","Time taken: " & TimerDiff($timer))

Fortran dll code:

CODE
integer function test(r,i,str1,str2,str3,j)

implicit none

real, intent(in) :: r

integer, intent(in) :: i

character(len=*), intent(in) :: str1

character(len=*), intent(in) :: str2

character(len=65), intent(out) :: str3

integer, intent(out) :: j

integer :: k

real :: p

do k=1,100000

p=3.14*k+r !do some simple maths

end do

if (TRIM(str1)==TRIM(str2)) then

str3='equal strings'

else

str3='strings are different as str2 has 0x00 at the end'

end if

j=i+5

test=INT( r)

end function test

Attached file: test.dll, was compiled from above Fortran code with the following command: g95 -shared -o test.dll test.f95

test.dll

Link to comment
Share on other sites

  • 1 year later...
  • 2 months later...

This is really strange. I have written several specialised data treatment programs using this technique, and they run on at least 10 different computers here, mostly XP but we also had win2000 and even 98 if I remember it right. I just downloaded both programs and tried it and it worked. I had lots of dll errors until I figured out the right way but I never saw this error. Could you try it on another computer?

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...