Declaring Variant arguments
Visual Basic and Visual Basic for Applications can pass Variant arguments by value or by reference. A Variant passed by value is copied to the calling stack and must be retrieved from there by the called procedure. For Variants passed by reference, the calling program only passes the address (i.e. a pointer) of the Variant. In standard Fortran, all arguments to subroutines and functions are always passed by reference, so all compilers know how to handle Variants passed by reference. In addition, some compilers (Compaq Visual Fortran and Absoft Pro Fortran) have the necessary functionality to create subroutines that receive arguments by value.
Even if you are using a compiler that supports passing arguments by value, we recommend that you avoid creating subroutines that receive Variants by value. Because not all compilers can handle arguments by value, such subroutines would be harder to port among compilers supported by f90VB.
Example 4.5 (Chapter 4) shows an example of a Fortran function that can be called from Visual Basic and Visual Basic for Applications. In this section, you will see the compiler-specific changes that need to be done on Example 4.5 to make it into a DLL that can be used from Visual Basic.
Absoft Pro Fortran
Changes to SineX
To indicate that SineX should conform to the standard calling convention you need to add the qualifier STDCALL in front of the function declaration.
After this change, the code for SineX should be as follows (changes appear in bold):
stdcall function SineX(Value)
!Compute the Sine of the argument in Variant VarVal
!if Value contains a base type (VT_I4, VT_R8, etc.)
!SineX returns the computed value in a variant of type
!double precision. If an error occurs, the return
!value of SineX is an empty variant
!Note: Value can be a reference to a string,
!in which case SineX attempts to cast the variant
!into a numeric value. If the Value cannot be
!cast, SineX will return an empty variant
!Note: This function cannot handle Safe Arrays
use f90VBDefs
use f90VBVariants
implicit none
!Function arguments
type(VARIANT)::SineX
type(VARIANT),intent(in)::Value
(the rest of the code stays the same)
end function SineX
Compiling SineX
To compile SineX use the following command-line instruction:
f90 –c Example45.f90 -p "%f90VBAPFModDir%"
Which produces the object file Example45.obj. The –p compiler switch is used to indicate the path of the f90VB modules. This path is stored in environment variable f90VBAPFModDir, created when you installed f90VB.
Linking SineX
Absoft Pro Fortran will mangle the name of subroutines with the STDCALL qualifier. To solve this problem, you need to create an accessory file (Example45.als) for the linker containing aliases for the mangled names. In this case, the file contains a single line:
_SineX@4 SineX
This file is used later to tell the linker that subroutine _SineX@4 will be known externally as SineX.
You also need another file (Example45.xps) to indicate the name of the Fortran procedures that will be exported to the DLL. In this case, the exports file contains only one line:
SineX
Use the following command-line instruction to link the library:
lnk /dll Example45.obj /exports:Example45.xps /aliases:Example45.als absRT0.lib fmath.lib f90math.lib "%f90VBAPFLibDir%\f90VBVariants.lib"
The /dll switch instructs Absoft's linker to create Example45.dll. The /exports switch loads the file with the list of procedures to export into Example45.dll. The /aliases switch loads the aliases file with the external name of the function. Note that you link against the f90VBVariants library. The path of this library is stored in environment variable f90VBAPFLibDir, which was created by the f90VB installation program.
Example45.dll and SineX are now ready to be called from your Visual Basic program.
By Value Variants in Absoft Pro Fortran
Absoft Pro Fortran allows Fortran procedures to receive and/or pass arguments by value. This is especially useful for mixed language programming, because it gives you an additional tool to enforce argument rules in the calling program, as well as the called subroutine.
Argument Value in SineX is a good candidate to be passed by value, because it is an input-only argument. To declare an argument passed by value, you use the value qualifier in place of intent(in). The modified code for SineX would be (changes shown in bold):
stdcall function SineX(Value)
!Compute the Sine of the argument in Variant VarVal
!if Value contains a base type (VT_I4, VT_R8, etc.)
!SineX returns the computed value in a variant of type
!double precision. If an error occurs, the return
!value of SineX is an empty variant
!Note: Value can be a reference to a string,
!in which case SineX attempts to cast the variant
!into a numeric value. If the Value cannot be
!cast, SineX will return an empty variant
!Note: This function cannot handle Safe Arrays
use f90VBDefs
use f90VBVariants
implicit none
!Function arguments
type(VARIANT)::SineX
type(VARIANT),value::Value
(the rest of the code stays the same)
end function SineX
If you change the method SineX uses to receive the Value argument, you must also change the declaration of the function in the calling Visual Basic program. The new declaration should be as follows (changes in bold):
Declare Function SineX Lib "Example45.dll" (byVal Value As Variant) As Variant
Compiling and linking is done the same way as explained above.
Absoft Pro Fortran extensions to pass/receive arguments by value are an excellent tool to create Fortran DLLs that integrate well with Visual Basic and C/C++. However, keep in mind that few Fortran compilers for Windows support arguments passed by value. This may be an important consideration if you plan to port your Fortran code to other compilers, or if you plan to call SineX from code compiled with a different Fortran compiler.
Compaq (Digital) Visual Fortran
Changes to SineX
To indicate that SineX should conform to the standard calling convention, you need to add a DEC$ATTRIBUTE STDCALL compiler directive to the declaration of the subroutine:
!DEC$ATTRIBUTES STDCALL:: SineX
The STDCALL attribute also changes the default method used to pass arguments, so you need to tell the compiler that arguments to subroutine SineX are passed by reference. You can do this using the DEC$ATTRIBUTE REFERENCE directive:
!DEC$ATTRIBUTES REFERENCE:: Value
To indicate that the subroutine must be exported to the DLL as a public procedure, you add the DEC$ATTRIBUTES DLLEXPORT compiler directive to the function declaration:
!DEC$ATTRIBUTES DLLEXPORT:: SineX
Compaq Visual Fortran will mangle the name of subroutines with the STDCALL attribute. You can use another compiler directive to indicate an alias for the mangled name of the exported function:
!DEC$ATTRIBUTES ALIAS: 'SineX'::SineX
The first argument is the alias (i.e. the name by which the function would be available to external programs using the DLL), the second argument is the Fortran name of the function.
After these changes, the code for SineX should be as follows (changes appear in bold):
function SineX(Value)
!Compute the Sine of the argument in Variant VarVal
!if Value contains a base type (VT_I4, VT_R8, etc.)
!SineX returns the computed value in a variant of type
!double precision. If an error occurs, the return
!value of SineX is an empty variant
!Note: Value can be a reference to a string,
!in which case SineX attempts to cast the variant
!into a numeric value. If the Value cannot be
!cast, SineX will return an empty variant
!Note: This function cannot handle Safe Arrays
use f90VBDefs
use f90VBVariants
implicit none
!DEC$ATTRIBUTES DLLEXPORT:: SineX
!DEC$ATTRIBUTES STDCALL:: SineX
!DEC$ATTRIBUTES ALIAS: 'SineX'::SineX
!DEC$ ATTRIBUTES REFERENCE :: Value
!Function arguments
type(VARIANT)::SineX
type(VARIANT),intent(in)::Value
!Internal variables
type(VARIANT)::varTmp
integer(HRESULT_KIND)::iRet
(the rest of the code stays the same)
end subroutine SineX
Compiling and Linking SineX
You can compile and link SineX in a single operation, using the following command-line instruction:
f90 Example45.f90 /dll /out:Example45.dll /module:"%f90VBDVFModDir%" "%f90VBDVFLibDir%\f90VBVariants.lib"
The /dll switch instructs CVF's linker to create a dynamic link library whose name is provided by the /out switch. The /mod compiler switch is used to indicate the path of the f90VB modules. This path is stored in environment variable f90VBDVFModDir, created when you installed f90VB. Note that you link against the f90VBVariants library. The path of this library is stored in environment variable f90VBDVFLibDir, also created by the f90VB installation program.
Example45.dll and SineX are now ready to be called from your Visual Basic program.
By Value Variants in Compaq Visual Fortran
Compaq Visual Fortran allows Fortran functions and subroutines to receive and/or pass arguments by value. This is especially useful for mixed language programming, because it gives you an additional tool to enforce argument rules in the calling program, as well as the called subroutine. To declare an argument passed by value, you use the compiler directive DEC$ATTRIBUTE VALUE.
Argument Value in SineX is a good candidate to be passed by value, because it is an input-only argument. To declare this argument as passed by value, add the following directive to the body of the subroutine:
!DEC$ATTRIBURES VALUE:: Value
After this change, the code for SineX should be as follows (changes appear in bold):
function SineX(Value)
!Compute the Sine of the argument in Variant VarVal
!if Value contains a base type (VT_I4, VT_R8, etc.)
!SineX returns the computed value in a variant of type
!double precision. If an error occurs, the return
!value of SineX is an empty variant
!Note: Value can be a reference to a string,
!in which case SineX attempts to cast the variant
!into a numeric value. If the Value cannot be
!cast, SineX will return an empty variant
!Note: This function cannot handle Safe Arrays
use f90VBDefs
use f90VBVariants
implicit none
!DEC$ATTRIBUTES DLLEXPORT:: SineX
!DEC$ATTRIBUTES STDCALL:: SineX
!DEC$ATTRIBUTES ALIAS: 'SineX'::SineX
!DEC$ ATTRIBUTES VALUE :: Value
!Function arguments
type(VARIANT)::SineX
type(VARIANT),intent(in)::Value
!Internal variables
type(VARIANT)::varTmp
integer(HRESULT_KIND)::iRet
(the rest of the code stays the same)
end subroutine SineX
If you change the method SineX uses to receive the Value argument, you must also change the declaration of the function in the calling Visual Basic program. The new declaration should be as follows (changes in bold):
Declare Function SineX Lib "Example45.dll" (byVal Value As Variant) As Variant
Compiling and linking is done the same way as explained above.
CVF extensions to pass/receive arguments by value are an excellent tool to create Fortran DLLs that integrate well with Visual Basic and C/C++. However, keep in mind that few Fortran compilers for Windows support arguments passed by value. This may be an important consideration if you plan to port your Fortran code to other compilers, or if you plan to call SineX from code compiled with a different Fortran compiler.
Lahey/Fujitsu Fortran 95
Changes to SineX
The only change you need to do to compile subroutine SineX with Lahey’s compiler is to add the DLL_EXPORT directive to indicate that the subroutine should be exported to a DLL. After this change, the code for SineX should be as follows (changes appear in bold):
function SineX(Value)
!Compute the Sine of the argument in Variant VarVal
!if Value contains a base type (VT_I4, VT_R8, etc.)
!SineX returns the computed value in a variant of type
!double precision. If an error occurs, the return
!value of SineX is an empty variant
!Note: Value can be a reference to a string,
!in which case SineX attempts to cast the variant
!into a numeric value. If the Value cannot be
!cast, SineX will return an empty variant
!Note: This function cannot handle Safe Arrays
use f90VBDefs
use f90VBVariants
implicit none
DLL_EXPORT SineX
!Function arguments
type(VARIANT)::SineX
type(VARIANT),intent(in)::Value
!Internal variables
type(VARIANT)::varTmp
integer(HRESULT_KIND)::iRet
(the rest of the code stays the same)
end subroutine SineX
The compiler directive DLL_EXPORT also indicates the external name of the procedure, so the subroutine becomes publicly available with the name that follows DLL_EXPORT.
Compiling and Linking SineX
To compile SineX with Lahey/Fujitsu Fortran 95 you need to use the special switch –ml msvb to indicate to the compiler/linker the appropriate name-mangling and calling convention. The command
lf95 Example45.f90 -dll -win -ml msvb -mod "%f90VBLF95ModDir%" -lib "%f90VBLF95LibDir%\f90VBVariants.lib"
creates Example45.dll containing subroutine SineX, which is ready to be called from Visual Basic. The -mod compiler switch is used to indicate the path of the f90VB modules. This path is stored in environment variable f90VBLF95ModDir, created when you installed f90VB. Note that you link against the f90VBVariants library. The path of this library is stored in environment variable f90VBLF95LibDir, also created by the f90VB installation program.
Example45.dll and SineX are now ready to be called from your Visual Basic program.