contents   index   previous   next



Handling arguments by reference

 

So far all the method calls you have seen had only input arguments, i.e. arguments that you pass to the ActiveX object your program is controlling. As you would expect, some objects expose methods with arguments that are also used as output. Example 6.4 has one of these methods (GetInputData). The method can receive three optional arguments (InitValue, EndValue and StepIncrement that return directly the values that were entered by the user, saving you from having to call PropertyGet later to retrieve these values.

 

Arguments by reference (those on which the ActiveX object will return values) must be passed as Variants by reference (i.e. Variants with a VT_BYREF mask on their VT-Type). From Chapter 3 you may remember that these Variants do not contain the real value, but a pointer to the value. Here is the new version of The Ultimate Logarithms Program, which uses the return values from GetInputData instead of querying object UltimateLogsIO for its properties.

 

 

 

 

 

Example 6.4B

 

program UltimateLogsB

 

use f90VBDefs

use f90VBBStrings

use f90VBSafeArrays

use f90VBVariants

use f90VBAutomation

implicit none

 

type(VARIANT)::UltLogsIO,TmpVar,TmpLogTable

real(8),pointer::MapArray(:,:)

integer(HRESULT_KIND)::iRet, i

type(EXCEPINFO)::EInfo

character(len=255)::TmpStr

integer(SAFEARRAY_KIND)::LogTable

 

real(8)::InitValue, FinalValue, IncStep

type(VARIANT)::VarInitValue, VarFinalValue, VarIncStep

real(8)::Value

 

iRet = OleInitialize()

 

!create an instance of UltimateLogsIO object

UltLogsIO = CreateOleObject('UltimateLogsIOServer.UltimateLogsIO',iRet)

if (iRet.ne.S_OK) goto 1000

 

VarInitValue%VarVal%vt=VT_R8+VT_BYREF

VarInitValue%VarVal%byRef = loc(InitValue)

VarFinalValue%VarVal%vt=VT_R8+VT_BYREF

VarFinalValue%VarVal%byRef = loc(FinalValue)

VarIncStep%VarVal%vt=VT_R8+VT_BYREF

VarIncStep%VarVal%byRef = loc(IncStep)

 

!Show input screen and get range for the table

TmpVar = ExecMethod(UltLogsIO,'GetInputData', VarInitValue, &

                    VarFinalValue, VarIncStep, iRet=iRet,EInfo=EInfo)

if (iRet.ne.S_OK) goto 1000

 

 

!Compute size of array and allocate an appropriate Safe Array

call SafeArrayCreate(LogTable,VT_R8, &

                     1,int((FinalValue - InitValue) / IncStep + 1), &

                     1,2)

!Map Fortran array to Safe Array for fast access

call SafeArrayAccessData(LogTable,MapArray)

 

!compute the table of logs

Value = InitValue

do i=1,ubound(MapArray,1)

    MapArray(i,1)=Value !VariantCreate(VT_R8,Value)

    MapArray(i,2) =dlog(Value) !VariantCreate(VT_R8,dlog(Value))

    Value = Value+IncStep

enddo

 

!we don't need the mapped array anymore, so unmap it

call SafeArrayUnAccessData(LogTable)

 

!hand-build a variant containing LogTable + VT_BYREF

TmpLogTable%varVal%vt = VT_BYREF+VT_R8+VT_ARRAY

TmpLogTable%varVal%byref = loc(LogTable)

 

!show the logarithm table

TmpVar = ExecMethod(UltLogsIO,'ShowLogTable',TmpLogTable,iRet=iRet,EInfo=EInfo)

if (iRet.ne.S_OK) goto 1000

 

goto 2000

 

1000 continue

     !Print error messages

     print *, 'The following Errors were detected:'

     print *, 'HRESULT:',iRet

     call StrCopy(EInfo%bstrDescription,TmpStr)

     print *,TmpStr

 

2000 continue

 

!release all variants, safe arrays, etc.

call Release(UltLogsIO)

call ExceptionClear(EInfo)

call VariantClear(TmpVar)

call SafeArrayDestroy(LogTable)

call VariantClear(TmpLogTable)

call OleUnInitialize()

 

stop

end

 

 

As you might have noticed most of the code looks the same, but there are four changes:

 

First, we have added three more Variant definitions:

 

type(VARIANT)::VarInitValue, VarFinalValue, VarIncStep

 

 

These are the Variants that will be passed as arguments to method GetInputData. Second, notice that we create the Variants manually, rather than using function VariantCreate. f90VB’s Variants Library does not create Variants by reference:

 

VarInitValue%VarVal%vt=VT_R8+VT_BYREF

VarInitValue%VarVal%byRef = loc(InitValue)

VarFinalValue%VarVal%vt=VT_R8+VT_BYREF

VarFinalValue%VarVal%byRef = loc(FinalValue)

VarIncStep%VarVal%vt=VT_R8+VT_BYREF

VarIncStep%VarVal%byRef = loc(IncStep)

 

 

Note that what is stored in the Variants are addresses, rather than real values.

 

Third, you see that now we call GetInputData passing these variants as arguments:

 

TmpVar = ExecMethod(UltLogsIO,'GetInputData', VarInitValue, &

                    VarFinalValue, VarIncStep, iRet=iRet,EInfo=EInfo)

 

 

Forth, and final, note that we removed a section from UltimateLogsA in this version. In the original version we had:

 

InitValue = VariantToDouble(PropertyGet(UltLogsIO,'InitValue'))

FinalValue = VariantToDouble(PropertyGet(UltLogsIO,'EndValue'))

IncStep = VariantToDouble(PropertyGet(UltLogsIO,'StepIncrement'))

 

 

But these statements are gone now. The reason for this is that the Variants now contain the addresses of InitValue, FinalValue and IncStep, so when the Variants are updated, so are these variables. Note that independently of whether an argument for a method is of input or output type, you can always pass the argument by reference. The ActiveX object is responsible for de-referencing Variants that contain arguments by reference. If you are willing to create your Variants manually (f90VB’s VariantCreate function cannot create Variants with the VT_BYREF flag), you can save considerable processing time and memory by taking advantage of this behavior.

 

Now, before you go mad passing all your arguments by reference, there is one thing you should keep in mind; ActiveX objects are not allowed to release any dynamic variables that have been allocated outside the object. If you are wondering why this is a problem, take a few seconds to study the lines of code presented below:

 

BStrHndl = BStrAlloc('10')

VarInitValue%VarVal%vt=VT_BSTR+VT_BYREF

VarInitValue%VarVal%byRef = loc(BStrHndl)

TmpVar = ExecMethod(UltLogsIO,'GetInputData', VarInitValue, &

                    VarFinalValue, VarIncStep, iRet=iRet,EInfo=EInfo)

 

 

Note that instead of passing a double precision argument in VarInitValue, we are passing a BString by reference. The ActiveX object will take and convert the variant into the appropriate type, and return you another BString variant back. But you have lost the handle to the original BString, and with it the capability of returning to the operating system the memory used by the original BString. In other words, your program just leaked some memory. As a rule of thumb, whenever you call a method that receives Variants containing BStrings or Safe Arrays by reference (i.e. methods that may return a BString or Safe Array), make sure you have a backup handle for the BString or Safe Array that the variant refers to.

 

Working with Events