contents   index   previous   next



Declaring string arguments

 

Declaring string arguments passed by reference

 

In general, Visual Basic string arguments must be declared as BStrings by reference in your Fortran-f90VB procedures. Because this is the default for Fortran, usually you do not have to do anything special when declaring BString arguments in your Fortran code.

 

BStrings are always treated as handles, and this is what your Fortran procedure receives when you pass a Visual Basic string to it. A handle is a long-integer that contains a pointer to the BString (see Chapter 1). Because BStrings are usually passed by reference to your Fortran subroutine, the subroutine owns the BStrings. That is, the subroutine can change their content, deallocate the strings, move them to a different location, etc.

 

Declaring string arguments passed by value

 

Some Fortran compilers have extensions that allow tagging subroutine arguments as being passed by value. These are very useful extensions for Fortran code that will be used in a mixed-language environment, because in some languages (e.g. C), the default method for passing arguments is by value. Declaring arguments passed by value is a compiler-specific feature. This option will be explained later in this chapter, in the sections corresponding to those compilers that offer this functionality.

 

 

 

 

 

Example 5.1

 

The following example will be used throughout the rest of this section dealing with string arguments. In this example, you will create a Fortran subroutine that generates a string of the requested length with randomly selected characters from an input string. This is a useful subroutine for some genetic studies. DNA (Deoxy-ribo-Nucleic Acid) molecules are formed by combinations of four bases, coded as A (adenine), G (guanine), C (cytosine) and T (thymine), which may appear along the length of a strand of a DNA helix in any order. These bases are complex organic molecules that provide the fundamental genetic building blocks for the description of the overall organism that the DNA will construct and maintain. Geneticists frequently want to generate random sequences of these bases, which then are used to represent a portion of the helix of a DNA molecule. A Fortran subroutine to do this can be written as follows:

 

subroutine GenDNASequence(StrBases, iSeqLength, StrSequence, bError)

 

    use f90VBDefs

    use f90VBBstrings

    implicit none

 

    !Subroutine arguments

    !BString with available bases

    integer(BSTRHNDL_KIND),intent(in)::StrBases        

    !length of sequence to generate

    integer(LONG_KIND),intent(in)::iSeqLength                     

    !BString with generated sequence

    integer(BSTRHNDL_KIND),intent(inout)::StrSequence  

    !error flag (set to .true. if an error occurs)

    logical(SHORT_KIND),intent(out)::bError            

 

    !internal variables

    integer(BSTRHNDL_KIND)::TmpBStr

    type(OLESTRING)::TmpBases,TmpSeq

    integer(HRESULT_KIND)::iRet

    integer(LONG_KIND):: i,iRnd,TmpBasesLength

    real rRnd

 

    !Reformat input string into a standard BString

    call BStrConvFromVB(StrBases,TmpBStr)

 

    !Get the length of string with bases

    TmpBasesLength = StrLenTrim(TmpBStr)

 

    if (TmpBasesLength.eq.0 .or. iSeqLength.lt.1) then

 

        !returns with an error condition

        bError=.true.

 

    else

 

        bError=.false.

        !Copy bases into an OLE string for easy use

        call StrCopy(TmpBstr, TmpBases)

        !Allocate an empty OLE string for the resulting sequence

        call OLEStrAlloc('',TmpSeq,iRet,iSeqLength)

 

        !Seed random number generator

        call random_seed()

        !loop to generate the random sequence

        do i=1,iSeqLength

            !Generate a random number between 1 and (number of bases)

            call random_number(rRnd)

            iRnd=Int((TmpBasesLength - 1 + 1) * rRnd + 1)

            !assign random base to next entry in generated sequence

            TmpSeq%Str(i)=TmpBases%Str(iRnd)

        enddo                          

 

        !Create a BString with the generated sequence

        call StrCopy(TmpSeq, StrSequence)

        !Reformat BString for VB 

        call BStrConvToVB(StrSequence)

 

        !release memory used by temporary string

        call StrFree(TmpSeq)

        call StrFree(TmpBases)

        call StrFree(TmpBStr)

 

    endif

 

end subroutine GenDNASequence

 

 

In subroutine GenDNASequence, argument StrBases is a BString containing a list of all the possible bases that are involved in the construction of a DNA molecule (i.e. A, G, C and T). Using a string to pass the available bases gives the subroutine the necessary flexibility to use it to generate sequences of RNA, a molecule similar to DNA, but that performs a different function in the organism and has a different set of bases. Note that StrBases is declared with intent(in), because this string is not modified by the subroutine. Argument iSeqLength is used to indicate the length of the sequence that GenDNASequence must generate. The sequence generated by the subroutine is returned in argument StrSequence. Because StrSequence is modified inside the Fortran subroutine, we declare this argument with intent(inout). Argument bError is used by the subroutine to report errors during the sequence-generation process. For example, if any of the other arguments passed to GenDNASequence are invalid, the subroutine will return a value of .TRUE. in argument bError.

 

Note that all BString arguments are declared as handles. Because the subroutine will be called from Visual Basic, the first thing we need to do with the input string argument is to convert it into a standard BString. This is done by calling subroutine BStrConvFromVB, which receives the input string in Visual Basic external format (StrBases), and generates a new BString in standard BString format (TmpBStr). We then get the length of the string using f90VB's function StrLenTrim, to know how many bases are available. At this point, GenDNASequence checks for errors in the input arguments. If none are found, it proceeds to compute a sequence.

 

Due to the nature of the problem, it is easier and faster to use vectors to select the random bases and to create the DNA sequence. So we copy the string with the bases into a Fortran OLE String using StrCopy(TmpBStr, TmpBases). We also allocate another Fortran OLE String long enough to hold the complete resulting sequence by calling OLEStrAlloc. Next in GenDNASequence is a loop that generates the random sequence and puts the result into TmpSeq. This process is more or less self-explanatory.

 

The generated sequence is stored in a Fortran OLE String (TmpSeq), which needs to be converted into a BString before it can be returned to Visual Basic. To do this, GenDNASequence first calls StrCopy(TmpSeq, StrSequence) to create a new BString with the generated sequence, followed by a call to BStrConvToVB, to reformat the BString into the special format Visual Basic expects in returned BStrings. At this point the sequence is ready for Visual Basic. The calls to subroutine StrFree free the memory used by the intermediate BStrings and Fortran OLE Strings.

 

You may now create the Visual Basic front end for subroutine GenDNASequence. Start with a new Visual Basic project, and add a new module file with the following declaration:

 

Declare Sub GenDNASequence Lib "Example51.dll" (BasesStr As String, SeqSize As Long, SeqStr As String, ErrorFlag As Boolean) [26]

 

This gives Visual Basic the information it needs to locate the DLL file where GenDNASequence resides (we are assuming the DLL and the VB executable reside in the same directory), as well as the arguments that must be passed to the subroutine. Note that all arguments are passed by reference (Visual Basic's default).

 

Now you can add a new form to the project (Figure 5.1)

 

The text control txtBaseStr is used to input the bases that can be used to generate a sequence. Control txtSeqSize is used to input the size of the requested sequence, and control txtSeqSize will contain and show the generated sequence, after GenDNASequence is called. The command button cmdGo fires the event that calls the Fortran subroutine.

 

The click event for the cmdGo button has the following code:

 

Private Sub cmdGo_Click()

    

    Dim SeqStr As String

    Dim SeqSize As Long

    Dim ErrorFlag As Boolean

    

    'Check for an empty entry in sequence size

    If txtSeqSize.Text = "" Then

        SeqSize = 0

    Else

        SeqSize = CInt(txtSeqSize.Text)

    End If

    'Generate a sequence

    Call GenDNASequence(txtBasesStr.Text, SeqSize, SeqStr, ErrorFlag)

    'Check for error flag returned by GenDNASequence

    If ErrorFlag Then

        txtSeqStr.Text = "ERROR"

    Else

        txtSeqStr.Text = SeqStr

    End If

 

End Sub

 

 

Note that subroutine GenDNASequence expects an integer for the argument that indicates the size of the sequence that needs to be generated, however, you are using a text control to enter this value. Because of this, the value must be converted into an integer before GenDNASequence is called. To do this, you can use Visual Basic's CInt function. But CInt would generate an error if it is passed an empty string, so if the user leaves txtSeqSize empty, the code assigns SeqSize=0 as default. The code is still far from fool-proof, but for simplicity we will keep error-checking to a minimum.

 

The following subsections explain changes and additions that need to be made on subroutine GenDNASequence so that you can create the appropriate DLL with the different Fortran compilers supported by f90VB. We also give a very basic description of how to compile and link GenDNASequence with these compilers. For some compilers, we will also show variations in both the Visual Basic code and the Fortran code that take advantage of compiler-specific features dealing with calling convention, name-mangling and the way arguments are passed (i.e. by reference and by value). The sections are ordered alphabetically by compiler-vendor. You do not need to read them all, only those relevant to the Fortran compilers you use.

 

Absoft Pro Fortran

 

As explained earlier, to make subroutine GenDNASequence callable from Visual Basic, you need to instruct the Absoft compiler to perform the following operations:

 

Add the necessary code to make GenDNASequence conform to the STDCALL convention.

 

Create a DLL file containing GenDNASequence as a subroutine publicly available (i.e. export the subroutine).

 

Make the necessary name-mangling changes so the exported subroutine can be accessed with a friendly name.

 

Operation (1) is addressed with changes in the source code for GenDNASequence. Absoft Pro Fortran handles DLL exports and name-mangling (operations 2 and 3) at link time through a combination of compiler switches and auxiliary files.

 

Changes to GenDNASequence

 

To indicate that GenDNASequence should conform to the standard calling convention, all you need to do is add the qualifier STDCALL in front of the subroutine declaration.

 

After this change, the code for GenDNASequence should be as follows (changes appear in bold):

 

stdcall subroutine GenDNASequence(StrBases, iSeqLength, StrSequence, bError)

    use f90VBDefs

    use f90VBBStrings

    implicit none

 

    !Subroutine arguments

    !BString with available bases

    integer(BSTRHNDL_KIND),intent(in)::StrBases        

    !length of sequence to generate

    integer(LONG_KIND),intent(in)::iSeqLength                     

    !BString with generated sequence

    integer(BSTRHNDL_KIND),intent(inout)::StrSequence  

    !error flag (set to .true. if an error occurs)

    logical(SHORT_KIND),intent(out)::bError            

 

    !internal variables

    integer(BSTRHNDL_KIND)::TmpBStr

    type(OLESTRING)::TmpBases,TmpSeq

    integer(HRESULT_KIND)::iRet

    integer(LONG_KIND):: i,iRnd,TmpBasesLength

    real rRnd

 

    !Reformat input string into a standard BString

    call BStrConvFromVB(StrBases,TmpBStr)

 

    !Get the length of string with bases

    TmpBasesLength = StrLenTrim(TmpBStr)

 

    if (TmpBasesLength.eq.0 .or. iSeqLength.lt.1) then

 

        !returns with an error condition

        bError=.true.

 

    else

 

        bError=.false.

        !Copy bases into an OLE string for easy use

        call StrCopy(TmpBstr, TmpBases)

        !Allocate an empty OLE string for the resulting sequence

        call OLEStrAlloc('',TmpSeq,iRet,iSeqLength)

 

        !Seed random number generator

        call random_seed()

        !loop to generate the random sequence

        do i=1,iSeqLength

            !Generate a random number between 1 and (number of bases)

            call random_number(rRnd)

            iRnd=Int((TmpBasesLength - 1 + 1) * rRnd + 1)

            !assign random base to next entry in generated sequence

            TmpSeq%Str(i)=TmpBases%Str(iRnd)

        enddo                          

 

        !Create a BString with the generated sequence

        call StrCopy(TmpSeq, StrSequence)

        !Reformat BString for VB 

        call BStrConvToVB(StrSequence)

 

        !release memory used by temporary string

        call StrFree(TmpSeq)

        call StrFree(TmpBases)

        call StrFree(TmpBStr)

 

    endif

 

end subroutine GenDNASequence

 

Compiling GenDNASequence

 

By default, Absoft Pro Fortran does not initialize declared variables. Un-initialized handles can have spurious values, which would be interpreted by Window's BString services as addresses of allocated Bstrings. The initialization problem can be approached in two ways:

 

Explicitly initialize to zero all declared Bstring and Fortran OLE String handles: In GenDNASequence you can do this by adding the following statements at the beginning of the source code:

 

TmpBStr=0
TmpBases%StrHndl=0
TmpSeq%StrHndl=0

 

Use the –s compiler switch, which forces the compiler to treat internal variables as static and initialized to zero.

 

For simplicity, you can use the second approach to compile GenDNASequence using the following command-line instruction:

 

f90 –c –s Example51.f90 -p "%f90VBAPFModDir%"

 

Which produces the object file Example51.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 GenDNASequence

 

Absoft Pro Fortran will mangle the name of subroutines with the STDCALL qualifier. To solve this problem, you need to create an accessory file (Example51.als) for the linker containing aliases for the mangled names. In this case, the file contains a single line:

 

_GenDNASequence@16 GenDNASequence

 

This file is used later to tell the linker that subroutine _GenDNASequence@16 will be known externally as GenDNASequence.

 

You also need another file (Example51.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:

 

GenDNASequence

 

Use the following command-line instruction to link the library:

 

lnk /dll Example51.obj /exports:Example51.xps /aliases:Example51.als absRT0.lib fmath.lib f90math.lib "%f90VBAPFLibDir%\f90VBBstrings.lib"

 

The /dll switch instructs Absoft's linker to create Example51.dll. The /exports switch loads the file with the list of procedures to export into Example51.dll. The /aliases switch loads the aliases file with the external name of the subroutine. Note that you link against the f90VBBStrings library. The path of this library is stored in environment variable f90VBAPFLibDir, which was created by the f90VB installation program.

 

Example51.dll and GenDNASequence are now ready to be called from your Visual Basic program.

 

By Value Arguments in Absoft Pro Fortran

 

Absoft Pro Fortran allows Fortran 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.

 

Arguments StrBases and iSeqLength in GenDNASequence are good candidates to be passed by value. They are input-only parameters, and the subroutine does not (and is not supposed to) change their values. To declare an argument passed by value, you use the value qualifier in place of intent(in). The modified code for GenDNASequence would be (changes shown in bold):

 

stdcall subroutine GenDNASequence(StrBases, iSeqLength, StrSequence, bError)

 

    use f90VBDefs

    use f90VBBstrings

    implicit none

 

    !Subroutine arguments

    !BString with available bases

    integer(BSTRHNDL_KIND),value::StrBases        

    !length of sequence to generate

    integer(LONG_KIND),value::iSeqLength                     

    !BString with generated sequence

    integer(BSTRHNDL_KIND),intent(inout)::StrSequence  

    !error flag (set to .true. if an error occurs)

    logical(SHORT_KIND),intent(out)::bError            

 

    … (the rest of the code stays the same)

 

end subroutine GenDNASequence

 

 

If you change the method GenDNASequence uses to receive the arguments, you must also change the declaration of the subroutine in the calling Visual Basic program. The new declaration should be as follows (changes in bold):

 

Declare Sub GenDNASequence Lib "Example51.dll" (ByVal BasesStr As String, ByVal SeqSize As Long, SeqStr As String, ErrorFlag As Boolean)

 

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-f90VB code to other compilers, or if you plan to call GenDNASequence from code compiled with a different Fortran compiler.

 

Compaq (Digital) Visual Fortran

 

As explained earlier, to make subroutine GenDNASequence callable from Visual Basic, you need to instruct the CVF compiler to perform the following operations:

 

Add the necessary code to make GenDNASequence conform to the STDCALL convention.

 

Create a DLL file containing GenDNASequence as a subroutine publicly available (i.e. export the subroutine).

 

Make the necessary name-mangling changes so the exported subroutine can be accessed with a friendly name.

 

With Compaq Visual Fortran all these issues can be handled by adding the appropriate compiler pragmas (or in-line compiler directives) to the code for GenDNASequence.

 

Changes to GenDNASequence

 

To indicate that GenDNASequence 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:: GenDNASequence 

 

The STDCALL attribute also changes the default method used to pass arguments, so you need to tell the compiler that arguments to subroutine GenDNASequence are passed by reference. You can do this using the DEC$ATTRIBUTE REFERENCE directive:

 

!DEC$ATTRIBUTES REFERENCE:: StrBases, iSeqLength, StrSequence, bError

 

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 subroutine declaration:

 

!DEC$ATTRIBUTES DLLEXPORT:: GenDNASequence

 

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 subroutine:

 

!DEC$ATTRIBUTES ALIAS: 'GenDNASequence'::GenDNASequence

 

The first argument is the alias (i.e. the name by which the subroutine would be available to external programs using the DLL), the second argument is the Fortran name of the subroutine.

 

After these changes, the code for GenDNASequence should be as follows (changes appear in bold):

 

subroutine GenDNASequence(StrBases, iSeqLength, StrSequence, bError)

 

    use f90VBDefs

    use f90VBBstrings

    implicit none

 

    !DEC$ATTRIBUTES STDCALL:: GenDNASequence

    !DEC$ATTRIBUTES DLLEXPORT:: GenDNASequence

    !DEC$ATTRIBUTES ALIAS: 'GenDNASequence':: GenDNASequence

    !DEC$ATTRIBUTES REFERENCE::StrBases, iSeqLength, StrSequence, bError

 

    !Subroutine arguments

    !BString with available bases

    integer(BSTRHNDL_KIND),intent(in)::StrBases        

    !length of sequence to generate

    integer(LONG_KIND),intent(in)::iSeqLength                     

    !BString with generated sequence

    integer(BSTRHNDL_KIND),intent(inout)::StrSequence  

    !error flag (set to .true. if an error occurs)

    logical(SHORT_KIND),intent(out)::bError            

 

    !internal variables

    integer(BSTRHNDL_KIND)::TmpBStr

    type(OLESTRING)::TmpBases,TmpSeq

    integer(HRESULT_KIND)::iRet

    integer(LONG_KIND):: i,iRnd,TmpBasesLength

    real rRnd

 

    !Reformat input string into a standard BString

    call BStrConvFromVB(StrBases,TmpBStr)

 

    !Get the length of string with bases

    TmpBasesLength = StrLenTrim(TmpBStr)

 

    if (TmpBasesLength.eq.0 .or. iSeqLength.lt.1) then

 

        !returns with an error condition

        bError=.true.

 

    else

 

        bError=.false.

        !Copy bases into an OLE string for easy use

        call StrCopy(TmpBstr, TmpBases)

        !Allocate an empty OLE string for the resulting sequence

        call OLEStrAlloc('',TmpSeq,iRet,iSeqLength)

 

        !Seed random number generator

        call random_seed()

        !loop to generate the random sequence

        do i=1,iSeqLength

            !Generate a random number between 1 and (number of bases)

            call random_number(rRnd)

            iRnd=Int((TmpBasesLength - 1 + 1) * rRnd + 1)

            !assign random base to next entry in generated sequence

            TmpSeq%Str(i)=TmpBases%Str(iRnd)

        enddo                          

 

        !Create a BString with the generated sequence

        call StrCopy(TmpSeq, StrSequence)

        !Reformat BString for VB 

        call BStrConvToVB(StrSequence)

 

        !release memory used by temporary string

        call StrFree(TmpSeq)

        call StrFree(TmpBases)

        call StrFree(TmpBStr)

 

    endif

 

end subroutine GenDNASequence

 

Compiling and Linking GenDNASequence

 

Compiling GenDNASequence with Compaq Visual Fortran is straightforward. By default, the compiler initializes all declared integer variables and strings to zero, so initialization of handles is not an important issue. If you plan to port your code to another Fortran compiler, you may want to read the comments regarding initialization of variables in the other compilers' sections.

 

You can compile and link GenDNASequence in a single operation, using the following command-line instruction:

 

f90 Example51.f90 /dll /out:Example51.dll /module:"%f90VBDVFModDir%" "%f90VBDVFLibDir%\f90VBBstrings.lib"

 

The /dll switch instructs CVF's linker to create a dynamic link library whose name is provided by the /out switch. The /module compiler switch is used to indicate the path of the f90VB-BStrings modules. This path is stored in environment variable f90VBDVFModDir, created when you installed f90VB. Note that you link against the f90VBBStrings library. The path of this library is stored in environment variable f90VBDVFLibDir, also created by the f90VB installation program.

 

Example51.dll and GenDNASequence are now ready to be called from your Visual Basic program.

 

By Value Arguments in Compaq Visual Fortran

 

Compaq Visual Fortran allows Fortran 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.

 

Arguments StrBases and iSeqLength in GenDNASequence are good candidates to be passed by value. They are input-only parameters, and the subroutine does not (and is not supposed to) change their values. To declare these arguments as passed by value, add the following directive to the body of the subroutine:

 

!DEC$ATTRIBURES VALUE:: StrBases, iSeqLength

 

After these change, the code for GenDNASequence should be as follows (changes appear in bold):

 

subroutine GenDNASequence(StrBases, iSeqLength, StrSequence, bError)

 

    use f90VBDefs

    use f90VBBstrings

    implicit none

 

    !DEC$ATTRIBUTES STDCALL:: GenDNASequence

    !DEC$ATTRIBUTES DLLEXPORT:: GenDNASequence

    !DEC$ATTRIBUTES ALIAS: 'GenDNASequence':: GenDNASequence

    !DEC$ATTRIBUTES REFERENCE:: StrSequence, bError

    !DEC$ATTRIBUTES VALUE: StrBases, iSeqLength

 

    !Subroutine arguments

    !BString with available bases

    integer(BSTRHNDL_KIND),intent(in)::StrBases        

    !length of sequence to generate

    integer(LONG_KIND),intent(in)::iSeqLength                     

    !BString with generated sequence

    integer(BSTRHNDL_KIND),intent(inout)::StrSequence  

    !error flag (set to .true. if an error occurs)

    logical(SHORT_KIND),intent(out)::bError            

 

    … (the rest of the code stays the same)

 

end subroutine GenDNASequence

 

 

If you change the method GenDNASequence uses to receive the arguments, you must also change the declaration of the subroutine in the calling Visual Basic program. The new declaration should be as follows (changes in bold):

 

Declare Sub GenDNASequence Lib "Example51.dll" (ByVal BasesStr As String, ByVal SeqSize As Long, SeqStr As String, ErrorFlag As Boolean)

 

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 GenDNASequence from code compiled with a different Fortran compiler.

 

Lahey/Fujitsu Fortran 95

 

As explained earlier, to make subroutine GenDNASequence callable from Visual Basic, you need to instruct the LF95 compiler to perform the following operations:

 

Add the necessary code to make GenDNASequence conform to the STDCALL convention.

 

Create a DLL file containing GenDNASequence as a subroutine publicly available (i.e. export the subroutine).

 

Make the necessary name-mangling changes so the exported subroutine can be accessed with a friendly name.

 

With Lahey/Fujitsu Fortran 95, operations (1) and (3) are performed automatically by the compiler/linker when you use the –ml msvb switch. To indicate that a procedure should be exported to a DLL, you add the compiler directive DLL_EXPORT to the body of your subroutine.

 

Changes to GenDNASequence

 

The only change you need to do to compile subroutine GenDNASequence 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 GenDNASequence should be as follows (changes appear in bold):

 

subroutine GenDNASequence(StrBases, iSeqLength, StrSequence, bError)

 

    use f90VBDefs

    use f90VBBstrings

    implicit none

 

    DLL_EXPORT GenDNASequence

 

    !Subroutine arguments

    !BString with available bases

    integer(BSTRHNDL_KIND),intent(in)::StrBases        

    !length of sequence to generate

    integer(LONG_KIND),intent(in)::iSeqLength                     

    !BString with generated sequence

    integer(BSTRHNDL_KIND),intent(inout)::StrSequence  

    !error flag (set to .true. if an error occurs)

    logical(SHORT_KIND),intent(out)::bError            

 

    !internal variables

    integer(BSTRHNDL_KIND)::TmpBStr

    type(OLESTRING)::TmpBases,TmpSeq

    integer(HRESULT_KIND)::iRet

    integer(LONG_KIND):: i,iRnd,TmpBasesLength

    real rRnd

 

    !Reformat input string into a standard BString

    call BStrConvFromVB(StrBases,TmpBStr)

 

    !Get the length of string with bases

    TmpBasesLength = StrLenTrim(TmpBStr)

 

    if (TmpBasesLength.eq.0 .or. iSeqLength.lt.1) then

 

        !returns with an error condition

        bError=.true.

 

    else

 

        bError=.false.

        !Copy bases into an OLE string for easy use

        call StrCopy(TmpBstr, TmpBases)

        !Allocate an empty OLE string for the resulting sequence

        call OLEStrAlloc('',TmpSeq,iRet,iSeqLength)

 

        !Seed random number generator

        call random_seed()

        !loop to generate the random sequence

        do i=1,iSeqLength

            !Generate a random number between 1 and (number of bases)

            call random_number(rRnd)

            iRnd=Int((TmpBasesLength - 1 + 1) * rRnd + 1)

            !assign random base to next entry in generated sequence

            TmpSeq%Str(i)=TmpBases%Str(iRnd)

        enddo                          

 

        !Create a BString with the generated sequence

        call StrCopy(TmpSeq, StrSequence)

        !Reformat BString for VB 

        call BStrConvToVB(StrSequence)

 

        !release memory used by temporary string

        call StrFree(TmpSeq)

        call StrFree(TmpBases)

        call StrFree(TmpBStr)

 

    endif

 

end subroutine GenDNASequence

 

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 GenDNASequence

 

Compiling GenDNASequence with Lahey/Fujitsu Fortran 95 is straight forward. 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 Example51.f90 -dll -win -ml msvb -mod "%f90VBLF95ModDir%" -lib "%f90VBLF95LibDir%\f90VBBstrings.lib"

 

creates Example51.dll containing subroutine GenDNASequence, which is ready to be called from Visual Basic. The -mod compiler switch is used to indicate the path of the f90VB-BStrings modules. This path is stored in environment variable f90VBLF95ModDir, created when you installed f90VB. Note that you link against the f90VBBStrings library. The path of this library is stored in environment variable f90VBLF95LibDir, also created by the f90VB installation program.