Community
cancel
Showing results for 
Search instead for 
Did you mean: 
Highlighted
Beginner
35 Views

Passing Strings from VB.NET to IVF dll

I am attempting to add a GUI to FORTRAN programs using VB.NET. I am unable to pass strings. I have read the information in mixed-language programming in the IVF user guide and this forum, but am just not able to solve my problem.
vb module code:
Code:
Module Module1
    Declare Sub TestSSdll _
        Lib "C:Documents and Settings . . . TestSSdll.dll" _
        (ByVal Phi0 As Double, ByVal RLambda0 As Double, _
        ByVal NWinTimStps As Long, ByVal gridfile As String, _
        ByRef len_gridfile As Integer)
End Module


vb form code:
Code:
Call TestSSdll(Phi0, RLambda0, NWinTimStps, _
            gridfile, gridfile.Length)

fortran dll code:Code:
subroutine TestSSdll (phi0, rlambda0, lgridfile, nWinTimStps, &
    gridfile, gridfile_lng)
implicit none
    
!DEC$ ATTRIBUTES DLLEXPORT, STDCALL :: TestSSdll
!DEC$ ATTRIBUTES ALIAS: "TestSSdll" :: TestSSdll
!DEC$ ATTRIBUTES REFERENCE :: gridfile, gridfile_lng

    real(8), intent(in) :: phi0, rlambda0
    integer(4), intent(in) :: gridfile_lng
    integer(8) :: nWinTimStps
    character(gridfile_lng), intent(in) :: gridfile
    
 	open(unit=14,file=gridfile,status='old')


I have tried passing the strings by value and reference. With the current configuration above, the fortran code does not seem to recognize the string "gridfile" as a string.When added to the Watch window, the value for gridfile shows "Invalid Debug Information". I am able to successfully pass integers and real variables. I currently have the compiler option set to pass string length after all arguments (although I have tried both). I would appreciate any help.
Iam usingIVF V9.0 and Visual Studio.NET 2003.
0 Kudos
11 Replies
Highlighted
35 Views

Okay, first of all a huge disclaimer; I did NOT try any of this, I just observed some things that were wrong.

First obvious thing - your routine signatures didn't match.

This is the VB code:

Call TestSSdll(Phi0, RLambda0, NWinTimStps, _
            gridfile, gridfile.Length)
This is the Fortran code:
subroutine TestSSdll (phi0, rlambda0, lgridfile, nWinTimStps, &
    gridfile, gridfile_lng)
Before we even talk about the character string, please note that you have an extra "lgridfile" 
in the Fortran signature.
That aside, Fortran passes the length of strings as a hidden argument, passed by value.  
You explicitly listed "gridfile_lng" as the length. That is not necessary - the Fortran compiler
"knows" that length is there and generates code to deal with it.
So - remove lgridfile and gridfile_lng from your Fortran program.  Make the declaration of gridfile
to be CHARACTER(*)
Back to the VB one - you need to make the fifth argument "pass by value".
I think this should work for you.
	- Lorri
0 Kudos
Highlighted
Beginner
35 Views

Thanks very much for your help, Lorri. I apologize for the extra argument -- I was piecing together an example with less arguments than my actual code and left too many in one statement. I believe I tried what you suggested:

Code:

VB module:

Declare Sub TestSSdll _
        Lib "C:Documents and Settings . . . TestSSdll.dll" _
        (ByVal Phi0 As Double, ByVal RLambda0 As Double, _
        ByVal NWinTimStps As Long, ByVal gridfile As String, _
        ByVal len_gridfile As Integer)

VB Call:

Call TestSSdll(Phi0, RLambda0, NWinTimStps, gridfile, _
        gridfile.Length)  

FORTRAN dll:

subroutine TestSSdll (phi0, rlambda0, nWinTimStps, gridfile)
implicit none
    
!DEC$ ATTRIBUTES DLLEXPORT, STDCALL :: TestSSdll
!DEC$ ATTRIBUTES ALIAS: "TestSSdll" :: TestSSdll
!DEC$ ATTRIBUTES REFERENCE :: gridfile

    real(8) :: phi0, rlambda0
    integer(8) :: nWinTimStps
    character(*) :: gridfile
    
 	open(unit=14,file=gridfile,status='old')
   


When building I get the following error referring to line 1 of the FORTRAN code:

Error: This passed length character name has been used in an invalid context. [GRIDFILE]

Also, I actually need to pass several (3 in this case) strings. Do you list the string lengths for every included string at the end of the argument list in the calling statement?Again, I'm using the compiler option to pass string length after all arguments.


0 Kudos
Highlighted
Valued Contributor II
35 Views

Let me sum up the mechanisms:

- If you declare !DEC$ATTRIBUTES REFERENCE for a string argument, the length is not expected, nor should be passed. However, in that case, you have to hard-code the string length on Fortran side or determine it in other way (but it mustn't be a character(*)).
- If you don't declare REFERENCE, you can use character(*) but you have to pass the length (by value) in the caller for every string argument you have.

HTH,
Jugoslav
0 Kudos
Highlighted
Beginner
35 Views

Jugoslav, thanks for the help. It's quite possible I missed something or don't understand, but here's what I get:

- If you declare !DEC$ATTRIBUTES REFERENCE for a string argument, the length is not expected, nor should be passed. However, in that case, you have to hard-code the string length on Fortran side or determine it in other way (but it mustn't be a character(*)).
Code:

VB Module:

Declare Sub TestSSdll _
        Lib "C:Documents and Settings. . .TestSSdll.dll" _
        (ByVal Phi0 As Double, ByVal RLambda0 As Double, _
        ByVal NWinTimStps As Long, ByVal gridfile As String)

(I tried both ByVal and ByRef for the string)

VB Call:

Call TestSSdll(Phi0, RLambda0, NWinTimStps, _
            gridfile)

FORTRAN:

subroutine TestSSdll (phi0, rlambda0, nWinTimStps, gridfile)
implicit none
    
!DEC$ ATTRIBUTES DLLEXPORT, STDCALL :: TestSSdll
!DEC$ ATTRIBUTES ALIAS: "TestSSdll" :: TestSSdll
!DEC$ ATTRIBUTES REFERENCE :: gridfile

real(8) :: phi0, rlambda0

integer(8) :: nWinTimStps

character(99) :: gridfile

open(unit=14,file=gridfile,status='old')


Result: gridfile value is incorrect (two symbols and two letters)

I'm not sure how this first method would work practically with VB.NET since, as I understand, fixed length strings are not allowed. I am passing file paths which would be variable lengths. Length of gridfile for this example is 99.

2nd Option:

- If you don't declare REFERENCE, you can use character(*) but you have to pass the length (by value) in the caller for every string argument you have.
Code:

VB Module:

Declare Sub TestSSdll _
        Lib "C:Documents and Settings. . . TestSSdll.dll" _
        (ByVal Phi0 As Double, ByVal RLambda0 As Double, _
        ByVal NWinTimStps As Long, ByVal gridfile As String, _
        ByVal len_gridfile As Integer)

VB Call:

Call TestSSdll(Phi0, RLambda0, NWinTimStps, _
            gridfile, gridfile.Length)

FORTRAN:

subroutine TestSSdll (phi0, rlambda0, nWinTimStps, gridfile)
implicit none
    
!DEC$ ATTRIBUTES DLLEXPORT, STDCALL :: TestSSdll
!DEC$ ATTRIBUTES ALIAS: "TestSSdll" :: TestSSdll

    real(8) :: phi0, rlambda0
    integer(8) :: nWinTimStps
    character(*) :: gridfile
    
 	open(unit=14,file=gridfile,status='old')


Result: gridfile value in the watch window is listed as Invalid Debug Information

Did I miss something or misunderstand?

David Coggin



0 Kudos
Highlighted
Valued Contributor II
35 Views

Both codes now look fine in isolation; the fishy thing is that VB and IVF can't appear to "communicate" the correct address of gridfile.

I'm not familiar with VB.NET enough; offhand, it seems that
 ByVal gridfile As String
is not the correct declaration for a plain vanilla array of character(1), but I don't know which one is right. I'm sure someone else could help.

In the meantime, I'd check LOC(gridfile) in the debugger and take a look at what's in Memory window around it (on VB side too).

Jugoslav
0 Kudos
Highlighted
Valued Contributor II
35 Views

No, ByVal as String is OK. And everything else looks OK.

What happens if you delete other arguments, leaving only gridfile in the argument-list?

Jugoslav
0 Kudos
Highlighted
35 Views

Ok, I did some experiments. Here's what you need to do.

On the VB side, the string gets declared as "ByVal stringname as string". You also need to add the string length to the end of the argument list as "ByVal stringlen as Integer". I also changed the numeric args to ByRef, especially as one of them is written. For example:

Module Module1
Declare Sub TestSSdll _
Lib "C:Documents and Settings . . . TestSSdll.dll" _
(ByRef Phi0 As Double, ByRef RLambda0 As Double, _
ByRef NWinTimStps As Long, ByVal gridfile As String, _
ByVal len_gridfile As Integer)
End Module


You will need to pass the length of the string explicitly using this arrangement.

On the Fortran side, the routine needs to have the STDCALL and REFERENCE attributes, the string argument should not have the REFERENCE attribute, assuming you want the length passed. For example:

subroutine TestSSdll (phi0, rlambda0, lgridfile, nWinTimStps, &
gridfile)
implicit none

!DEC$ ATTRIBUTES DLLEXPORT, STDCALL, REFERENCE :: TestSSdll
!DEC$ ATTRIBUTES ALIAS: "TestSSdll" :: TestSSdll

real(8), intent(in) :: phi0, rlambda0
integer(8) :: nWinTimStps
character(*), intent(in) :: gridfile


Note that the string length is not explicitly specified in the Fortran, as the compiler will look for it, passed by value.

An alternative which I tested that works is to have the length passed from VB ByRef, and having the Fortran look like this:

subroutine TestSSdll (phi0, rlambda0, lgridfile, nWinTimStps, &
gridfile,gridfile_lng)
implicit none

!DEC$ ATTRIBUTES DLLEXPORT, STDCALL, REFERENCE :: TestSSdll
!DEC$ ATTRIBUTES ALIAS: "TestSSdll" :: TestSSdll

real(8), intent(in) :: phi0, rlambda0
integer(8) :: nWinTimStps
integer(4), intent(in) :: gridfile_lng
character(gridfile_lng), intent(in) :: gridfile


See how this works for you. The key seems to be the REFERENCE on the routine, as otherwise you get VALUE by default and for character arguments that is a bit strange. I tried just REFERENCE on the character arg and that didn't work - not sure why.
0 Kudos
Highlighted
Beginner
35 Views

If I delete all other arguments, I get the exact same results. I'm not sure how to use the LOC but the call stack window shows (gridfile=).When I have the longer list of arguments, theother parameters indicate correct values in the call stack and watch windows. Thanks again.

David

0 Kudos
Highlighted
Beginner
35 Views

Works like a charm, now! Thanks, Steve.
0 Kudos
Highlighted
Valued Contributor II
35 Views

The key seems to be the REFERENCE on the routine, as otherwise you get VALUE by default and for character arguments that is a bit strange

My Lord! I could have sworn that VF never does that, i.e. that characters are always passed by reference (with or without length), and I was about to argue your statement above. However, when I did some checking on both CVF and IVF, I verified that you're right...

I'm not sure why that semantics was adopted, i.e. I don't see why would anyone ever wanted to pass a string by value; besides, the name of
subroutine StrTest(s)
!DEC$ATTRIBUTES STDCALL:: StrTest
character(10) s
still gets mangled as _strtest@8, meaning that you can pass only characters shorter than 4 bytes (if at all).

I beg for a change; I don't see how can this ever be useful (and if needed user can still spell out VALUE attrigute), and it obviously leads to problems such as David's.

Jugoslav
0 Kudos
Highlighted
35 Views

I agree that it makes no sense. We did it because MSFPS did it. It's sort of the C behavior. It's even documented. I doubt we'd change it now.

I'll take a look at the decoration issue.
0 Kudos