简体   繁体   English

从 Fortran 拨打具体电话 C++ DLL

[英]Call specific C++ DLL from Fortran

I received a DLL made in C++, and I'm building a Fortran program to call the C++ DLL. My compiler (gfortran) shows no warnings, but it crashes during runtime with the following description:我收到了 C++ 中的 DLL,我正在构建一个 Fortran 程序来调用 C++ DLL。我的编译器 (gfortran) 没有显示任何警告,但它在运行时崩溃,并显示以下描述:

    forrtl: severe (157): Program Exception - access violation
    Image              PC        Routine            Line        Source
    MainDLL_v10.dll    0B7A6B01  Unknown               Unknown  Unknown
    MainDLL_v10.dll    0B7A1BEF  Unknown               Unknown  Unknown
    ...

I guess something is wrong with my call arguments. The C++ DLL includes the following:我想我的电话 arguments 有问题。C++ DLL 包括以下内容:

    #include <DllClasses.h>
   ...
   extern "C"
   {
   ...
     __declspec(dllexport) RESULT __cdecl Initialize(char *DllNames[], int NumberOfDlls, char *InputFile,  char *OutputFile, char *CtrlVersion, int InitState)
     {
     ...
     }
   ...
   } // extern C

My Fortran program is written like this.我的Fortran程序是这样写的。 Here is a part of my code before CONTAINS.这是 CONTAINS 之前我的代码的一部分。

    ABSTRACT INTERFACE
        SUBROUTINE Initialize(PDLLNames, NumberOfDLLs, PInputfile, Poutputfile, CtrlVersion, InitState) BIND(C)
            USE, INTRINSIC :: ISO_C_Binding
            IMPLICIT NONE
            !DEC$ ATTRIBUTES C :: Initialize
            CHARACTER(KIND=C_CHAR), INTENT(IN   ), DIMENSION(9) :: PDLLNames    
            INTEGER(C_INT),         INTENT(IN   )               :: NumberOfDLLs   
            CHARACTER(KIND=C_CHAR), INTENT(IN   )               :: PInputfile  
            CHARACTER(KIND=C_CHAR), INTENT(INOUT)               :: Poutputfile 
            CHARACTER(KIND=C_CHAR), INTENT(IN   )               :: CtrlVersion 
            INTEGER(C_INT),         INTENT(IN   )               :: InitState
        END SUBROUTINE Initialize
        SUBROUTINE MainDll(InputSignals, OutputSignals, PErrorMessage) BIND(C)
            USE, INTRINSIC :: ISO_C_Binding
            IMPLICIT NONE
            !DEC$ ATTRIBUTES C :: MainDll
            REAL(C_DOUBLE),          INTENT(IN   )   :: InputSignals   (*) 
            REAL(C_DOUBLE),          INTENT(  OUT)   :: OutputSignals  (*)
            CHARACTER(KIND=C_CHAR),  INTENT(  OUT)   :: PErrorMessage  (*)
        END SUBROUTINE MainDll
    END INTERFACE

And here is a part of my Fortran code in a procedure.这是程序中我的 Fortran 代码的一部分。

        ! Variables for dll interface
        PROCEDURE(Initialize), BIND(C), POINTER                 :: Initialize_proc
        INTEGER(C_INT)                                          :: NumberOfDLLs=9, InitState
        CHARACTER(KIND=C_CHAR, LEN=56), TARGET                  :: MainDll, DLLInputfile, DLLOutputfile, StateControllerName  
        CHARACTER(KIND=C_CHAR, LEN=56), TARGET, DIMENSION(9)    :: DLLname
        CHARACTER(KIND=C_CHAR, LEN=56), POINTER                 :: PoInputfile, PoOutputfile, PoStateControllerName    
        CHARACTER(KIND=C_CHAR, LEN=56), POINTER, DIMENSION(9)   :: PoDLLname(:)           
        PoInputfile => DLLInputfile
        PoOutputfile => DLLOutputfile
        PoStateControllerName => StateControllerName
        PoDLLname(1:) => DLLname(1:9)
    ...    
        ! Load DLL
        module_handle = LoadLibrary(MainDll // C_NULL_CHAR)
        proc_address = GetProcAddress( module_handle, C_CHAR_'Initialize' // C_NULL_CHAR )
        ! Call Initialize function in DLL
        CALL C_F_PROCPOINTER(proc_address, Initialize_proc)        
        CALL Initialize_proc(PoDLLname, NumberOfDLLs, PoInputfile, PoOutputfile, PoStateControllerName, InitState)

The characteristics of your C functions and that described by the associated Fortran interface bodies do not match.您的 C 函数的特征与关联的 Fortran 接口体描述的特征不匹配。

When an interoperable procedure is called (one that has BIND(C)) in Fortran, scalar arguments without the VALUE attribute are passed by reference to the C++ function. If you want the argument to be passed by value, you need to add the VALUE attribute on the Fortran side.当在 Fortran 中调用可互操作过程(具有 BIND(C) 的过程)时,不带 VALUE 属性的标量 arguments 通过引用传递给 C++ function。如果要按值传递参数,则需要添加 VALUE Fortran 端的属性。

For example, in the C++ fragment:例如,在 C++ 片段中:

__declspec(dllexport) RESULT __cdecl Initialize(... int NumberOfDlls

NumberOfDlls is pass by value, but the Fortran shows: NumberOfDlls 是按值传递的,但是 Fortran 显示:

SUBROUTINE Initialize(... NumberOfDLLs, ...) BIND(C)
  ...
  INTEGER(C_INT), INTENT(IN) :: NumberOfDLLs   

No value attribute - the Fortran argument corresponds to a C++ parameter of int *NumberOfDlls .无值属性 - Fortran 参数对应于int *NumberOfDlls的 C++ 参数。 For pass by value, use a Fortran argument declaration of:对于按值传递,使用 Fortran 参数声明:

  INTEGER(C_INT), INTENT(IN), VALUE :: NumberOfDLLs   

Your Fortran interface also includes compiler directives ( .DEC$ ATTRIBUTES... ) for a family of compilers (represented today by Intel Fortran) other than gfortran.您的 Fortran 接口还包括编译器指令 ( .DEC$ ATTRIBUTES... ),用于除 gfortran 以外的一系列编译器(今天由 Intel Fortran 表示)。 That directive would change the behaviour of the arguments on the Fortran side for that family of compilers, possibly in a way that matches the C function. But gfortran does not understand that directive - it considers it to be just a comment.该指令将改变 arguments 在 Fortran 端对于该系列编译器的行为,可能以与 C function 相匹配的方式。但 gfortran 不理解该指令 - 它认为它只是一个注释。

gfortran has its own equivalent directives, but the use of directives like that reflects a time when the language standard had no C interoperability support. gfortran 有自己的等效指令,但使用这样的指令反映了语言标准没有 C 互操作性支持的时间。

Since compilers started supporting Fortran 2003, you are better off using the standard language features to manage interoperability - add VALUE to the arguments that need it (InitState too), and delete the directives.自从编译器开始支持 Fortran 2003 以来,您最好使用标准语言功能来管理互操作性——将 VALUE 添加到需要它的 arguments(也包括 InitState),然后删除指令。

Not shown in your question are the specifics around the Windows API's LoadLibrary and GetProcAddress.您的问题中未显示有关 Windows API 的 LoadLibrary 和 GetProcAddress 的细节。 I am assuming they have been described correctly for all compilers that might be used.我假设它们已针对可能使用的所有编译器进行了正确描述。

There is a suggestion in the way the arguments are being prepared in Fortran (lots of pointers being used - why??) of other misunderstandings around the requirements for calling C code.在 Fortran 中准备 arguments 的方式中有一个建议(使用了很多指针 - 为什么??)关于调用 C 代码的要求的其他误解。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM