Silverfrost Logo About Us | Contact Us
 

Visual C Interoperability - Calling an FTN95 DLL from Visual C++

Topics Covered

Calling a Win32 DLL from unmanaged Visual C++
Exporting routines from a Win32 Fortran DLL
Debugging a FTN95 Win32 DLL within Visual Studio .NET

Introduction

Default Location:

<Documents Folder>\FTN95 Examples\Win32\VisualCInteroperability

A set of shortcuts has also been provided in the start menu.

This example demonstrates the use of an FTN95 Win32 DLL called from unmanaged Visual C++. This examples requires the use of the Microsoft Visual C compiler, cl.exe. This is installed by Visual Studio .NET. To build this example the solution file can be loaded (VisualCInteroperability.sln) or the project can be built from the command line by using the supplied batch files present in the solution folder. To compile this application from the command line you should ensure that the Visual C++ compiler is available for use. This document refers to the creation of this project using Microsoft Visual Studio .NET, for command line usage much of this document applies, but please see the batch files for command syntax details. The Visual Studio .NET environment batch file vsvars32.bat should have been run for command line usage.

This sample assumes the __cdecl calling convention is used.

For an example of calling a Visual C++ Win32 DLL from Silverfrost Fortran see the VisualCInteroperability2 example.

Example Creation

This solution makes use of two language projects, an FTN95 Application Extension and a Visual C++ Win32 Console Application.

To create this solution two projects need to be created. First create the FTN95 Application Extension solution by selecting New Project from the File menu and choose a FTN95 Application Extension project. Name the project as desired or accept the default. When the project has loaded the configuration should be changed from .NET to Win32 if this is not already set. This can be done by selecting the project type combo box in the toolbar within Visual Studio .NET.

Enter the Fortran code as shown in the Code Features section below.

Select Properties from the Project menu. When creating a Fortran Application Extension project the default file type produced by a build is a DLL. As this project is to be used with Microsoft Visual C++ a .lib file has to be produced. In the project properties dialog, navigate to Compiler Options->Miscellaneous. In the Output Options section, select Output filetype and change the extension, using the dropdown available, to LIB as shown in the following image.

Select Linker Options in the same dialog and set Export all to Yes. This ensures that any routines that are included in the library produced by the project will be exported and callable from Visual C++. If only select routines need to be exported then a custom link script can be passed to the linker by creating a script and passing the script to the linker by specifying the script file in Extra linker options. A short description of how to do this for this project is shown below.

Using a Custom Link Script

If more options are required to be passed to the linker than are provided in the project configuration dialog then a custom link script can be provided. A blank text file should be created in the project directory and named as appropriate. For this example the file will be called extras.slink. To export specific routines from a DLL instead of all routines by use of Export all then specific Export commands can be passed to the linker. To export four specific routines from the code below the following linker script could be used:

export SUBSIMPLE
export SUBARGUMENTS
export SUBSTRING
export SUBARRAY

These exported routines can be accessed from the C program in the same way as with the use of Export all.

It is possible to provide alternate export names for the routines by the use of the following syntax within the script file:

export SUBSIMPLE=_SubSimple

This routine is then exported as _SubSimple and should be treated as such within the C program.

To specify the script file to the linker, the name of the script file should be prefixed with '$' and added to Linker Options->Extra linker options as shown in the following image. If specific exports are used as shown then Export all should also be set to No. Note that if the linker script is not resident within the project directory then a full path or a path relative to the project directory should be specified.

For more information about custom link scripts and the commands that can be used with them please see the following topics.

Script or command files
Interactive mode

Example Creation (continued)

When the Fortran project has been created, select Build from the Build menu. In the project output directory two files, a .lib and a .dll should have been created. The .lib file will be used by the Visual C++ project at build time, and the DLL will be used at runtime.

To create the Visual C++ project, select New Project from the File menu and select a Win32 Console Project from Visual C++ Projects. Name the project as appropriate and create the project. You should add the new project to the existing solution.

Enter the C code as shown in the Code Features section below.

Two changes need to be made to the Visual C++ project settings. Select Properties from the Project menu for the C++ project. Navigate to Linker->Input and select Additional Dependencies. The .lib file created by the Fortran project should be specified here. A path should be specified, using either a full or relative path. For this example the Fortran Application Extension and Visual C++ Project are in seperate parallel directories and therefore (in Debug/Win32 configuration) the path to the .lib file would be:

..\FortranApplicationExtension1\Debug\Win32\FortranApplicationExtension1.lib

Now we have ensured that the Visual C++ program has enough information to compile and link with the Fortran Application Extension, we need to ensure that the Fortran DLL is available for the Visual C++ program at runtime. This is accomplished by use of a Post-Build Event. When the Visual C++ program runs the Fortran Application Extension DLL needs to be available either in a local directory or on the system path. To make the DLL available locally a custom event can be used to copy the DLL from the Fortran output directory to the Visual C++ output directory. Still within the project properties navigate to Build Events->Post-Build Event and select Command Line. Enter the following command:

copy ..\FortranApplicationExtension1\Debug\Win32\FortranApplicationExtension1.dll $(OutDir)

Ensure that the solution build order is configured so that the Fortran Application Extension is built before the Visual C++ project. The entire solution can now be built. The solution should also be runnable from the Debug menu. Ensure that the Visual C++ project is set as the startup project.

Code Features

This example demonstrates calling an FTN95 from Visual C++. A variety of routines are called, with different arguments and return types, array passing and function pointers are demonstrated. Explanations are provided after each code snippet below.

Fortran Source

!---------------------------------------------------------------
! No arguments
!---------------------------------------------------------------
SUBROUTINE SubSimple
PRINT *,"Simple subroutine called"
END SUBROUTINE

!---------------------------------------------------------------
! Simple Integer and Real arguments
!---------------------------------------------------------------
SUBROUTINE SubArguments(i,x)
INTEGER(KIND=3) :: i
REAL(KIND=1) :: x
PRINT *,"SubArguments called with values:"
PRINT *,"i = ",i
PRINT *,"x = ",x
END SUBROUTINE

!---------------------------------------------------------------
! String arguments
!---------------------------------------------------------------
SUBROUTINE SubString(s1,s2)
CHARACTER(LEN=*) :: s1
CHARACTER(LEN=*) :: s2
PRINT *,"SubString called with two strings"
PRINT *,s1,s2
END SUBROUTINE

The above are three simple Fortran subroutines that accept different arguments. When passing the strings to the Fortran routines, as demonstrated by the last example, the lengths of the two strings are passed as extra arguments to the Fortran, see the C code for full details of this.

!---------------------------------------------------------------
! Array arguments
!---------------------------------------------------------------
SUBROUTINE SubArray(arr)
INTEGER(KIND=3) :: arr(:)
PRINT *,"SubArray called with array"
PRINT *,ARR
END SUBROUTINE

!---------------------------------------------------------------
! Call a routine with a function pointer from C
!---------------------------------------------------------------
SUBROUTINE SubFunc(func)
EXTERNAL :: func
CALL func(34)
END SUBROUTINE

The first of these two routines accepts an array from the C code. The size of the array is passed, by value from the C as an extra argument. The second subroutine illustrates the passing of a function pointer as an argument. func is a function pointer passed from C and then called from within the Fortran code.

!---------------------------------------------------------------
! An Integer returning Function
!---------------------------------------------------------------
INTEGER(KIND=3) FUNCTION FuncInteger()
FuncInteger = 347
END FUNCTION

!---------------------------------------------------------------
! A Real returning Function
!---------------------------------------------------------------
REAL(KIND=1) FUNCTION FuncReal()
FuncReal = 2.46677
END FUNCTION

The code above shows two Fortran functions returning different types.

For details of type differences between Fortran and C see the following topic:

Basic data types

Visual C++ Code

//----------------------------------------------------------------------
// Declare the external Fortran routines
//----------------------------------------------------------------------
extern "C" void SUBSIMPLE();
extern "C" void SUBARGUMENTS(int* i, float* x);
extern "C" void SUBSTRING(char* s1, char* s2, int i, int j);
extern "C" void SUBARRAY(int* arr, int size);
extern "C" void SUBFUNC(void* p);
extern "C" int FUNCINTEGER();
extern "C" float FUNCREAL();

These declarations should be provided to specify the Fortran routines that are to be called. Two cirumstances should be considered. If Export all was specified from the Fortran then the names should match those in the Fortran code but should be all upper-case, regardless of the case within the Fortran code. If explicit export statements have been provided by a link script then the names should match those provided in the link script. extern "C" should be specified to prevent Visual C++ from modifying the names of the routines in any way.

//----------------------------------------------------------------------
// Constants
//----------------------------------------------------------------------
#define ARRAY_SIZE 10

//----------------------------------------------------------------------
// Calls an FTN95 subroutine with no arguments
//----------------------------------------------------------------------
void CallSubroutineSimple()
{
  SUBSIMPLE();
}

//----------------------------------------------------------------------
// Calls an FTN95 subroutine with simple (int and float) arguments
//----------------------------------------------------------------------
void CallSubroutineArguments()
{
  int i = 3;
  float x = 4.6;
 
SUBARGUMENTS(&i,&x);
}

//----------------------------------------------------------------------
// Calls an FTN95 subroutine with string arguments. The lengths of the
// two string arguments are passed by value as extra arguments.
//----------------------------------------------------------------------
void CallSubroutineString()
{
  char* chStr1 = "Hello ";
  char* chStr2 = "World...";
  SUBSTRING(chStr1,chStr2,strlen(chStr1),strlen(chStr2));
}

//----------------------------------------------------------------------
// Calls an FTN95 subroutine with an int array - note the size is passed
// as an extra argument in order to facilitate assumed size within the
// Fortran code. Extra arguments may be passed by value as shown, actual
// arguments must be passed by reference.
//----------------------------------------------------------------------
void CallSubroutineArray()
{
  int arr[ARRAY_SIZE] = {1,2,3,4,5,6,7,8,9,0};
  SUBARRAY(arr,ARRAY_SIZE);
}

The first routines above demonstrate calling FTN95 DLL subroutines with no arguments and with simple arguments. Note that arguments are always passed by reference.

The second two routines demonstrate calling subroutines that require extra arguments to be passed along with the actual arguments. These two subroutines accept assumed length character arguments and an assumed size array. The lengths/sizes of these arguments should be passed from the C as extra arguments on the end of the argument list, by value and in the order in which the arguments requiring extra arguments are passed.

//----------------------------------------------------------------------
// Calls an FTN95 function returning an int
//----------------------------------------------------------------------
void CallFuncInteger()
{
  int ret = FUNCINTEGER(); 
  printf("Return from FUNCINTEGER: %d\n",ret);
}

//----------------------------------------------------------------------
// Calls an FTN95 function returning a float
//----------------------------------------------------------------------
void CallFuncReal()
{
  float ret = FUNCREAL();
  printf("Return from FUNCREAL: %f\n",ret);
}

The above demonstrates calling functions in the Fortran DLL.

//----------------------------------------------------------------------
// Utility function used by CallSubroutineFunc
//----------------------------------------------------------------------
void FunctionPointer(int* i)
{
  printf("FunctionPointer called with i=%d\n",*i);
}

//----------------------------------------------------------------------
// Calls an FTN95 function with a function pointer argument
//----------------------------------------------------------------------
void CallSubroutineFunc()
{
  void (*pFunc) (int*);
  pFunc = FunctionPointer;
  SUBFUNC(pFunc);
}

The above routines are used to pass a function pointer to the Fortran. Within the Fortran the argument is specified as EXTERNAL.

Debugging an FTN95 DLL within Visual Studio .NET

Normally, to run this example the Visual C++ Project should be set as the startup project. To debug the Fortran DLL we need to go through extra steps as mixed language debugging with FTN95 and Visual C++ is only available in .NET configuration.

In order to debug the Fortran DLL the following steps need to be taken. Firstly, set the Fortran Application Extension as the startup project by right clicking the project in the Solution Explorer and selecting Set as StartUp Project. Enter the properties dialog for the Fortran Application Extension and select the Miscelleneoustab. Enter the full path to the Visual C++ Exe. Any time a debug target other than the main project output for a project is required then the full path should be entered. See the following image.

Click OK. Breakpoints can now be set inside the Fortran DLL and the debugger will break when execution hits these points. Alternatively Debug->Step Into can be used to break at the first executable statement that is reached in the Fortran DLL.

 

 

Copyright © 1999-2017 Silverfrost Limited