Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
Announcements
FPGA community forums and blogs on community.intel.com are migrating to the new Altera Community and are read-only. For urgent support needs during this transition, please visit the FPGA Design Resources page or contact an Altera Authorized Distributor.
29298 Discussions

Passing strings between VB and fortran (Com server)

jonathan
Beginner
1,994 Views

Hi,

I am trying to iron out teething problems with a com server that used to work in compaq but still won't work in the latest incarnation of the intel software.

At present some of my calls between the vb and the fortran involve passing arguments as strings. The com server wizard (the .hie file) allows you to select between two types of string in fortran. Either Character(*) or Character(1). If I choose character(*) it doesn't generate an error immediately but the string is blank when it arrives in fortran(although the vb variable was definitely not blank at runtime!). If I choose Character(1) the program errors with a message saying that you can't convert between a string and a byte.

The Variables have an intent set to 'in' in the fortran.

Any ideas please?

Jonathan.

0 Kudos
14 Replies
jonathan
Beginner
1,994 Views

Still struggling with this but have noticed a few things:

In the server.idl file the type definition for an argument set as Character(*) is 'BSTR'. However, for a Character(1) it is set to 'int'.

If I set the interface to use Character(550) for a particular argument say, then during runtime the watch window will show this variable as being filled with wierd characters e.g.'- '. The VB passed the variable in as a string.

any ideas would be welcome....

0 Kudos
anthonyrichards
New Contributor III
1,994 Views
Are you calling Fortran FROM VB or vice versa? As far as I understand it, VB passes a pointer to the first character of a string, the string being stored as aBITSTRING (double-byte character pairs terminated by a null character, thefirst characterpreceded by a 4-byte integer character length)UNLESS the string has been DECLARED in VB as an argument to an external procedure (e.g. a routine in a DLL), in which case it sends a modified BITSTRING that can be handled somewhat like a C-string and, when returned to VB by the external procedure is reverse-modified to get back to its proper Bitstring representation. If the external procedure changes the received bitstring, it must make sure to return it in this special modified form so that when VB receives control again, it will do the correct reverse-modofication back to its internally-stored BITSTRING form.
0 Kudos
jonathan
Beginner
1,994 Views

Thanks for the help. I think I understand - However, the answer to your question regarding the calling arrangement is that I am calling Fortran from VB. The Fortran requires a string argument (with intent set to in). It does not need to pass this back to VB, and the error occurs during the running of the Fortran code when it tries to use this string to define a file name. (Before control returns to VB)

The VB calling routine is:

Dim DataPathTest As String = "C:DataTest"

SiliconCow.ModelInputs(MDIP.Simulation.SelectedItem, MDIP.Simulation.SelectedItem.ToString.Length, DataPathTest, DataPathTest.Length, VBParameters, VBVFAcoeff, VBNutrients, Defaunate, VBLIParameters, VBLIVFAcoeff, VBLINutrients, LIDefaunate)

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

When DataPathTest arrives in Fortran it is blank!

The code for the Fortran is:

function

ISiliconCow2_ModelInputs( ObjectData ,&

SimName,&

LenSimName,&

DataPath,&

LenDataPath,&

Parameters,&

VFACoeff,&

Nutrients,&

Defaunate,&

LIParameters,&

LIVFACoeff,&

LINutrients,&

LIDefaunate)

result (hresult)

use SiliconCow2_Types

implicit none

type(SiliconCow2_InstanceData) ObjectData

!dec$ attributes reference :: ObjectData

CHARACTER(*), intent(in) :: SimName

INTEGER(4) :: LenSimName

CHARACTER(*), intent(in) :: DataPath

INTEGER(4) :: LenDataPath

REAL(4), intent(inout) :: Parameters

DIMENSION Parameters(1:178)

REAL(4), intent(inout) :: VFACoeff

DIMENSION VFACoeff(1:60)

REAL(4), intent(inout) :: Nutrients

DIMENSION Nutrients(1:24)

LOGICAL(2) :: Defaunate

REAL(4), intent(inout) :: LIParameters

DIMENSION LIParameters(1:169)

REAL(4), intent(inout) :: LIVFACoeff

DIMENSION LIVFACoeff(1:60)

REAL(4), intent(inout) :: LINutrients

DIMENSION LINutrients(1:8)

LOGICAL(2) :: LIDefaunate

integer(long) hresult

! DO NOT REMOVE THIS LINE

! TODO: Add implementation

Call InitialiseModel(SimName,LenSimName,DataPath,LenDataPath,Parameters,VFAcoeff,Nutrients,Defaunate,LIParameters,LIVFAcoeff,LINutrients,LIDefaunate)

hresult = S_OK

! DO NOT REMOVE THIS LINE

end function

0 Kudos
Lorri_M_Intel
Employee
1,994 Views

I see a couple of things.

First, if I read it correctly, you are passing MDIP.Simulation.SelectedItem and its length, MDIP.Simulation.SelectedItem.ToString.Length.

The Fortran code is only expecting the address of a structure, not the address plus its length. Remove the length from the caller (the VB code).

Secondly, you haven't passed the character argument correctly; let me explain.

By default, Fortran passes the lengths of CHARACTER arguments as a hidden argument in the routine call. That is, the user does not need to explicitly declare the length argument. Also by default, ifort puts those lengths at the END of the argument list. CVF put the length immediately following the character argument. (( note: It is possible to override the default placement of lengths, and one option is to use /iface:mixed-str-len-arg when building the Fortran library.))

The non-Fortran caller needs to be aware of where the length is expected to be, and to explicitly declare it there and pass it there.

In your example above, you have explicitly listed the length argument in the Fortran code. Take that out.

Finally, you might want to look at the chapter on mixed language programming in the Fortran docs. There are lots of other details that I didn't list here.

Good luck, and I hope this helps!

- Lorri

0 Kudos
anthonyrichards
New Contributor III
1,994 Views

This works. As a test, create a VB project with a form containing a text box (Text1)and a button(Command1). To test, you will enter a string into the text box and the button triggers the code to take the text and call a routine in a Fortran DLLwith the text and its length as argument. Declare your Fortran routine in VB as follows (or similar):

Declare Sub MyFortranRoutine Lib "c:yourfolderheremyfortrandebugMyFortran.DLL" (Mystring As String, Mylength As Long)

Enter the following as the button (command1)-click code:

Private Sub Command1_Click()
Dim Mystring As String
Dim Mylength As Long

Mystring = Text1.Text
If Mystring = "" Then
Mylength = 0
Else
Mylength = Len(Mystring)
End If
MsgBox ("String =" & Mystring & " - Calling My Fortran Routine")
MsgBox ("String Length =" & CInt(Mylength))
Call MyFortranRoutine(Mystring, Mylength)
End Sub

Then have something like the following in the Fortran to create your DLL:

! Myfortran.f90
!
! FUNCTIONS/SUBROUTINES exported from Myfortran.dll:
!MyfortranRoutine - subroutine
!
subroutine MyFortranRoutine (Mystringref, iLength)

! Expose subroutine MyfortranRoutine to users of this DLL
!
!DEC$ ATTRIBUTES DLLEXPORT::MyFortranRoutine
!DEC$ ATTRIBUTES STDCALL::MyFortranRoutine
!DEC$ ATTRIBUTES ALIAS : 'MyFortranRoutine'::MyFortranRoutine
!DEC$ ATTRIBUTES REFERENCE::iLength
!DEC$ ATTRIBUTES REFERENCE::Mystringref
use dfwin ! CVF module used to be able to call the Messagebox function
implicit none
!Subroutine arguments
integer(4),intent(in)::Mystringref
!length ofstring
integer(4),intent(in)::iLength

character(iLength) inputstring
pointer(pinput,inputstring)
pinput=Mystringref
iret=messagebox(null,inputstring//char(0),'Input String'c,mb_ok)
end subroutine MyFortranRoutine

Compile the above into a DLL and try the VB project with it. You should get a Fortran string out of it.
Note that I use Compaq Visual Fortran (CVF), so you may need to use a different modulenameto get the Fortraninterface to MessageBox.

0 Kudos
Steven_L_Intel1
Employee
1,994 Views
Folks here should note that this is a COM Server application and is not just calling a DLL routine directly. The actual sequence of things is more complicated than it looks here - when the VB code makes the call to the "method", it first goes through a generated Fortran routine which picks apart the VB-passed arguments and then calls the actual Fortran code. As such, the explicit passing of the length looks ok to me, since the generated code does pass it separately in addition to the hidden argument.
0 Kudos
jonathan
Beginner
1,994 Views

Thanks for everyones help so far. Steve is correct to point out that this is a com server so there is not a direct call. However, I am finding that the program is behaving very strangly because even if I try to force the string to take a value using the fortran code - i.e. bypassing the VB directly, I can't seem to load any value into the Character variable. I have tried defining a new Character variable and using that - directly in the module where it is required, or just assigning a value to the existing variable. neither actually show a value during runtime. So for a minute lets forget VB !

Definitions in included module:

CHARACTER(LEN=50) :: SimName

CHARACTER(LEN=550) :: DataPath

Fortran code that uses the above module:

SimName = "Simulation"

File1 =

trim(SimName)//'Var1.dat'

In this code - The variable SimName is normally passed in from VB via the COM server. However, as its not working I have as you can see manually assigned a value. However if you set a break point after this code - SimName is just a load of blank spaces. If I set up a new Character variable lets say Character(Len=*) :: TestString and then assign a value before I need it - the variable never seems to get filled with anything.

What's going on?!

0 Kudos
Steven_L_Intel1
Employee
1,994 Views
Jonathan, I think more background (that is, code) is needed than what can be presented here in the forum. I'm working on your issue through Intel Premier Support so let's take it there.
0 Kudos
jonathan
Beginner
1,994 Views

Thanks Steve - let me know if you want more info on setting up the project etc...

0 Kudos
g_f_thomas
Beginner
1,994 Views

With variable length strings, VB uses BSTRs which implicitly includes info on the string's length. A BSTR passed to IVF has to be converted to a fortran string via ConvertBSTRtoString in ifcom wherein the length is determined and the BSTR is then converted to a string via the Win API.

Gerry

0 Kudos
Steven_L_Intel1
Employee
1,994 Views
Yes, but this is (supposed to be) handled by the COM Server generated jacket code.
0 Kudos
g_f_thomas
Beginner
1,994 Views

All Windows programming reduces to the use of the Win API and COM is no exception. In many respects COM is an obscure mother of a wrapper around the WinAPI which perhaps is why it is approaching near obsolescence.

Why do you think ifcom has ConvertBSTRtoString/ConvertStringtoBSTR? Yes, so you can use them from COM, or anywhere else for that matter.

BTW,fixed length strings in VB can be converted to byte arraysvia the CopyMemory function and passed as such to Fortran, no COM required as with variable length strings.

Gerry

0 Kudos
Steven_L_Intel1
Employee
1,994 Views
Steve Lionel:
Yes, but this is (supposed to be) handled by the COM Server generated jacket code.


That "supposed to be" was in fact key - there is a bug in the COM Server in that intent(in) string arguments are missing the convert resulting in an uninitialized string being passed to the Fortran code. The simple workaround is to change the intent of the argument in the hierarchy to intent(in,out), or you can manually add the convert call. This will get fixed in a future update.
0 Kudos
g_f_thomas
Beginner
1,994 Views

Quite, and the convert is successful if you use ifcom to call, respectively,ConvertBSTRtoString/ConvertStringtoBSTR as IVF gets/putsfrom/to VB.

Gerry

0 Kudos
Reply