Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

C dll called from Fortran dll called from VB

Kipling__Michael
Beginner
763 Views

I have successfully called a C routine in a dll from Fortran and a Fortran routine in a dll called from VB many times.  This is the first time I've tried using VB to call Fortran to call C. 

The simple test routines below try to pass an integer from a Fortran routine called by VB to a C routine.

The value being passed = 12, but when it reaches the C routine, the value = 1239536. 

Suggestions?

VB 2010 code:
   Declare Sub TEST_FTN Lib "LHM_Fsrc.dll" ()

   Call TEST_FTN()
   
  
FTN code:
  SUBROUTINE TEST_FTN()  
  
  !DEC$ ATTRIBUTES DLLEXPORT, STDCALL :: TEST_FTN
  !DEC$ ATTRIBUTES ALIAS:'TEST_FTN' :: TEST_FTN
  
  IMPLICIT NONE
  
  INTERFACE
     SUBROUTINE Test_Int(i1) 
        !DEC$ ATTRIBUTES DLLIMPORT :: Test_Int
        !DEC$ ATTRIBUTES DECORATE, ALIAS:'Test_Int' :: Test_Int
        INTEGER :: i1
     END SUBROUTINE Test_Int
  END INTERFACE
  
  INTEGER :: i1
  
  i1 = 12
  
  CALL Test_Int(i1)
  
  RETURN
  END SUBROUTINE TEST_FTN

C code:
  __declspec(dllexport) void Test_Int(long i1);
  
  void Test_Int(long i1)
  {
     long i2;
     i2 = i1;
  }


Regards, Mike

0 Kudos
6 Replies
Steven_L_Intel1
Employee
763 Views

You've told C that i1 is being passed by value, but Fortran thinks it is by refrerence. You could fix this by any of the following (in my decreasing order of preference):

1. Change the Fortran declaration of Test_Int to use the C interoperability features and the VALUE attribute:

 INTERFACE
     SUBROUTINE Test_Int(i1)  BIND(C,NAME="Test_Int")
        !DEC$ ATTRIBUTES DLLIMPORT :: Test_Int
        INTEGER, VALUE :: i1
     END SUBROUTINE Test_Int
  END INTERFACE

2. Add the following directive to the interface body:

!DEC$ ATTRIBUTES VALUE :: i1

I want to point out that the VALUE attribute in 1 above, if you didn't have BIND(C), is not equivalent to ATTRIBUTES VALUE. With BIND(C), it is - at least in this case.

3. Change the C code to accept the argument by reference (*i1 in the prototype) and dereference it in the code.

0 Kudos
Kipling__Michael
Beginner
763 Views

Hi Steve,

Following your #1 preference works for me.

How would you change the following Fortran code which passes a character string to a C routine, or would you?

 Fortran code:  
   SUBROUTINE TEST_FTN(choice, fname_)                                                 
                                                                                    
   !DEC$ ATTRIBUTES DLLEXPORT, STDCALL :: TEST_FTN                                     
   !DEC$ ATTRIBUTES ALIAS:'TEST_FTN' :: TEST_FTN                                       
   !DEC$ ATTRIBUTES REFERENCE :: fname_                                                
                                                                                    
   IMPLICIT NONE                                                                       
                                                                                    
   INTERFACE                                                                           
      SUBROUTINE C_Routine(iChoice, FileName)                               
         !DEC$ ATTRIBUTES DLLIMPORT :: C_Routine                            
         !DEC$ ATTRIBUTES DECORATE, ALIAS:'C_Routine' :: C_Routine
                                                                                       
         INTEGER, VALUE     :: iChoice                                                 
         CHARACTER(LEN=256) :: FileName                                                
                                                                                       
         !DEC$ ATTRIBUTES REFERENCE :: FileName                                        
      END SUBROUTINE C_Routine                                              
   END INTERFACE                                                                       
                                                                                    
   !Type call list variables                                                           
   INTEGER,            INTENT(IN)  :: choice                                           
   CHARACTER(LEN=256), INTENT(IN)  :: fname_                                           
                                                                                    
   ! Type local variables                                                              
   CHARACTER(LEN=256) :: fname                                                         
   INTEGER            :: i                                                             
                                                                                    
   fname = ' '                                                                         
                                                                                    
   fname(1:255) = fname_(1:255)                                                       
                                                                                    
   i = LEN_TRIM(fname)                                                                 
                                                                                    
   fname(i+1:i+1) = CHAR(0)                                                            
                                                                                    
   CALL C_Routine(choice, fname)                                            
                                                                                    
   RETURN                                                                              
   END SUBROUTINE TEST_FTN                                                              
   
 C code:
   __declspec(dllimport) void C_Routine (int iChoice, char *FileName);

 void C_Routine(int iChoice, char *FileName)
 {
  ....
 }

Regards, Mike

 

0 Kudos
Steven_L_Intel1
Employee
763 Views

  INTERFACE                                                                           
      SUBROUTINE C_Routine(iChoice, FileName) BIND(C,NAME="C_Routine")
         USE, INTRINSIC :: ISO_C_BINDING                        
         !DEC$ ATTRIBUTES DLLIMPORT :: C_Routine                            
                                                                                       
         INTEGER(C_INT), VALUE     :: iChoice                                                 
         CHARACTER, DIMENSION(*) :: FileName                                                
                                                                                                                           
      END SUBROUTINE C_Routine                                              
   END INTERFACE                 

The standard makes an exception in this case for the rule about passing a scalar to an array.

0 Kudos
Kipling__Michael
Beginner
763 Views

I'll need to study that a bit.

Thanks Steve

0 Kudos
SergeyKostrov
Valued Contributor II
763 Views
>>...I have successfully called a C routine in a dll from Fortran and a Fortran routine in a dll called from VB many times... Hi Mike, I wonder if you will be able to upload small Visual Studio project? I think it iwll be a very valuable contribution on the Fortran forum!
0 Kudos
Kipling__Michael
Beginner
763 Views

Yes, I'll build one and post it Sergey

0 Kudos
Reply