Delphi 中的 Fortran DLL
Fortran DLL in Delphi
我正在尝试在 DLL 中编译一些非常古老的 Fortran 程序,以便能够将它们与 Delphi 一起使用。虽然 Fortran 代码不是很大(750-800 行),但它的结构非常复杂,有几十个 GOTO 命令,而且翻译起来并不容易(我试图用它制作一些有用的代码,但我失败了)。
虽然我是Fortran新手,在调用DLL方面经验不多,但我逐渐克服了所有困难,但有一个困难,那就是能够调用具有多个动态数组的Fortran Subroutine。这是我创建的一个简单示例:
SUBROUTINE MYSUB1( NoEquations, INTARR1 )
!DEC$ ATTRIBUTES DLLEXPORT::MYSUB1
!DEC$ ATTRIBUTES C, REFERENCE, ALIAS:'MYSUB1' :: MYSUB1
C
C***************************************************************
C
INTEGER NoEquations, I
INTEGER INTARR1(*)
C
C***************************************************************
C
DO 100, I=1,NoEquations
INTARR1(I) = I
100 CONTINUE
RETURN
C
END
SUBROUTINE MYSUB2( NoEquations, INTARR1, INTARR2 )
!DEC$ ATTRIBUTES DLLEXPORT::MYSUB2
!DEC$ ATTRIBUTES C, REFERENCE, ALIAS:'MYSUB2' :: MYSUB2
C
C***************************************************************
C
INTEGER NoEquations, I
INTEGER INTARR1(*)
INTEGER INTARR2(*)
C
C***************************************************************
C
DO 100, I=1,NoEquations
INTARR2(I) = INTARR1(I)
100 CONTINUE
RETURN
C
END
我使用以下命令使用 mingw-w64 编译 Fortran 代码:
gfortran -shared -mrtd -fno-underscoring -o simple.dll simple.f
然后我从 Delphi 中声明程序:
procedure mysub1(var NoEquations: integer; var INTARR1 : array of integer); stdcall; external 'simple.dll';
procedure mysub2(var NoEquations: integer; var INTARR1,INTARR2: array of integer); stdcall; external 'simple.dll';
Delphi proram 编译正确,但当我 运行 它时,mysub1 正常工作并更新 INTARR1,但 mysub2 给我一个访问冲突。显然,第二个动态数组混淆了编译器,但我不知道如何让它理解。
提前致谢
我不知道 Delphi,但您可以按照以下步骤创建可从 C 语言访问的 DLL。我希望你觉得这对你有帮助。这是您的 F77 代码经过修改后可通过 Fortran 的 iso_c_binding
模块功能进行互操作:
SUBROUTINE MYSUB1(NoEquations,INTARR1) bind(C,name="MYSUB1")
!DEC$ ATTRIBUTES DLLEXPORT :: MYSUB1
use, intrinsic :: iso_c_binding, only: IK => c_int32_t
integer(IK), intent(in), value :: NoEquations
integer(IK), intent(out) :: INTARR1(NoEquations)
integer :: I
DO 100, I=1,NoEquations
INTARR1(I) = I
100 CONTINUE
RETURN
END SUBROUTINE MYSUB1
C***************************************************************
C***************************************************************
SUBROUTINE MYSUB2(NoEquations,INTARR1,INTARR2)
+bind(C,name="MYSUB2")
!DEC$ ATTRIBUTES DLLEXPORT :: MYSUB2
use, intrinsic :: iso_c_binding, only: IK => c_int32_t
integer(IK), intent(in), value :: NoEquations
integer(IK), intent(in) :: INTARR1(NoEquations)
integer(IK), intent(out) :: INTARR2(NoEquations)
integer :: I
DO 100, I=1,NoEquations
INTARR2(I) = INTARR1(I)
100 CONTINUE
RETURN
END SUBROUTINE MYSUB2
请注意我对您的代码所做的许多微妙但重要的更改,以使其与 C:
互操作
bind(C,name="MYSUB1")
修复了子例程的名称,因此您不需要额外的第二个编译器指令(我现在已从您的代码中删除)。
- 另外,请注意
value
属性,它告诉编译器像在 C 中所做的那样按值传递。
- 另请注意,我将子程序接口中的整数类型定义为
c_int32_t
以与 C 处理器的整数类型兼容。
- 此外,请注意,我已将您的 assumed-size
INTARR1
和 INTARR2
数组转换为显式数组 INTARR1(NoEquations)
,INTARR1(NoEquations)
.
由于您的代码中有英特尔 !DEC$
编译器指令,我假设您在 Windows 上使用英特尔 Fortran 编译器。请注意,我删除了两个 Fortran 子例程中的第二行指令。
现在,假设您将上述代码存储在名为 mysubs.F
的文件中,然后在 Intel-Windows 命令提示符下通过 Intel Fortran 编译器 ifort
编译此文件在以下命令中,
ifort mysubs.F /dll /out:libsubs
会在当前文件夹中生成一个名为mysubs.dll
的DLL,并在屏幕上打印如下信息,
Intel(R) Visual Fortran Intel(R) 64 Compiler for applications running on Intel(R) 64, Version 19.1.1.216 Build 20200306
Copyright (C) 1985-2020 Intel Corporation. All rights reserved.
Microsoft (R) Incremental Linker Version 14.16.27027.1
Copyright (C) Microsoft Corporation. All rights reserved.
-out:mysubs.dll
-dll
-implib:mysubs.lib
mysubs.obj
Creating library mysubs.lib and object mysubs.exp
要测试此 DLL,您可以尝试存储在 main.c
、
中的以下 C 代码
#include <stdio.h>
#include <stdint.h>
#include <string.h>
void MYSUB1(int32_t, int32_t []);
void MYSUB2(int32_t, int32_t [], int32_t []);
int main(int argc, char *argv[])
{
const int32_t NoEquations = 5;
int32_t INTARR1[NoEquations];
int32_t INTARR2[NoEquations];
int loop;
// C rules for argument passing apply here
MYSUB1(NoEquations,INTARR1);
printf("\nINTARR1:\n"); for(loop = 0; loop < NoEquations; loop++) printf("%d ", INTARR1[loop]);
MYSUB2(NoEquations,INTARR1,INTARR2);
printf("\nINTARR2:\n"); for(loop = 0; loop < NoEquations; loop++) printf("%d ", INTARR2[loop]);
return 0;
}
使用英特尔 C 编译器编译此 C 代码,
icl main.c -c
在屏幕上打印以下内容,
Intel(R) C++ Intel(R) 64 Compiler for applications running on Intel(R) 64, Version 19.1.1.216 Build 20200306
Copyright (C) 1985-2020 Intel Corporation. All rights reserved.
main.c
并生成 C 主目标文件。最后,link用Fortran DLL库生成可执行文件的C目标文件,
icl main.obj mysubs.lib -o main.exe
在屏幕上打印以下内容,
Intel(R) C++ Intel(R) 64 Compiler for applications running on Intel(R) 64, Version 19.1.1.216 Build 20200306
Copyright (C) 1985-2020 Intel Corporation. All rights reserved.
Microsoft (R) Incremental Linker Version 14.16.27027.1
Copyright (C) Microsoft Corporation. All rights reserved.
-out:main.exe
main.obj
mysubs.lib
正在测试 DLL 调用。只需调用生成的可执行文件 main.exe
,
main.exe
打印在屏幕上,
INTARR1:
1 2 3 4 5
INTARR2:
1 2 3 4 5
现在,要从 Delphi 调用此 DLL,只需假设您正在使用 C 主代码中指定的原型调用 C 函数。就这样。不再从 Delphi.
内部处理 Fortran
最终建议:
Fortran 具有强大的标准互操作性功能,就像我添加到您的 F77 代码中的功能一样,可以轻松地将几乎所有 Fortran 代码连接到任何语言(通过 C)。
远离快半个世纪的FORTRAN77,Fortran 90也已经有3年多的历史了。最新的 Fortran 标准于 2018 年发布,与 Fortran 2008 一起使 Fortran 成为一种极其强大、高级、快速、原生矢量化、并发、共享和分布式的数值计算并行编程语言。
我正在尝试在 DLL 中编译一些非常古老的 Fortran 程序,以便能够将它们与 Delphi 一起使用。虽然 Fortran 代码不是很大(750-800 行),但它的结构非常复杂,有几十个 GOTO 命令,而且翻译起来并不容易(我试图用它制作一些有用的代码,但我失败了)。 虽然我是Fortran新手,在调用DLL方面经验不多,但我逐渐克服了所有困难,但有一个困难,那就是能够调用具有多个动态数组的Fortran Subroutine。这是我创建的一个简单示例:
SUBROUTINE MYSUB1( NoEquations, INTARR1 )
!DEC$ ATTRIBUTES DLLEXPORT::MYSUB1
!DEC$ ATTRIBUTES C, REFERENCE, ALIAS:'MYSUB1' :: MYSUB1
C
C***************************************************************
C
INTEGER NoEquations, I
INTEGER INTARR1(*)
C
C***************************************************************
C
DO 100, I=1,NoEquations
INTARR1(I) = I
100 CONTINUE
RETURN
C
END
SUBROUTINE MYSUB2( NoEquations, INTARR1, INTARR2 )
!DEC$ ATTRIBUTES DLLEXPORT::MYSUB2
!DEC$ ATTRIBUTES C, REFERENCE, ALIAS:'MYSUB2' :: MYSUB2
C
C***************************************************************
C
INTEGER NoEquations, I
INTEGER INTARR1(*)
INTEGER INTARR2(*)
C
C***************************************************************
C
DO 100, I=1,NoEquations
INTARR2(I) = INTARR1(I)
100 CONTINUE
RETURN
C
END
我使用以下命令使用 mingw-w64 编译 Fortran 代码:
gfortran -shared -mrtd -fno-underscoring -o simple.dll simple.f
然后我从 Delphi 中声明程序:
procedure mysub1(var NoEquations: integer; var INTARR1 : array of integer); stdcall; external 'simple.dll';
procedure mysub2(var NoEquations: integer; var INTARR1,INTARR2: array of integer); stdcall; external 'simple.dll';
Delphi proram 编译正确,但当我 运行 它时,mysub1 正常工作并更新 INTARR1,但 mysub2 给我一个访问冲突。显然,第二个动态数组混淆了编译器,但我不知道如何让它理解。 提前致谢
我不知道 Delphi,但您可以按照以下步骤创建可从 C 语言访问的 DLL。我希望你觉得这对你有帮助。这是您的 F77 代码经过修改后可通过 Fortran 的 iso_c_binding
模块功能进行互操作:
SUBROUTINE MYSUB1(NoEquations,INTARR1) bind(C,name="MYSUB1")
!DEC$ ATTRIBUTES DLLEXPORT :: MYSUB1
use, intrinsic :: iso_c_binding, only: IK => c_int32_t
integer(IK), intent(in), value :: NoEquations
integer(IK), intent(out) :: INTARR1(NoEquations)
integer :: I
DO 100, I=1,NoEquations
INTARR1(I) = I
100 CONTINUE
RETURN
END SUBROUTINE MYSUB1
C***************************************************************
C***************************************************************
SUBROUTINE MYSUB2(NoEquations,INTARR1,INTARR2)
+bind(C,name="MYSUB2")
!DEC$ ATTRIBUTES DLLEXPORT :: MYSUB2
use, intrinsic :: iso_c_binding, only: IK => c_int32_t
integer(IK), intent(in), value :: NoEquations
integer(IK), intent(in) :: INTARR1(NoEquations)
integer(IK), intent(out) :: INTARR2(NoEquations)
integer :: I
DO 100, I=1,NoEquations
INTARR2(I) = INTARR1(I)
100 CONTINUE
RETURN
END SUBROUTINE MYSUB2
请注意我对您的代码所做的许多微妙但重要的更改,以使其与 C:
互操作bind(C,name="MYSUB1")
修复了子例程的名称,因此您不需要额外的第二个编译器指令(我现在已从您的代码中删除)。- 另外,请注意
value
属性,它告诉编译器像在 C 中所做的那样按值传递。 - 另请注意,我将子程序接口中的整数类型定义为
c_int32_t
以与 C 处理器的整数类型兼容。 - 此外,请注意,我已将您的 assumed-size
INTARR1
和INTARR2
数组转换为显式数组INTARR1(NoEquations)
,INTARR1(NoEquations)
.
由于您的代码中有英特尔 !DEC$
编译器指令,我假设您在 Windows 上使用英特尔 Fortran 编译器。请注意,我删除了两个 Fortran 子例程中的第二行指令。
现在,假设您将上述代码存储在名为 mysubs.F
的文件中,然后在 Intel-Windows 命令提示符下通过 Intel Fortran 编译器 ifort
编译此文件在以下命令中,
ifort mysubs.F /dll /out:libsubs
会在当前文件夹中生成一个名为mysubs.dll
的DLL,并在屏幕上打印如下信息,
Intel(R) Visual Fortran Intel(R) 64 Compiler for applications running on Intel(R) 64, Version 19.1.1.216 Build 20200306
Copyright (C) 1985-2020 Intel Corporation. All rights reserved.
Microsoft (R) Incremental Linker Version 14.16.27027.1
Copyright (C) Microsoft Corporation. All rights reserved.
-out:mysubs.dll
-dll
-implib:mysubs.lib
mysubs.obj
Creating library mysubs.lib and object mysubs.exp
要测试此 DLL,您可以尝试存储在 main.c
、
#include <stdio.h>
#include <stdint.h>
#include <string.h>
void MYSUB1(int32_t, int32_t []);
void MYSUB2(int32_t, int32_t [], int32_t []);
int main(int argc, char *argv[])
{
const int32_t NoEquations = 5;
int32_t INTARR1[NoEquations];
int32_t INTARR2[NoEquations];
int loop;
// C rules for argument passing apply here
MYSUB1(NoEquations,INTARR1);
printf("\nINTARR1:\n"); for(loop = 0; loop < NoEquations; loop++) printf("%d ", INTARR1[loop]);
MYSUB2(NoEquations,INTARR1,INTARR2);
printf("\nINTARR2:\n"); for(loop = 0; loop < NoEquations; loop++) printf("%d ", INTARR2[loop]);
return 0;
}
使用英特尔 C 编译器编译此 C 代码,
icl main.c -c
在屏幕上打印以下内容,
Intel(R) C++ Intel(R) 64 Compiler for applications running on Intel(R) 64, Version 19.1.1.216 Build 20200306
Copyright (C) 1985-2020 Intel Corporation. All rights reserved.
main.c
并生成 C 主目标文件。最后,link用Fortran DLL库生成可执行文件的C目标文件,
icl main.obj mysubs.lib -o main.exe
在屏幕上打印以下内容,
Intel(R) C++ Intel(R) 64 Compiler for applications running on Intel(R) 64, Version 19.1.1.216 Build 20200306
Copyright (C) 1985-2020 Intel Corporation. All rights reserved.
Microsoft (R) Incremental Linker Version 14.16.27027.1
Copyright (C) Microsoft Corporation. All rights reserved.
-out:main.exe
main.obj
mysubs.lib
正在测试 DLL 调用。只需调用生成的可执行文件 main.exe
,
main.exe
打印在屏幕上,
INTARR1:
1 2 3 4 5
INTARR2:
1 2 3 4 5
现在,要从 Delphi 调用此 DLL,只需假设您正在使用 C 主代码中指定的原型调用 C 函数。就这样。不再从 Delphi.
内部处理 Fortran最终建议:
Fortran 具有强大的标准互操作性功能,就像我添加到您的 F77 代码中的功能一样,可以轻松地将几乎所有 Fortran 代码连接到任何语言(通过 C)。
远离快半个世纪的FORTRAN77,Fortran 90也已经有3年多的历史了。最新的 Fortran 标准于 2018 年发布,与 Fortran 2008 一起使 Fortran 成为一种极其强大、高级、快速、原生矢量化、并发、共享和分布式的数值计算并行编程语言。