|
||||||||||||||||||||||||||||||||||||||||||||||||||
![]() |
||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
|
Calling
Fortran Dlls from Visual Basic By Marco A. Garcia Copyright
©1999 Canaima Software
Bstrings (or BSTR) are the basic string type for Object Linking and Embedding (OLE-Automation) and Component-Object Model (COM) services. If Fortran programmers ever expect to take full advantage of these interesting new technologies, they need to get used to the idea that Bstrings are here to stay. Even if you are not interested in using OLE/COM, your Fortran programs may benefit from the use of Bstrings. Bstrings are allocated dynamically, can handle international alphabets and can store large amounts of text. Bstrings are also relatively stable because most to their handling is done through the Windows operating system. In this article I will introduce the Bstrings Library for Fortran (Bstrings.lib), developed by Canaima Software. The aim of this library is to offer Fortran programmers a set of well-designed functions and subroutines to manipulate Bstrings, and to easily convert Fortran strings to Bstrings or back. The memory representation of Bstrings and the rules governing their manipulation were explained in a previous article. This article has a more practical approach. Here, we will see how to create and destroy Bstrings, how to exchange information with Bstrings, and how they can be used exchange data between your Fortran subroutines and your Visual Basic or C/C++ programs.
character(len=10)::MyFString In this document, Fortran strings are frequently called Fstrings for brevity. OLE-Strings are 2-byte strings (each character in the string takes 2 bytes of storage) and are null terminated. With Canaima Software's Bstring Library, OLE-Strings are always defined as vectors of integer(kind=2). To maintain compatibility with future changes and among compiler vendors, you should use constant OLECHAR_KIND instead of 2 for the kind. This constant is defined in the Bstrings Library. A typical definition of an OLE-String looks like this: integer(OLECHAR_KIND)::MyOleStr(25) Because OLE-Strings are null terminated, you should always allocate an extra entry in the vector to account for the null terminator. For example, to define an OLE-String that will contain the string "Example", you must allocate a vector with 8 elements. The first 7 will contain the ASCII representation of the characters in the string; the last entry will contain 0 (null terminator). Bstrings were explained in a previous article. Bstrings are dynamically allocated by the operating system. Canaima Software's Bstring Library takes care for you of doing the appropriate OS calls to handle allocation, destruction and changes in Bstrings. Canaima Software's Bstring Library contains three components. The library file (Bstrings.lib), which contains all the subroutines necessary to handle the strings. A module with definition of constants (BstringsDefs.mod), and a module with definitions of interfaces (Bstrings.mod). Before you can use these definitions in your Fortran code, you must USE the modules. You must also link your object files against the Bstrings.lib to create the executable.
Program Example01 !This program requests a text input from the user !and allocates a new Bstring with the text !then it deallocates the Bstring before closing use f90OLEDefs use f90BStrings implicit none integer(BSTRHNDL_KIND)::BSTRHndl character(Len=100) InputStr print *,'Enter the text to initialize the Bstring:' read (*,*) InputStr !Allocate a new Bstring BSTRHndl=BstrAlloc(InputStr) if (BSTRHndl.eq.0) then
print *,'System could not allocate Bstring'
else
print *,'Bstring successfully allocated. Handle Value:', &
BSTRHndl
endif
!Destroy Bstring and deallocate used memory call BstrFree(BSTRHndl) print *,'Bstring deallocated. Handle Value:', BSTRHndl stop end The argument InputStr in function BstrAlloc, can be a normal Fortran-style string, another Bstring or an OLE-string (more on OLE-Strings later). The function can be called with an additional argument indicating the size of the Bstring buffer (i.e. the amount of memory the operating system reserves for the new Bstring. See the section "Changing the Contents of a Bstring", later in this manual). If the system cannot allocate the necessary space for the Bstring, the returned handle has the value zero (0).
The following example shows how to use BstrReAlloc. In the example, we allocate and initialize a new Bstring, and then change its contents.
Program Example02 !This program allocates and initializes a Bstring !then requests a new string from the user and !uses BstrReAlloc to replace the contents of the !original Bstring. use f90OLEDefs use f90BStrings implicit none integer(BSTRHNDL_KIND)::BSTRHndl
character(Len=500)::InputStr
integer(HRESULT_KIND):: iRet
!Allocate a new Bstring
BSTRHndl=BstrAlloc('Original Contents')
if (BSTRHndl.eq.0) then
print *,'System could not allocate original Bstring'
else
print *,'Bstring successfully allocated. Handle Value:', &
BSTRHndl
endif
print *,'Enter the text to initialize the Bstring:'
read (*,*) InputStr
!Changes contents of original Bstring
call BstrReAlloc(BSTRHndl,InputStr,iRet)
if (iRet.eq.OLE_TRUE) then
print *,'System successfully changed the contents'
print *,'of the Bstring. Handle Value:',BSTRHndl
else
print *,'System could not change the contents'
print *,'of the original Bstring'
endif
!Destroy Bstring and deallocate used memory call BstrFree(BSTRHndl) print *,'Bstring deallocated. Handle Value:', BSTRHndl stop end Using StrCopy: A Generic Subroutine to Convert String Formats.
Arguments DestStr and ScrStr can be of any of the supported string types (Fstrings, OLE-Strings or Bstrings). If DestStr is a Bstring handle, StrCopy will allocate a new string if the passed handle is has a null, or reallocate the Bstring if the handle has been already allocated. The following program shows several examples of how StrCopy can be used.
program Example03
!This program shows examples of how to use subroutine StrCopy
use f90OLEDefs
use f90BStrings
implicit none
integer(BSTRHNDL_KIND)::BSTRHndlA,BSTRHndlB
character(Len=100)::FStrA,FStrB
integer(OLECHAR_KIND)::OLEStrA(101), OLEStrB(101)
integer(HRESULT_KIND):: iRet
integer i
print *,'Enter a string'
read(*,*) FStrA
!Create a Bstring, initialize to FStrA
Call StrCopy(BSTRHndlA,FStrA,iRet)
if (iRet.eq.BSTRINGS_ERR) then
print *,'Error creating Bstring A'
stop
else
print *,'Bstring A created. Handle Value:',BSTRHndlA
endif
!Convert FStrA into an OLE string stored in OLEStrA
Call StrCopy(OLEStrA,FStrA,IRet)
if (iRet.eq.BSTRINGS_ERR) then
print *,'Error creating OLE-String A'
stop
else
print *,'OLE-String A created.'
print *,'Values:',(OLEStrA(i),i=1,StrLenTrim(OLEStrA))
print *,'Characters:',(char(OLEStrA(i)),i=1, &
StrLenTrim(OLEStrA))
endif
!Copy BstringA to FStringB
Call StrCopy(FStrB,BSTRHndlA,iRet)
if (iRet.eq.BSTRINGS_ERR) then
print *,'Error copying Bstring A to FString B'
stop
else
print *,'FString B created. Value:',trim(FStrB)
endif
!Error checks removed from this section for brevity
!Copy some text to OLEStrB
Call StrCopy(OLEStrB,'Sample text stored in OLE-String B',iRet)
print *,'OLE-String B created.'
print *,'Values:',(OLEStrB(i),i=1,StrLenTrim(OLEStrB))
print *,'Characters:',(char(OLEStrB(i)),i=1,StrLenTrim(OLEStrB))
!Create a Bstring, initialize to text in OLEStrB
Call StrCopy(BSTRHndlB,OLEStrB,iRet)
print *,'Bstring B created. Handle Value:',BSTRHndlB
!Changes BstringA to text in BstringB
Call StrCopy(BSTRHndlA,BSTRHndlB,iRet)
print *,'Bstring A modified. Handle Value:',BSTRHndlA
!Copy Bstring A to FString B
Call StrCopy(FStrB,BSTRHndlA,iRet)
print *,'FString B created. Value:',trim(FStrB)
!Free Bstrings
call BstrFree(BSTRHndlA)
call BstrFree(BSTRHndlB)
stop
end
Other Functions available in Canaima Software's Bstrings Library
The following table lists all the procedures and functions available in Canaima Software's Bstring Library.
Some of the functions and subroutines included in Canaima Software's Bstring library are redundant with intrinsic Fortran subroutines that handle Fstrings. For example, function StrLen, when used with a Fstring argument, is equivalent to FORTRAN's intrinsic function Len(). This redundancy was introduced on purpose. The objective of Canaima Software's Bstring Library was to offer a homogenous set of procedures that would work in the same way, regardless of the string types passed as arguments.
I have said several times that VB strings and Bstrings are the same. Internally VB handles all its string as Bstrings. A point that is frequently misunderstood, however, is that when Visual Basic calls functions declared as external (i.e. using VB's DECLARE statement), all string arguments are converted to a special type of Bstring. This special type (I will call it External VB Bstring) uses only one byte per character, instead of the two bytes per character used by standard OLE Bstrings. Why would Microsoft do such a thing? Well the only reason I can think of is to allow VB programs to call Windows' API functions, most of which expect C-style ANSI strings. But the strings passed by VB to external subroutines are real Bstrings, they still have the string length on front of the string, and they still must be handled as Bstrings, with the exception of the number of bytes they use to represent each character. The following figure illustrates how a normal Bstring and VB External-Bstring look like.
The meaning of this change of formats is that procedures that expect standard OLE Bstrings as arguments, will not work properly if called from Visual Basic through a DECLARE interface. Now, you may be thinking that Microsoft's developers went nuts on this; and I thought the same for a while, until I realized that VB can use OLE and COM services as naturally as if they were part of the language. In fact, Visual Basic is OLE in and out. So there is not reason to use DECLARE interfaces to access the OLE/COM functionality. Instead, VB developers designed the DECLARE interface as a mean to interface Visual Basic with other languages and the Windows' APIs; converting Bstrings to this special format when calling Windows' API functions makes much more sense. When you pass a Bstring by reference to an external procedure, VB first calls an internal subroutine that creates a temporary special Bstring with the passed argument. This is the Bstring passed to the external procedure. On return, VB calls another internal subroutine that converts the special Bstring into an OLE standard Bstring. You should notice, however, that the string passed to the external procedure is still a Bstring, so the external procedure can only change it by using the appropriate Bstring APIs. Canaima Software's Bstrings Library was designed to handle standard OLE Bstrings. Many of the procedures included in the library will fail if called with External Visual Basic Bstrings. The library, however, includes two special subroutines to deal with this problem. Subroutine BstrConvFromVB takes an External Visual Basic Bstring and converts it to a standard OLE Bstring. The subroutine BstrConvToVB does the opposite. At this point we have seen most of the details we need to know to handle Bstring arguments in Fortran. Let's now see a simple example of how a Fortran subroutine can receive a Bstring argument, change it and return the changed Bstring to the calling Visual Basic program. The following Fortran subroutine receives a Bstring, finds its length and returns a new Bstring with a text indicating the length of the input Bstring.
subroutine BStringLength(BstrHndl)
!Subroutine changes the passed Bstring to a text
!indicating its length in number of characters
use f90OLEDefs
use f90BStrings
implicit none
!Uncomment next lines if you are using DVF
!!DEC$ATTRIBUTES DLLEXPORT::BStringLength
!!DEC$ATTRIBUTES ALIAS: 'BStringLength'::BStringLength
!Uncomment the next line if you are using Lahey Fortran 95
!or Absoft Pro Fortran
!DLL_EXPORT BStringLength
!Create an export file if you are using Salford Fortran 95
!subroutine arguments
integer(BSTRHNDL_KIND),intent(inout)::BSTRHndl
!internal variables
integer::BstrLength
character(Len=5)::CharBStrLen
!Reformat Bstring to standard format
call BstrConvFromVB(BSTRHndl)
!write length of passed Bstring to a character variable
BstrLength=StrLenTrim(BSTRHndl)
if (BstrLength.eq.BSTRINGS_ERROR) then
CharBStrLen='ERROR'
else
write(CharBStrLen,'(i5)') BstrLength
endif
!write return Bstring
call StrCopy(BstrHndl,'The length of the passed Bstring was ' // &
trim(CharBstrLen))
!Reformat return Bstring to VB external format
call BstrConvToVB(BstrHndl)
end subroutine
To call the previous subroutine from a VB program, you need to put the subroutine into a DLL. The way you do this is compiler dependent, and out of the scope of this article. You should consult your compiler documentation for instructions on how to create a DLL that includes the previous function. Make sure you link the DLL against Canaima Software's Bstrings library (Bstrings.lib). Once you have your Fortran DLL, open a new Visual Basic project. Add a text Box to the project's form and name it txtBox. Add also a command button and name it Go. Add the following instructions to the form's code:
Private Sub cmdGo_Click() Dim MyString As String MyString = txtBox.Text Call BStringLength(MyString) MsgBox (MyString) End Sub
Now, add a new module to the project, and define the interface to subroutine BstringLength using a declare statement. For example:
Declare Sub BStringLength Lib "MyDLLDirectoryPath" _
(ByRef str As String)
Replace MyDLLDirectoryPath by the path to your fortran DLL. You can now run the example. This is a screenshot of the running project.
Where to find Canaima Software's Bstrings library
Canaima Software's Bstrings library is available for Digital Visual FORTRAN (ver. 5.0 and 6.0), Absoft Pro FORTRAN (ver. 5.0 and 6.0), Lahey/Fujitsu FORTRAN 95 and Salford FORTRAN 95. The library can be downloaded from Canaima Software's web site (http://www.canaimasoft.com). Make sure you read the user license agreement. The library is free for personal and academic use only. Commercial or corporate users of the library must contact Canaima Software for information about how to obtain a license. |
||||||||||||||||||||||||||||||||||||||||||||||||||
|
Download a PostScript copy of this article |
||||||||||||||||||||||||||||||||||||||||||||||||||
|
Copyright
© 1998-2000 Canaima Software
For questions regarding this site, sent an e-mail to webmaster@canaimasoft.com |
||||||||||||||||||||||||||||||||||||||||||||||||||