FORTRAN subroutine not running from VBA call

FORTRAN subroutine not running from VBA call

Hello, dear fellow developers!

Well, I have kind of a weird problem. I have a Module in FORTRAN with several subroutines, some of which are called from a Main and others that are called from other subroutines within the module. That worked really fine (amazing, I would say :) ). But I transformed my main into a subroutine and complied the project into a .dll so that I could call it from an Excel VBA macro... There isn't any problem with this part, until a subroutine calls another subroutine. More or less, the subroutines are:

SUBROUTINE FirstOne(VarX, VarY)
            REAL(DP), INTENT(IN),  DIMENSION(:) :: VarX
            REAL(DP) :: Var1[ALLOCATABLE](:),Var2[ALLOCATABLE](:)
            REAL(DP) ::Var3[ALLOCATABLE](:)
            INTEGER (KIND(1)) :: i
      
            ALLOCATE (VAR1(N), STAT=R)
                IF(R.NE.0) CALL some-other-subroutine
            ALLOCATE (VAR2(N) , STAT=R)  
                IF(R.NE.0) CALL some-other-subroutine
            ALLOCATE (VAR3(N) , STAT=IERROR)  
                IF(IERROR.NE.0) CALL some-other-subroutine

      !N is a global variable in the module, that indicates the length of the arrays troughout the program
       
            DO i=1,NC
                Var1(i)=0.D0
                Var2(i)=0.D0
                Var3(i)=0.D0
            END DO
      
            CALL SecondOne(Var1,Var2,Var3)

            DEALLOCATE(Var1 , STAT=R)
                IF(R.NE.0) CALL some-other-subroutine
            DEALLOCATE(Var2 , STAT=R)
                IF(R.NE.0) CALL some-other-subroutine
            DEALLOCATE(Var3 , STAT=R)     
                IF(R.NE.0) CALL some-other-subroutine
            RETURN
 END SUBROUTINE

SUBROUTINE SecondOne(Var1,Var2,Var3)
            REAL (DP), INTENT(OUT), DIMENSION(:) :: Var1,Var2,Var3

           DO i=1,N
                Var1(i)=Some Arithmetic Operation     <-----------------------Here is the problem... allegedly
                Var2(i)=Some Arithmetic Operation
                Var3(i)=Some Arithmetic Operation
            END DO
 
            RETURN
 END SUBROUTINE

And then I get "fortl: severe(408): fort: (2): Subscript #1 of the array Var1 has value 1 which is greater than the upper bound of 0"

I already tried writing Intent(inout), changing to dimension(N), transferring to other variables (Var12, Var22, Var32) and several combinations. As far as I know, I can't make it allocatable, but I'm not sure wether it's going to solve my problem

Please, my friends... Any suggestions? It worked before I linked it to Excel VBA

5 posts / 0 new
Last post
For more complete information about compiler optimizations, see our Optimization Notice.
Steve Lionel (Intel)'s picture

It's always a risk when we don't see the whole code, or worse, paraphrases and snippets of code. Two things come to mind. First, I don't see that the caller of SecondOne has a visible external interface to SecondOne. This is required because of the DIMENSION(:). If these two subroutines are together in a module, then that is sufficient, otherwise you need to add an interface (ideally by putting the routine in a module and USEing the module, or putting them together in a module.

Second, when calling from VBA, you need to have !DEC$ ATTRIBUTES STDCALL for the routine called from VBA. For example:

!DEC$ ATTRIBUTES STDCALL,REFERENCE,ALIAS:"FirstOne" :: FirstOne

This assumes you declare the routine as "FirstOne" exactly like that in VBA. However you spell it in VBA is exactly what you should put in the ALIAS value. Of course, it's obvious here that FIrstOne is not the one you're calling from VBA, since it also has a DIMENSION(:) argument and that can't be used from VBA, but you should get the idea.
 

Steve

Thanks a lot, Steve.

Yes. What I do, is calling from VBA a subroutine "Commander" from a .f90 file, which USEs the module in which i have the FirstOne and the SecondOne (the module is in another .f90 file).

"Commander" does have the

!DEC$ ATTRIBUTES DLLEXPORT, STDCALL, DECORATE :: Commander

!DEC$ ATTRIBUTES REFERENCE :: Some Set of dummy variables that are not used in the module but are used in some logic operations.

Actually the variable that isn't working is declared within the module and is a local variable that is used only in the module in order to give some other return variables in VBA.

----

Maybe I forgot to say that the references I send to FORTRAN from VBA are actually getting in. I did some simple operation and I get a return value. When I proved the subroutine is working, I called the subroutine from the module... That worked fine too. But everything fails as soon as the first routine calls the second routine within the module. Then FORTRAN doesn't know or doesn't want to know the length of the array I'm working with. And if I give explicitly Dimension(N), my computer freezes and I have to reboot Excel.

Thanks in advance

Steve Lionel (Intel)'s picture

With only paraphrased snippets, I don't think there's more we can tell you. Probably the next step I'd take is to run this in the debugger. For a DLL project the way you do this is set the property Debugging > Command to be the path to Excel.exe. Make sure that Excel is loading your Debug configuration DLL. Set a breakpoint in Commander in your DLL and then start execution. When Excel calls your DLL, the debugger will break in and you can step through it.

Sometimes, odd errors like this are due to stack corruption, which is why I mentioned STDCALL, but you say you have that covered. The next thing I can suggest is to start cutting out pieces of the Fortran code until you get the minimum that shows the problem. If cutting out something else makes the problem go away, look at that more closely.

Steve
David White's picture

you may want to look at these code snippets and compare them with yours (and a couple of other posts I have made, including an example to download):

-------------------------------------

I think in VBA, ByRef is assumed as default, and is not required.  In VB, however, it is (just to make life more interesting).

To allow for the different versions of Excel, I need to now use declarations like this one.  The VBA7 section is for Excel 2010 - it needs the addition of the PtrSafe keyword.

#If VBA7 Then
    Private Declare PtrSafe Sub WaterDensity_F Lib "AWAProps.dll" _
        (TempC As Double, Value As Double, ByVal Units As String)
#Else
    Private Declare Sub WaterDensity_F Lib "AWAProps.dll" _
        (TempC As Double, Value As Double, ByVal Units As String)
#End If

on the Fortran Side, I have:

Subroutine WaterDensity_F(TempC, Value, Units)

!DEC$ ATTRIBUTES DLLEXPORT, STDCALL, ALIAS:'WaterDensity_F' :: WaterDensity_F

!DEC$ ATTRIBUTES REFERENCE :: Units

 Hope this is helpful.  (note the apparent inconsistency in keywords for passing back strings - on the VB side ByVal vs using the Reference attribute on the Fortran side)

David

Login to leave a comment.