Jump to content

Recommended Posts

Posted

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

  • 1 year later...
Posted

I downloaded test.dll and created autoit script "as-is" in the same directory, but something does not work.

@error value after DllCall function is "2" (unknown "return type"). ;)

  • 2 months later...
Posted

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?

Posted

Wow, this has quite a big speed increase, takes around 1.6ms for the DLL to do the math, and about 185ms for AutoIt on this machine, very nice :mellow:

Posted

it took 1.45ms for the dll and almost 192ms for autoit. Wow! That's something. Thank you very much.

[Not using this account any more. Using "iShafayet" instead]

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
  • Recently Browsing   0 members

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